Migrated logic to create a DataTable from a Node or pretty much any other object in to a central place so we only have to maintain code in one place.

Now need to just test it with Node and create the DynamicNode extension methods for returning as a DataTable.
This commit is contained in:
Shannon Deminick
2012-09-15 12:17:42 +07:00
parent 1566b16f0f
commit 91834e8f53
4 changed files with 194 additions and 104 deletions

View File

@@ -0,0 +1,143 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading;
using umbraco.interfaces;
namespace Umbraco.Core
{
/// <summary>
/// Static and extension methods for the DataTable object
/// </summary>
internal static class DataTableExtensions
{
private static readonly Hashtable AliasToNames = new Hashtable();
private static readonly ReaderWriterLockSlim Lock = new ReaderWriterLockSlim();
/// <summary>
/// Creates a DataTable with the specified alias and columns and uses a callback to populate the headers.
/// </summary>
/// <param name="alias"></param>
/// <param name="getHeaders"></param>
/// <param name="rowData"> </param>
/// <returns></returns>
/// <remarks>
/// This has been migrated from the Node class and uses proper locking now. It is now used by the Node class and the
/// DynamicDocument extensions for legacy reasons.
/// </remarks>
public static DataTable GenerateDataTable(
string alias,
Func<string, IEnumerable<KeyValuePair<string, string>>> getHeaders,
Func<IEnumerable<Tuple<IEnumerable<KeyValuePair<string, object>>, IEnumerable<KeyValuePair<string, object>>>>> rowData)
{
var dt = new DataTable(alias);
//get the standard column headers from the standard data (not user data)
var tableData = rowData();
var standardColHeaders = tableData.SelectMany(x => x.Item1).Select(x => x.Key).Distinct();
var userPropColHeaders = new List<string>();
// get user property column headers
var propertyHeaders = GetPropertyHeaders(alias, getHeaders);
var ide = propertyHeaders.GetEnumerator();
while (ide.MoveNext())
{
userPropColHeaders.Add(ide.Value.ToString());
}
//now add all the columns, standard val headers first, then user val headers
foreach (var dc in standardColHeaders.Union(userPropColHeaders).Select(c => new DataColumn(c)))
{
dt.Columns.Add(dc);
}
//add row data
foreach(var r in rowData())
{
dt.PopulateRow(
(Hashtable)AliasToNames[alias],
r.Item1,
r.Item2);
}
return dt;
}
/// <summary>
/// Helper method to return this ugly object
/// </summary>
/// <returns></returns>
/// <remarks>
/// This is for legacy code, I didn't want to go creating custom classes for these
/// </remarks>
internal static List<System.Tuple<IEnumerable<KeyValuePair<string, object>>, IEnumerable<KeyValuePair<string, object>>>> CreateTableData()
{
return new List<System.Tuple<IEnumerable<KeyValuePair<string, object>>, IEnumerable<KeyValuePair<string, object>>>>();
}
/// <summary>
/// Helper method to deal with these ugly objects
/// </summary>
/// <param name="rowData"></param>
/// <param name="standardVals"></param>
/// <param name="userVals"></param>
/// <remarks>
/// This is for legacy code, I didn't want to go creating custom classes for these
/// </remarks>
internal static void AddRowData(
List<System.Tuple<IEnumerable<KeyValuePair<string, object>>, IEnumerable<KeyValuePair<string, object>>>> rowData,
IEnumerable<KeyValuePair<string, object>> standardVals,
IEnumerable<KeyValuePair<string, object>> userVals)
{
rowData.Add(new System.Tuple<IEnumerable<KeyValuePair<string, object>>, IEnumerable<KeyValuePair<string, object>>>(
standardVals,
userVals
));
}
private static Hashtable GetPropertyHeaders(string alias, Func<string, IEnumerable<KeyValuePair<string, string>>> getHeaders)
{
using (var l = new UpgradeableReadLock(Lock))
{
if (AliasToNames.ContainsKey(alias))
return (Hashtable)AliasToNames[alias];
l.UpgradeToWriteLock();
var headers = getHeaders(alias);
var def = new Hashtable();
foreach (var pt in headers)
def.Add(pt.Key, pt.Value);
AliasToNames.Add(alias, def);
return def;
}
}
private static void PopulateRow(
this DataTable dt,
IDictionary aliasesToNames,
IEnumerable<KeyValuePair<string, object>> standardVals,
IEnumerable<KeyValuePair<string, object>> userPropertyVals)
{
var dr = dt.NewRow();
foreach (var r in standardVals)
{
dr[r.Key] = r.Value;
}
foreach (var p in userPropertyVals.Where(p => p.Value != null))
{
dr[aliasesToNames[p.Key].ToString()] = p.Value;
}
dt.Rows.Add(dr);
}
}
}

View File

@@ -56,6 +56,7 @@
<Compile Include="Configuration\FileSystemProviderElementCollection.cs" />
<Compile Include="Configuration\FileSystemProvidersSection.cs" />
<Compile Include="CoreBootManager.cs" />
<Compile Include="DataTableExtensions.cs" />
<Compile Include="DictionaryExtensions.cs" />
<Compile Include="Dictionary\CultureDictionaryFactoryResolver.cs" />
<Compile Include="Dictionary\ICultureDictionaryFactory.cs" />

View File

@@ -9,6 +9,7 @@ using System.Xml.XPath;
using umbraco.cms.businesslogic;
using umbraco.cms.businesslogic.propertytype;
using umbraco.interfaces;
using Umbraco.Core;
namespace umbraco.NodeFactory
{
@@ -358,30 +359,11 @@ namespace umbraco.NodeFactory
public DataTable ChildrenAsTable()
{
if (Children.Count > 0)
{
DataTable dt = generateDataTable(Children[0]);
string firstNodeTypeAlias = Children[0].NodeTypeAlias;
foreach (Node n in Children)
{
if (n.NodeTypeAlias == firstNodeTypeAlias)
{
DataRow dr = dt.NewRow();
populateRow(ref dr, n, getPropertyHeaders(n));
dt.Rows.Add(dr);
}
}
return dt;
}
else
return new DataTable();
return Children.Count > 0 ? GenerateDataTable(Children[0]) : new DataTable();
}
public DataTable ChildrenAsTable(string nodeTypeAliasFilter)
{
if (Children.Count > 0)
{
@@ -399,94 +381,58 @@ namespace umbraco.NodeFactory
if (nodeFound)
{
DataTable dt = generateDataTable(Firstnode);
foreach (Node n in Children)
{
if (n.NodeTypeAlias == nodeTypeAliasFilter)
{
DataRow dr = dt.NewRow();
populateRow(ref dr, n, getPropertyHeaders(n));
dt.Rows.Add(dr);
}
}
return dt;
return GenerateDataTable(Firstnode);
}
else
{
return new DataTable();
}
}
else
return new DataTable();
}
return new DataTable();
}
private DataTable generateDataTable(Node SchemaNode)
private DataTable GenerateDataTable(INode node)
{
DataTable NodeAsDataTable = new DataTable(SchemaNode.NodeTypeAlias);
string[] defaultColumns = {
"Id", "NodeName", "NodeTypeAlias", "CreateDate", "UpdateDate", "CreatorName",
"WriterName", "Url"
};
foreach (string s in defaultColumns)
{
DataColumn dc = new DataColumn(s);
NodeAsDataTable.Columns.Add(dc);
}
// add properties
Hashtable propertyHeaders = getPropertyHeaders(SchemaNode);
IDictionaryEnumerator ide = propertyHeaders.GetEnumerator();
while (ide.MoveNext())
{
DataColumn dc = new DataColumn(ide.Value.ToString());
NodeAsDataTable.Columns.Add(dc);
}
return NodeAsDataTable;
//use new utility class to create table so that we don't have to maintain code in many places, just one
var dt = Umbraco.Core.DataTableExtensions.GenerateDataTable(
//pass in the alias
node.NodeTypeAlias,
//pass in the callback to extract the Dictionary<string, string> of user defined aliases to their names
alias =>
{
var ct = ContentType.GetByAlias(alias);
return ct.PropertyTypes.ToDictionary(x => x.Alias, x => x.Name);
},
//pass in a callback to populate the datatable, yup its a bit ugly but it's already legacy and we just want to maintain code in one place.
() =>
{
//create all row data
var tableData = Umbraco.Core.DataTableExtensions.CreateTableData();
//loop through each child and create row data for it
foreach (Node n in Children)
{
var standardVals = new Dictionary<string, object>()
{
{"Id", n.Id},
{"NodeName", n.Name},
{"NodeTypeAlias", n.NodeTypeAlias},
{"CreateDate", n.CreateDate},
{"UpdateDate", n.UpdateDate},
{"CreatorName", n.CreatorName},
{"WriterName", n.WriterName},
{"Url", library.NiceUrl(n.Id)}
};
var userVals = new Dictionary<string, object>();
foreach (var p in from Property p in n.Properties where p.Value != null select p)
{
userVals[p.Alias] = p.Value;
}
//add the row data
Umbraco.Core.DataTableExtensions.AddRowData(tableData, standardVals, userVals);
}
return tableData;
}
);
return dt;
}
private Hashtable getPropertyHeaders(Node SchemaNode)
{
if (_aliasToNames.ContainsKey(SchemaNode.NodeTypeAlias))
return (Hashtable)_aliasToNames[SchemaNode.NodeTypeAlias];
else
{
ContentType ct = ContentType.GetByAlias(SchemaNode.NodeTypeAlias);
Hashtable def = new Hashtable();
foreach (PropertyType pt in ct.PropertyTypes)
def.Add(pt.Alias, pt.Name);
System.Web.HttpContext.Current.Application.Lock();
_aliasToNames.Add(SchemaNode.NodeTypeAlias, def);
System.Web.HttpContext.Current.Application.UnLock();
return def;
}
}
private void populateRow(ref DataRow dr, Node n, Hashtable AliasesToNames)
{
dr["Id"] = n.Id;
dr["NodeName"] = n.Name;
dr["NodeTypeAlias"] = n.NodeTypeAlias;
dr["CreateDate"] = n.CreateDate;
dr["UpdateDate"] = n.UpdateDate;
dr["CreatorName"] = n.CreatorName;
dr["WriterName"] = n.WriterName;
dr["Url"] = library.NiceUrl(n.Id);
int counter = 8;
foreach (Property p in n.Properties)
{
if (p.Value != null)
{
dr[AliasesToNames[p.Alias].ToString()] = p.Value;
counter++;
}
}
}
private void initializeStructure()
{
// Load parent if it exists and is a node

View File

@@ -22,15 +22,15 @@ namespace umbraco.MacroEngines.Library
internal static INode ConvertToNode(this DynamicDocument doc)
{
var node = new SimpleNode(doc);
var node = new ConvertedNode(doc);
return node;
}
private class SimpleNode : INode
private class ConvertedNode : INode
{
private readonly DynamicDocument _doc;
public SimpleNode(DynamicDocument doc)
public ConvertedNode(DynamicDocument doc)
{
_doc = doc;
template = doc.TemplateId;