为什么在我 运行 我的 LINQ 查询之后我的部分 class 的属性是空的?

Why are the properties of my partial class empty after I've run my LINQ query?

我正在通过 SDK 从 Dynamics CRM 2011 中提取项目实体列表,如下所示:

var projects = (from project in Context.ProjectSet
               where project.ModifiedOn > DateTime.Now.AddHours(-1)
               select new Project
               {
                   Id = project.Id
               }).ToList();

这很好用,returns 正确的项目数量及其 ID。

项目 class 由 CrmSvcUtil.exe 自动生成并存储在部分 class 中,如下所示:

Entities.cs
(实际文件比较大,我删减了)

[assembly: Microsoft.Xrm.Sdk.Client.ProxyTypesAssembly()]

namespace Framework.CRM
{
    public partial class Project : Microsoft.Xrm.Sdk.Entity, System.ComponentModel.INotifyPropertyChanging, System.ComponentModel.INotifyPropertyChanged
    {
        // There are lots of properties and events etc here
        // Here's one example of a property for brevity
        [Microsoft.Xrm.Sdk.AttributeLogicalName("createdby")]
        public Microsoft.Xrm.Sdk.EntityReference CreatedBy
        {
            get
            {
                return GetAttributeValue<Microsoft.Xrm.Sdk.EntityReference>("createdby");
            }
        }
    }
}

几个自动生成的属性没有 setter,所以当我 运行 我的查询时我无法存储它们的值。因为它是自动生成的代码,所以我没有修改它,而是用一些额外的属性扩展了 class,例如:

CustomProperties.cs

namespace Framework.CRM
{
    using System;

    public partial class Project
    {
        public Guid CustomerGuid { get; set; }
        public DateTime? LastUpdated { get; set; }
        // It's only the nullable DateTime because CRM implements 
        // that for some reason even though it's never null
    }
}

我现在可以使用相同的查询,但这次我可以存储一些以前不能存储的属性:

var projects = (from project in Context.ProjectSet
               where project.ModifiedOn > DateTime.Now.AddHours(-1)
               select new Project
               {
                   Id = project.Id,
                   LastUpdated = project.ModifiedOn,
                   CustomerGuid = project.CompanylinkId != null ? project.CompanylinkId.Id : Guid.Empty
               }).ToList();

这仍然获取了正确数量的项目并且填充了 Id 属性 但是 我的所有自定义属性都是空的,我不明白为什么 .我可以向您保证 ModifiedOnCompanylinkId 在 CRM 中不为空。

例如:

foreach (var project in projects)
{
    Console.WriteLine(project.Id.ToString());
    Console.WriteLine(project.LastUpdated?.ToString("dd/MM/yyyy"));
}

输出:

{40e74702-d17b-4598-b4a0-150b353459a4}
 
{134aa5bd-aef3-4489-93c2-a3c0bdebd20a}
 

预计:

{40e74702-d17b-4598-b4a0-150b353459a4}
18/11/2016
{134aa5bd-aef3-4489-93c2-a3c0bdebd20a}
17/11/2016

更令人沮丧的是,这在我上次执行时有效,而且我将旧项目与新项目并排放置,看不到我必须做些什么来修复它。 我是否错误地使用了部分 classes 与 LINQ 查询的结合?

编辑:

有趣的是,如果我将查询更改为 select 现有项目而不是创建一个新项目,它就可以正常工作:

var projects = (from project in Context.ProjectSet
               where project.ModifiedOn > DateTime.Now.AddHours(-1)
               select project).ToList();

我真的不想这样做,因为我只想要数百个字段中的大约六个字段,这相当于 select * from

这是我对标准 CrmSvcUtil 生成的 classes 的不满之一。它们不允许设置只读字段,除非您检索整个 class,这是一种不好的做法。

为了帮助克服这个问题,我在 XrmToolBox 的 EarlyBoundGenerator 中写了一个更改为“Generate AnonymousType Constructors

/// <summary>
/// Constructor for populating via LINQ queries given a LINQ anonymous type
/// <param name="anonymousType">LINQ anonymous type.</param>
/// </summary>
[System.Diagnostics.DebuggerNonUserCode()]
public Project(object anonymousType) : 
        this()
{
    foreach (var p in anonymousType.GetType().GetProperties())
    {
        var value = p.GetValue(anonymousType, null);
        var name = p.Name.ToLower();

        if (name.EndsWith("enum") && value.GetType().BaseType == typeof(System.Enum))
        {
            value = new Microsoft.Xrm.Sdk.OptionSetValue((int) value);
            name = name.Remove(name.Length - "enum".Length);
        }

        switch (name)
        {
            case "id":
                base.Id = (System.Guid)value;
                Attributes["projectid"] = base.Id;
                break;
            case "productid":
                var id = (System.Nullable<System.Guid>) value;
                if(id == null){ continue; }
                base.Id = id.Value;
                Attributes[name] = base.Id;
                break;
            case "formattedvalues":
                // Add Support for FormattedValues
                FormattedValues.AddRange((Microsoft.Xrm.Sdk.FormattedValueCollection)value);
                break;
            default:
                Attributes[name] = value;
                break;
        }
    }
}

这样您就可以将查询更改为如下内容:

var projects = (from project in Context.ProjectSet
               where project.ModifiedOn > DateTime.Now.AddHours(-1)
               select new Project(new { 
               {
                   project.Id,
                   project.ModifiedOn,
                   project.CompanylinkId 
               }).ToList();

这有两件大事:

  1. 无需指定属性名称两次
  2. 现在可以填充只读字段,不再需要自定义属性!

如果您愿意,您可以复制并粘贴构造函数,切换到使用 EarlyBoundGenerator,或者更直接地回答您关于为什么您的值没有被填充的问题,很可能是因为您实际上并没有将值放入实体的 AttributeCollection 属性 中。从实体转换为您定义的类型时,CRM 会进行一些有趣的优化。