在运行LINQ查询后,为什么我的部分类的属性为空?

时间:2016-11-18 10:53:51

标签: c# linq dynamics-crm-2011 dynamics-crm-online partial-classes

我正在通过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();

这样可以正常工作并返回正确数量的项目及其ID。

Project类是由CrmSvcUtil.exe自动生成的,并存储在如下的部分类中:

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,因此在运行查询时无法存储它们的值。因为它是自动生成的代码,所以我没有修改它,而是使用一些额外的属性扩展类,如下所示:

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}
  2016/11/17

更令人沮丧的是,这次上次我做了这个工作,并且我让旧项目与新项目并排坐着,看不出我必须做些什么才能修复它。 我是否以某种方式错误地使用了部分类和LINQ查询?

修改

有趣的是,如果我更改查询以选择现有项目而不是创建新项目,那么它可以正常工作:

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

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

1 个答案:

答案 0 :(得分:1)

这是我对标准CrmSvcUtil生成的类的挫折之一。他们不允许设置只读字段,除非您检索整个课程,这是一种不好的做法。

为了帮助解决这个问题,我在XrmToolBox的EarlyBoundGenerator中写了一个更改为&#34; Generate AnonymousType Constructors&#34;

/// <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. 现在可以填充只读字段,无需使用自定义属性!
  3. 如果您愿意,可以复制并粘贴构造函数,切换到使用EarlyBoundGenerator,或者更直接地回答您的问题,了解为什么您的值不会被填充,很可能是因为您实际上并未将值放入实体的AttributeCollection属性中。当从实体转换为您定义的类型时,CRM会做一些有趣的优化。