Silverlight与WCF(WebService)交互多以实体类的形式进行数据传递。但对于报表平台这样的形式不可取,为你不能预知客户在设计报表时选中了哪个数据库的哪些表的哪些字段。因此传统.Net中的DataTable是一种很好解决方案。下面讨论笔者的一种解决方案。欢迎各位拍砖。
整个解决方案的思想就是将DataTable分为元数据(MetaData)、数据(Data)两部分进行传递。MetaData不用说就
是当前DataTable的列信息(例如列名,数据类型,最大长度等等)。至于数据,我们知道DataTable是一种二维结构。所以
我选择List<List<object>>的形式进行数据存储。这样的数据结构虽然效率不高但可扩展性比较强送到客户端后便于进一步
数据处理(例如:小记合计,动态列等)。MetaData,Data传递到客户端后客户端代码根据MetaData的信息用TypeBuilder等API
动态创建实体类,并转换为List<object>绑定到DataGrid。
首先定义数据结构,这里我用DataSetData作为自定义的数据容器类。
Field.cs 字段信息(对应DataTable中的列)

Field

/**//// <summary>
/// 字段信息
/// </summary>
[XCenter.Framework.Public.Serialization.Serializable,DataContract]
public class Field:XCenter.Framework.Public.Core.ICloneable,IMobileObject

{

public Field()

{
}


props#region props

private string dataType;
private string caption;
private string fieldName;
//private string note;
private string expression;
//private int precision;
//private int scale;
private int maxLength;
private bool isRequire;
private bool isKey;
private bool isReadOnly;


/**//// <summary>
/// 数据类型
/// </summary>
[DataMember]
public string DataType

{

get
{ return dataType; }

set
{ dataType = value; }
}


/**//// <summary>
/// 显示名称
/// </summary>
[DataMember]
public string Caption

{

get
{ return caption; }

set
{ caption = value; }
}


/**//// <summary>
/// 字段名称
/// </summary>
[DataMember]
public string FiledName

{

get
{ return fieldName; }

set
{ fieldName = value; }
}


/**//// <summary>
/// 表达式
/// </summary>
[DataMember]
public string Expression

{

get
{ return expression; }

set
{ expression = value; }
}


/**//// <summary>
/// 数据长度
/// </summary>
//[DataMember]
//public int Precision
//{
// get { return precision; }
// set { precision = value; }
//}


/**//// <summary>
/// 小数位数
/// </summary>
//[DataMember]
//public int Scale
//{
// get { return scale; }
// set { scale = value; }
//}


/**//// <summary>
/// 备注
/// </summary>
//[DataMember]
//public string Note
//{
// get { return note; }
// set { note = value; }
//}


/**//// <summary>
/// 最大长度
/// </summary>
[DataMember]
public int MaxLength

{

get
{ return maxLength; }

set
{ maxLength = value; }
}


/**//// <summary>
/// 是否可空
/// </summary>
[DataMember]
public bool IsRequire

{

get
{ return isRequire; }

set
{ isRequire = value; }
}


/**//// <summary>
/// 是否为主键
/// </summary>
[DataMember]
public bool IsKey

{

get
{ return isKey; }

set
{ isKey = value; }
}


/**//// <summary>
/// 是否只读
/// </summary>
[DataMember]
public bool IsReadOnly

{

get
{ return isReadOnly; }

set
{ isReadOnly = value; }
}

#endregion

public override string ToString()

{
return String.IsNullOrEmpty(caption) ? fieldName : caption;
}

public override bool Equals(object obj)

{
if (fieldName == null || obj == null || !(obj is Field))
return false;
Field f = (Field)obj;
return f.FiledName.Equals(fieldName);
}

public override int GetHashCode()

{
return fieldName.GetHashCode();
}


ICloneable 成员#region ICloneable 成员

public object Clone()

{
Field f = new Field();
f.Caption = this.caption;
f.DataType = this.DataType;
f.Expression = this.expression;
f.FiledName = this.fieldName;
f.IsKey = this.isKey;
f.IsReadOnly = this.isReadOnly;
f.IsRequire = this.isRequire;
f.MaxLength = this.maxLength;
//f.Note = this.note;
//f.Precision = this.precision;
//f.Scale = this.scale;
return f;
}

#endregion


IMobileObject 成员#region IMobileObject 成员

public void GetChildren(XCenter.Framework.Public.Serialization.Mobile.SerializationInfo info, MobileFormatter formatter)

{
}

public void GetState(XCenter.Framework.Public.Serialization.Mobile.SerializationInfo info)

{
info.AddValue("dataType", dataType);
info.AddValue("caption", caption);
info.AddValue("fieldName", fieldName);
//info.AddValue("note", note);
info.AddValue("expression", expression);
//info.AddValue("precision", precision);
//info.AddValue("scale", scale);
info.AddValue("maxLength", maxLength);
info.AddValue("isRequire", isRequire);
info.AddValue("isKey", isKey);
info.AddValue("isReadOnly", isReadOnly);
}

public void SetChildren(XCenter.Framework.Public.Serialization.Mobile.SerializationInfo info, MobileFormatter formatter)

{
}

public void SetState(XCenter.Framework.Public.Serialization.Mobile.SerializationInfo info)

{
dataType = info.GetValue<string>("dataType");
caption = info.GetValue<string>("caption");
fieldName = info.GetValue<string>("fieldName");
//note = info.GetValue<string>("note");
expression = info.GetValue<string>("expression");
//precision = info.GetValue<int>("precision");
//scale = info.GetValue<int>("scale");
maxLength = info.GetValue<int>("maxLength");
isRequire = info.GetValue<bool>("isRequire");
isKey = info.GetValue<bool>("isKey");
isReadOnly = info.GetValue<bool>("isReadOnly");
}

#endregion
}
MetaData.cs 元数据信息(Field的容器)

MetaData

/**//// <summary>
/// 元数据信息
/// </summary>
[XCenter.Framework.Public.Serialization.Serializable,DataContract]
public class MetaData : XCenter.Framework.Public.Core.ICloneable,IMobileObject

{

prop#region prop

//字段列表
private List<Field> fieldsList = new List<Field>();
//名称-字段缓存
[XCenter.Framework.Public.Serialization.NonSerialized]
private Dictionary<string, Field> fieldCache = null;
private object lockObject = new object();


/**//// <summary>
/// 字段列表
/// </summary>
[DataMember]
public List<Field> Fields

{

get
{ return fieldsList; }

set
{ fieldsList = value; }
}

#endregion


public#region public


/**//// <summary>
/// 根据名称获得字段
/// </summary>
/// <param name="fieldName"></param>
/// <returns></returns>
public Field GetField(string fieldName)

{
if (String.IsNullOrEmpty(fieldName))
return null;
return GetNameCache()[fieldName];
}


/**//// <summary>
/// 添加字段
/// </summary>
/// <param name="fields"></param>
public void AddField(Field[] fields)

{
if (fields != null && fields.Length > 0)

{
foreach (Field f in fields)

{
if (f == null || String.IsNullOrEmpty(f.FiledName) || fieldsList.Contains(f))
continue;
fieldsList.Add(f);
}
ClearCache();
}
}


/**//// <summary>
/// 添加字段
/// </summary>
/// <param name="field"></param>
public void AddField(Field field)

{
if (field == null || fieldsList.Contains(field))
return;
fieldsList.Add(field);
ClearCache();
}


/**//// <summary>
/// 获得字段在列表中的索引位置
/// </summary>
/// <param name="field"></param>
/// <returns></returns>
public int GetIndex(Field field)

{
if (field == null)
return -1;
return fieldsList.IndexOf(field);
}


/**//// <summary>
/// 根据字段名称获得索引
/// </summary>
/// <param name="fieldName"></param>
/// <returns></returns>
public int GetIndex(string fieldName)

{
if (String.IsNullOrEmpty(fieldName))
return -1;
Field f = GetField(fieldName);
return fieldsList.IndexOf(f);
}


/**//// <summary>
/// 从字段列表中删除字段
/// </summary>
/// <param name="fieldName"></param>
public void DropField(string fieldName)

{
Field field = GetField(fieldName);
if (field != null)

{
fieldsList.Remove(field);
ClearCache();
}
}


/**//// <summary>
/// 获得字段名称列表
/// </summary>
/// <returns></returns>
public string[] GetFieldNames()

{
string[] names = new string[fieldsList.Count];
for (int i = 0; i < fieldsList.Count; i++)

{
names[i] = fieldsList.ElementAt<Field>(i).FiledName;
}
return names;
}


/**//// <summary>
/// 获得字段数组
/// </summary>
/// <returns></returns>
public Field[] GetFields()

{
return GetNameCache().Values.ToArray();
}




/**//// <summary>
/// 清除字段列表
/// </summary>
public void Clear()

{
fieldsList.Clear();
ClearCache();
}


/**//// <summary>
/// 更新字段
/// </summary>
/// <param name="oldField"></param>
/// <param name="newField"></param>
public void UpdateField(Field oldField, Field newField)

{
lock (lockObject)

{
if (oldField == null || newField == null)
return;
int index = GetIndex(oldField);
if (index >= 0)

{
fieldsList.Remove(oldField);
fieldsList.Insert(index, newField);
ClearCache();
}
}
}

#endregion


private#region private


/**//// <summary>
/// 获得字段缓存
/// </summary>
/// <returns></returns>
private Dictionary<string, Field> GetNameCache()

{
if (fieldCache == null)

{
fieldCache = new Dictionary<string, Field>();
foreach (Field f in fieldsList)

{
if (!fieldCache.ContainsKey(f.FiledName.Trim()))

{
fieldCache.Add(f.FiledName.Trim(), f);
}
}
}
return fieldCache;
}


/**//// <summary>
/// 清空缓存
/// </summary>
private void ClearCache()

{
if (fieldCache != null)

{
fieldCache.Clear();
}
}

#endregion


ICloneable 成员#region ICloneable 成员

public object Clone()

{
MetaData md = new MetaData();
if (fieldsList.Count > 0)

{
md.Fields.AddRange(fieldsList);
}
return md;
}

#endregion


IMobileObject 成员#region IMobileObject 成员

public void GetChildren(XCenter.Framework.Public.Serialization.Mobile.SerializationInfo info, MobileFormatter formatter)

{
}

public void GetState(XCenter.Framework.Public.Serialization.Mobile.SerializationInfo info)

{
info.AddValue("fieldsList", fieldsList);
}

public void SetChildren(XCenter.Framework.Public.Serialization.Mobile.SerializationInfo info, MobileFormatter formatter)

{
}

public void SetState(XCenter.Framework.Public.Serialization.Mobile.SerializationInfo info)

{
fieldsList = info.GetValue<List<Field>>("fieldsList");
}

#endregion
}
DataSetData.cs 数据集定义信息(元数据、数据的容器)

DataSetData

/**//// <summary>
/// 数据集用于前后台传递数据
/// </summary>
[XCenter.Framework.Public.Serialization.Serializable,DataContract]
public class DataSetData : XCenter.Framework.Public.Core.ICloneable,IMobileObject

{

prop#region prop

private MetaData md;
private List<List<object>> datas = new List<List<object>>();


/**//// <summary>
/// 元数据信息
/// </summary>
[DataMember]
public MetaData MetaData

{

get
{ return md; }

set
{ md = value; }
}


/**//// <summary>
/// DataTable数据,目前只能放基本类型数据
/// </summary>
[DataMember]
public List<List<object>> Datas

{

get
{ return datas; }

set
{ datas = value; }
}


#endregion

public virtual Field GetField(string fieldName)

{
if (md == null)
return null;
return md.GetField(fieldName);
}

public virtual string[] GetFieldNames()

{
if(md == null)

return new string[]
{};
return md.GetFieldNames();
}



ICloneable 成员#region ICloneable 成员

public object Clone()

{
DataSetData dsd = new DataSetData();
dsd.MetaData = (MetaData)this.md.Clone();
return dsd;
}

#endregion


IMobileObject 成员#region IMobileObject 成员

public virtual void GetChildren(XCenter.Framework.Public.Serialization.Mobile.SerializationInfo info, MobileFormatter formatter)

{
}

public virtual void GetState(XCenter.Framework.Public.Serialization.Mobile.SerializationInfo info)

{
info.AddValue("md",md);
}

public virtual void SetChildren(XCenter.Framework.Public.Serialization.Mobile.SerializationInfo info, MobileFormatter formatter)

{
}

public virtual void SetState(XCenter.Framework.Public.Serialization.Mobile.SerializationInfo info)

{
md = info.GetValue<MetaData>("md");
}

#endregion
}
以上是数据结构定义。还有两个核心转换方法DataTable 上的扩展方法GetData,该方法的主要作用是将DataTable的数据装入
DataSetData实例。

DataTable扩展方法
/// <summary>
/// 根据DataTable获得DataSetData对象
/// </summary>
/// <param name="dt"></param>
/// <returns></returns>
public static DataSetData GetData(this DataTable dt)
{
DataSetData dsd = new DataSetData();
MetaData md = new MetaData();
//提取元数据信息
foreach (DataColumn col in dt.Columns)
{
Field f = new Field();
f.Caption = col.Caption;
f.DataType = col.DataType.ToString();
f.Expression = col.Expression;
f.FiledName = col.ColumnName;
f.IsKey = col.Unique;
f.IsReadOnly = col.ReadOnly;
f.IsRequire = !col.AllowDBNull;
f.MaxLength = col.MaxLength;
md.AddField(f);
}
dsd.MetaData = md;
//装数据
List<List<object>> datas = new List<List<object>>();
for (int i = 0; i < dt.Rows.Count; i++)
{
List<object> rowData = new List<object>();
for (int j = 0; j < dt.Columns.Count; j++)
{
rowData.Add(dt.Rows[i][j]);
}
datas.Add(rowData);
}
dsd.Datas = datas;
return dsd;
}
上面的方法在Server端运行。下面的方法在Client端运行,主要作用就是将List<List<object>>类型的数据根据元数据信息
生成实体类并填充到List<object>中供UI控件绑定。

DataTableHelper
public static class DataTableHelper

{

/**//// <summary>
/// 将DataSetData的数据转成SL绑定数据源
/// </summary>
/// <param name="dsd"></param>
/// <returns></returns>
public static List<object> ToDataSource(this DataSetData dsd)

{
try

{
if (dsd == null || dsd.Datas == null || dsd.Datas.Count == 0)
return new List<object>();
Type targetType = null;
string className = dsd.MetaData.GetTempClassName();
if (!WindowHelper.TempClassDic.ContainsKey(className))

{
//生成临时类
TypeBuilder tb = GetTypeBuilder(className);
ConstructorBuilder cBuilder = tb.DefineDefaultConstructor(
MethodAttributes.Public |
MethodAttributes.SpecialName |
MethodAttributes.RTSpecialName);
foreach (Field f in dsd.MetaData.Fields)

{
CreateProperty(tb, f.FiledName, Type.GetType(f.DataType));
}
targetType = tb.CreateType();
WindowHelper.TempClassDic.Add(className, targetType);
}
else

{
//临时类已经生成过,从缓存列表中得到
targetType = WindowHelper.TempClassDic[className];
}
if (targetType == null)
return new List<object>();
return GetDatas(targetType,dsd);
}
catch (Exception ex)

{
throw new Exception("动态创建数据源实体类错误", ex);
}
}


/**//// <summary>
/// 填充数据
/// </summary>
/// <param name="targetType"></param>
/// <param name="datas"></param>
/// <param name="dsd"></param>
/// <returns></returns>
private static List<object> GetDatas(Type targetType,DataSetData dsd)

{
try

{
List<object> resultList = new List<object>();
MetaData md = dsd.MetaData;
List<List<object>> datas = dsd.Datas;
foreach (List<object> li in datas)

{
var instance = Activator.CreateInstance(targetType);
List<Field> fields = md.Fields;
for (int i = 0; i < fields.Count; i++)

{
PropertyInfo p = targetType.GetProperty(fields[i].FiledName);
if (p != null)

{
p.SetValue(instance, li[i], null);
}
}
resultList.Add(instance);
}
return resultList;
}
catch (Exception ex)

{
throw new Exception("客户端填充数据错误", ex); //TODO:多语处理
}
}


/**//// <summary>
/// 获得TypeBuilder实例
/// </summary>
/// <param name="typeName"></param>
/// <returns></returns>
private static TypeBuilder GetTypeBuilder(string typeName)

{
try

{
//XCenter.TempData
AssemblyName an = new AssemblyName(DataConst.TempDataAssemblyName);
AssemblyBuilder assemblyBuilder =
AppDomain.CurrentDomain.DefineDynamicAssembly(an,
AssemblyBuilderAccess.Run);
//TempMainModule
ModuleBuilder moduleBuilder =
assemblyBuilder.DefineDynamicModule(DataConst.TempDataModuleName);
TypeBuilder typeBuilder = moduleBuilder.DefineType(typeName,
TypeAttributes.Public |
TypeAttributes.Class |
TypeAttributes.AutoClass |
TypeAttributes.AnsiClass |
TypeAttributes.BeforeFieldInit |
TypeAttributes.AutoLayout, typeof(object));
return typeBuilder;
}
catch (Exception ex)

{
throw new Exception("动态创建类型错误", ex);
}
}

private static void CreateProperty(TypeBuilder tb,string propertyName,
Type propertyType)

{
try

{
if (tb == null ||
propertyType == null ||
String.IsNullOrEmpty(propertyName))

{
throw new ArgumentException(":-> CreateProperty");
}
FieldBuilder fieldBuilder = tb.DefineField("_" + propertyName,
propertyType, FieldAttributes.Private);
PropertyBuilder propertyBuilder =
tb.DefineProperty(
propertyName, PropertyAttributes.HasDefault, propertyType, null);
MethodBuilder getPropMthdBldr =
tb.DefineMethod("get_" + propertyName,
MethodAttributes.Public |
MethodAttributes.SpecialName |
MethodAttributes.HideBySig,
propertyType, Type.EmptyTypes);
ILGenerator getIL = getPropMthdBldr.GetILGenerator();
getIL.Emit(OpCodes.Ldarg_0);
getIL.Emit(OpCodes.Ldfld, fieldBuilder);
getIL.Emit(OpCodes.Ret);
MethodBuilder setPropMthdBldr =
tb.DefineMethod("set_" + propertyName,
MethodAttributes.Public |
MethodAttributes.SpecialName |
MethodAttributes.HideBySig,

null, new Type[]
{ propertyType });

ILGenerator setIL = setPropMthdBldr.GetILGenerator();

setIL.Emit(OpCodes.Ldarg_0);
setIL.Emit(OpCodes.Ldarg_1);
setIL.Emit(OpCodes.Stfld, fieldBuilder);
setIL.Emit(OpCodes.Ret);

propertyBuilder.SetGetMethod(getPropMthdBldr);
propertyBuilder.SetSetMethod(setPropMthdBldr);
}
catch (Exception ex)

{
throw new Exception("动态创建属性错误", ex);
}
}
}
不行了,代码贴太多了,速度太慢了,下一篇Blog再解释吧。(不知是cnblogs的问题还是遨游的问题)
标签:
本站文章除注明转载外,均为本站原创或翻译。欢迎任何形式的转载,但请务必注明出处、不得修改原文相关链接,如果存在内容上的异议请邮件反馈至chenjj@evget.com
文章转载自:博客园