【问题标题】:Why are the properties of my partial class empty after I've run my LINQ query?为什么运行 LINQ 查询后部分类的属性为空?
【发布时间】:2016-11-18 10:53:50
【问题描述】:

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

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

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}
2016 年 18 月 11 日
{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

【问题讨论】:

  • 我对 Microsoft CRM 真的一无所知,但您提到您正在扩展 Project 课程,但在您的问题中,您似乎正在创建一个新课程并且仅从 Microsoft.Xrm.Sdk.Entity 扩展而不是来自您自动生成的部分类。对吗?
  • 老实说,我不确定 CRM 部分是否完全相关,因为它是来自数据库的标准 LINQ 查询,SDK 只是掩盖了其中的一部分,但我将其包括在内以供后代使用。不确定您的意思,但在重构代码以尝试使其正常工作后,我不需要在我的部分类上声明Microsoft.Xrm.Sdk.Entity,因为这是一个错误,所以我将其删除。 Visual Studio 肯定认识到我正在正确扩展类,它向我显示了引用部分类的两个部分的正确位置。
  • 我很抱歉 - 我刚刚意识到我们正在谈论部分类以及它的真正含义,例如,在代码中......不要介意我之前的评论。

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


【解决方案1】:

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

为了帮助解决这个问题,我将 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,或者更直接地回答您的问题,即为什么您的值没有被填充,很可能是因为您不是t 实际上将值放入实体的 AttributeCollection 属性中。从实体转换为您定义的类型时,CRM 做了一些有趣的优化。

【讨论】:

  • 对于任何对 Daryl 所指内容感兴趣的人,他的意思是 XrmToolBox 的名为“EarlyBoundGenerator”的插件中的“使只读字段可编辑”选项。这解决了我的问题,只需使用此工具生成类,然后按照描述更改我的查询。太棒了,谢谢你。现在我需要弄清楚为什么其他项目不这样做就可以工作,也许改天!
  • 是的,这是另一个选项,但您不必使字段可编辑。 AnonymousType 构造函数允许设置只读字段,因为它们都由 AttributesCollection 支持。
  • 哦,对了,我现在和你在一起。谢天谢地,出于我正在做的事情的目的(这是一项隐藏在任何用户之外的服务),我可以选择简单的出路。将记住这一点以备将来使用,谢谢!
  • 这取决于作为开发人员的您,以及通过设置真正只读的字段(createdOn/ModifedOn)并在运行时出现错误而不是在运行时出现错误的可能性有多大编译。
  • 明白。在这种情况下,我只是从 CRM 中读取数据以推送到另一个系统,而不进行任何更改。当然,这是对其他任何使只读字段可编辑的人的公平警告。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-08-19
  • 2016-12-06
  • 1970-01-01
  • 1970-01-01
  • 2014-05-31
  • 2013-03-13
相关资源
最近更新 更多