由一个需求聊聊WCF(四)

绑定数据

在本文的需求中,在数据库取出的数据并没有固定的格式,这导致不能使用固定的静态结构传输服务数据。为了传输这样的数据,需要设计一个类似System.Data.DataTable这样的数据结构,如下:

[DataContract]
public class SimpleTable
{
    [DataMember]
    public string[] Columns
    { get; set; }

    [DataMember]
    public Row[] Rows
    { get; set; }
}

[DataContract]
public struct Row
{
    [DataMember]
    public RowItem[] Items
    { get; set; }
}

[DataContract]
public struct RowItem
{
    [DataMember]
    public string Name
    { get; set; }

    [DataMember]
    public string Content
    { get; set; }
}

SimpleTable.columns属性记录数据有多少列,然后每行的数据都放入SimpleTable.Rows中。这样任意形式的行列数据都可以处理了。

服务把数据传递给silverlight后,DataGrid可以绑定这些数据。但这里遇到了一个问题,DataGrid可以绑定静态类型的数据,而我们的数据是动态的。为了能让DataGrid使用这些数据,我想到过下面两个方法:

1.使用List<object>做数据源,数据源里放匿名对象:

List<object> list = new List<object>();
foreach (Row row in Rows)
{
    list.Add(new { ColumnName1 = row.column1Value, ColumnName2 = row.column2Value });
}

实际做时就会发现这样根本行不通,这种做法要求ColumnName是可以直接写在代码中的简单名称,但我们的ColumnName是动态的。这时类似javascript的Eval机制或许能派上用场,但在.net的编译器作为服务(Complier as a Servie)功能推出以前,很好的实现这个机制并不容易。最重要的是,就算能做到也不应该这样去做,这样把原本问题复杂化是在钻牛角尖,会过度设计,误入歧途。

2.使用List<dynamic>做数据源,数据源里放ExpandoObject对象:

List<dynamic> list = new List<dynamic>();
foreach (Row row in Rows)
{
    dynamic data = new ExpandoObject();
    (data as ICollection<KeyValuePair<string, object>>).Add(new KeyValuePair<string, object>(row.columnName, row.columaValue));
    list.Add(data);
}

这样倒是没有第1种方法那样问题,但依然失败了。DataGrid不识别dynamic类型的数据类型。

看来动态数据源是不行了,如果必须使用静态类型数据,剩下的路还有一条:静态数据也是可以用代码动态创建的。.net提供了“反射发出”机制,可以使用代码在程序中生成程序集、模块、类、方法等,这成了能解决需求的唯一救命稻草。

下面是具体做法:

public static IEnumerable GetDataGridSource(SimpleTable simpleTable)
{
    //动态定义程序集、模块、类
    AssemblyName assemblyName = new AssemblyName("DynamicGridViewModelAssembly");
    AssemblyBuilder assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run);
    ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule("DynamicGridViewModelModule");
    TypeBuilder typeBuilder = moduleBuilder.DefineType("DynamicGridViewModelType", TypeAttributes.Public);

    //动态定义类的构造方法
    ConstructorBuilder constructorBuilder = typeBuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, Type.EmptyTypes);
    ILGenerator constructorIL = constructorBuilder.GetILGenerator();
    constructorIL.Emit(OpCodes.Ldarg_0);
    constructorIL.Emit(OpCodes.Call, typeof(object).GetConstructor(Type.EmptyTypes));
    constructorIL.Emit(OpCodes.Ret);

    //动态定义类的属性,每个属性对应数据集中的一列
    foreach (string column in simpleTable.Columns)
    {
        FieldBuilder fieldBuidler = typeBuilder.DefineField("m_" + column, typeof(string), FieldAttributes.Private);
        PropertyBuilder propertyBuilder = typeBuilder.DefineProperty(column, PropertyAttributes.HasDefault, typeof(string), null);
        MethodAttributes methodAttributes = MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig;

        MethodBuilder methodGetBuilder = typeBuilder.DefineMethod("get_" + column, methodAttributes, typeof(string), Type.EmptyTypes);
        ILGenerator methodGetIL = methodGetBuilder.GetILGenerator();
        methodGetIL.Emit(OpCodes.Ldarg_0);
        methodGetIL.Emit(OpCodes.Ldfld, fieldBuidler);
        methodGetIL.Emit(OpCodes.Ret);

        MethodBuilder methodSetBuilder = typeBuilder.DefineMethod("set_" + column, methodAttributes, null, new Type[] { typeof(string) });
        ILGenerator methodSetIL = methodSetBuilder.GetILGenerator();
        methodSetIL.Emit(OpCodes.Ldarg_0);
        methodSetIL.Emit(OpCodes.Ldarg_1);
        methodSetIL.Emit(OpCodes.Stfld, fieldBuidler);
        methodSetIL.Emit(OpCodes.Ret);

        propertyBuilder.SetGetMethod(methodGetBuilder);
        propertyBuilder.SetSetMethod(methodSetBuilder);
    }

    //动态创建类
    Type modelType = typeBuilder.CreateType();
    Type listType = typeof(List<>).MakeGenericType(modelType);
    object list = Activator.CreateInstance(listType);
    MethodInfo methodAdd = listType.GetMethod("Add");

    //动态创建类的实例,每个实例对应数据集中的一行
    foreach (Row row in simpleTable.Rows)
    {
        object model = Activator.CreateInstance(modelType);
        foreach (RowItem item in row.Items)
        {
            PropertyInfo propertyInfo = modelType.GetProperty(item.Name);
            propertyInfo.SetValue(model, item.Content, null);
        }
        methodAdd.Invoke(list, new object[] { model });
    }

    return (IEnumerable)list;
}

通过这个方法创建DataGrid的数据源,虽然类型是动态发出的,但它确实是静态的:List<DynamicGridViewModelType>,这样就可以绑定了:

client.GetTableCompleted += (s, a) =>
{
    dataGrid1.ItemsSource = GetDataGridSource(a.Result);
};
client.GetTableAsync("ExampleTable");

其实这套解决方法也不直观简单,但我没能找到更靠的方案。


到此,silverlight的DataGrid绑定动态类型的工作也完成了,整个需求全部完工。如果大家有什么建议或指导,请指教,谢谢。

 

本文示例下载地址:http://download.csdn.net/detail/wantalcs/4494183

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值