如何将类的属性转换为参数?

时间:2012-07-30 14:59:01

标签: c#-4.0 dynamics-crm-2011

背景

我正在使用Microsoft Dynamics CRM 2011 QueryExpressions,对于所有非CRM人员,只知道我正在使用SDK来访问要求您放置列的字符串名称的数据库您正在选择自定义ColumnSet类:

new ColumnSet("personid", "name", "age");

SDK确实生成了早期绑定类,因此我确实有所有数据库表的类,并且早期绑定类都有一个字典,其键是已填充在对象上的表的列。即:

var p = new Person { Name = "John", Age = 39, SSN = null };
p.Attributes.Count == 3;
// p.Attributes.Keys == { "name", "age", "ssn" };

问题

填充ColumnSet时有三个问题

  1. 我忘记了类/表包含哪些列
  2. 我胖了一些列,直到运行时才收到错误
  3. 实例化ColumnSet时,列名称all必须为小写,这会导致可读性差,即thissillyexampleisntthatreadable vs ThisSillyExampleIsntThatReadable
  4. 所有这三个问题都可以通过早期绑定来解决。

    我知道我可以为包含所有类的列的每个类创建一个枚举或结构,即:new ColumnSet(PersonColumns.PersonId,PersonColumns.Name,PersonColumns.Age),但是我希望它能使用具有的类已经生成了。

    我最好的尝试

    我目前能想到的最好的是:

    ColumnSetFactory.Create<Person>(p => p.PersonId = null, p.Name = null, p.Age = null);
    

    其中Create接受类型为T的对象(本例中为person),然后检查对象的字典以生成并返回ColumnSet。

    目标

    使用一个泛型函数,利用早期绑定类生成ColumnSet:

    ColumnSetFactory.Create<Person>(p => p.PersonId, p.Name, p.Age);
    

    有什么想法吗?

2 个答案:

答案 0 :(得分:3)

不幸的是,您的尝试/目标语法在C#中不起作用,但您可能会接近它。

我不熟悉动态crm所以我正在做这个假设,ColumnSet的构造函数接受可变数量的字符串参数并且有一个签名:

public ColumnSet(params string[] arguments)

您可以创建一个lambda表达式,该表达式返回一个对象初始值设定项(用于创建匿名对象)并使用一些反射来使用初始化程序的成员绑定来调用构造函数。如果我理解您要完成的任务,您希望使用对象的现有参数名称来将这些名称实际传递给此构造函数以创建对象。

以下是你如何做到这一点:

public static ColumnSet Create<T>(Expression<Func<T, object>> parameters)
{
    var initializer = parameters.Body as NewExpression;
    if (initializer == null || initializer.Members == null)
        throw new ArgumentException("lambda must return an object initializer");
    var memberNames = initializer.Members
        .Select(member => member.Name.ToLower())
        .ToArray();
    var ctor = typeof(ColumnSet).GetConstructor(new Type[] { typeof(string[]) });
    return (ColumnSet)ctor.Invoke(new object[] { memberNames });
}

然后使用它,像这样调用它:

ColumnSetFactory.Create<Person>(p => new { p.PersonId, p.Name, p.Age });

// Personally I prefer to call it like this to let the compiler
// infer the generic arguments
ColumnSetFactory.Create((Person p) => new { p.PersonId, p.Name, p.Age });

哪个会生成对构造函数的等效调用:

new ColumnSet("personid", "name", "age");

您甚至可以组成列名并为它们提供随机值,不会使用值本身,只会使用成员的名称。

ColumnSetFactory.Create<Person>(p => new
{
    p.PersonId,
    p.Name,
    p.Age,
    Foobar = 0,
    Boo = "rawr!!!",
});

哪个会生成对构造函数的等效调用:

new ColumnSet("personid", "name", "age", "foobar", "boo");

答案 1 :(得分:0)

这里有几个活动部件。

首先,使用C#,您需要具有延迟绑定执行的早期绑定项目,很遗憾,您无法以您想要的方式到达目的地。早期绑定在编译时之前构建一个类。

因此,一个选项是为您想要的少数实体创建自己的简化早期绑定创建(或使用Microsoft早期绑定的东西)

另一种选择是使用词典 - 但这可能会更加繁琐,只能解决问题#3。

我知道这不是您正在寻找的答案,但我会退一步看看使用Fetch。

使用像stunnware这样的工具,您可以轻松创建获取查询并对其进行测试,以便获得您正在寻找的结果。

然后只需创建一个通用函数来执行fetch以返回IList&gt;。这样,您可以根据在fetch中指定的属性轻松引用字段。 (处理问题#1&amp;#2)