4

背景

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

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

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

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 时,列名称都必须小写,这会导致可读性差,即thissillyexampleisntthatreadablevsThisSillyExampleIsntThatReadable

所有这三个问题都可以通过早期绑定来解决。

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

有任何想法吗?

4

2 回答 2

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");
于 2012-07-30T15:45:37.767 回答
0

这里有几个活动部分。

首先,使用 C#,您需要具有后期绑定执行的早期绑定项目,不幸的是,您无法以您想要的方式到达那里。早期绑定在编译时间之前构建一个类。

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

另一种选择是使用字典 - 但这可能会更乏味并且只处理问题#3。

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

使用 stunnware 之类的工具,您可以轻松创建获取查询并对其进行测试,以便获得所需的结果。

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

于 2012-07-31T13:52:35.207 回答