背景

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

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

SDK 确实生成了早期绑定(bind)类,所以我确实有所有数据库表的类,并且早期绑定(bind)类都有一个字典,其键是已填充到对象上的表的列。 IE:
var p = new Person { Name = "John", Age = 39, SSN = null };
p.Attributes.Count == 3;
// p.Attributes.Keys == { "name", "age", "ssn" };

问题

填充 ColumnSet 时出现三个问题
  • 我忘记了类/表包含哪些列
  • 我胖了一些列,直到运行时才出现错误
  • 列名在实例化 ColumnSet 时都必须小写,这会导致可读性差,即 thissillyexampleisntthatreadable vs ThisSillyExampleIsntThatReadable

  • 这三个问题都可以通过早期绑定(bind)来解决。

    我知道我可以为包含类的所有列的每个类创建一个枚举或结构,即: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。

    目标

    有一个利用早期绑定(bind)类生成 ColumnSet 的通用函数:
    ColumnSetFactory.Create<Person>(p => p.PersonId, p.Name, p.Age);
    

    有任何想法吗?

    最佳答案

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

    我不熟悉动态 crm,所以我做出这个假设,ColumnSet 的构造函数采用可变数量的字符串参数并具有签名:

    public ColumnSet(params string[] arguments)
    

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

    您可以这样做:
    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");
    

    关于c#-4.0 - 如何将类的属性转换为参数?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/11724058/

    10-15 13:49