【问题标题】:How to turn properties of class into parameters?如何将类的属性转换为参数?
【发布时间】:2012-07-31 21:52:35
【问题描述】:

背景

我正在使用 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 时,列名称都必须小写,这会导致可读性差,即 thissillyexampleisntthatreadableThisSillyExampleIsntThatReadable

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

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

有什么想法吗?

【问题讨论】:

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


    【解决方案1】:

    不幸的是,您的尝试/目标语法在 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");
    

    【讨论】:

    • 你说得对,ColumnSet 的构造函数是 ColumnSet(params string[] arguments)。
    【解决方案2】:

    这里有几个活动部分。

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

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

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

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

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

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

    【讨论】:

    • 我不确定您所说的需要后期绑定执行是什么意思...我已经拥有通过 CrmSrvcUtil 构建的所有类。我假设 Fetch,你的意思是 Fetch xml?只是想知道如何存储您的 fetch XML?它是作为编译字符串直接存储在您的 C# 类中,还是将其存储为 XML 文件,以便在需要时加载?
    猜你喜欢
    • 2012-09-26
    • 1970-01-01
    • 1970-01-01
    • 2021-01-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多