为什么我的CRM插件中所有的引用属性都是null?

5

我正在使用早期绑定编写联系人实体的PostUpdate插件。
不幸的是,应该代表1:x关系的所有属性都为空。
代码非常简单:
* CRMcontext是通过CrmSvcUtil.exe生成的文件,
* service是来自LocalPluginContext的IOrganizationService:

using ( var serviceContext = new CRMcontext(service) )
{
  // This works fine
  var contact = serviceContext.CreateQuery<Contact>().First(c => c.Id == context.PrimaryEntityId);

  // why is currency null after this line?! (and yes, it's set in the entity)
  var currency = contact.transactioncurrency_contact;
}

我跟随了这个示例(最后一段代码):http://msdn.microsoft.com/en-us/library/gg695791.aspx 感谢您的任何帮助!
编辑:
/// <summary>
/// N:1 transactioncurrency_contact
/// </summary>
[Microsoft.Xrm.Sdk.AttributeLogicalNameAttribute("transactioncurrencyid")]
[Microsoft.Xrm.Sdk.RelationshipSchemaNameAttribute("transactioncurrency_contact")]
public TransactionCurrency transactioncurrency_contact
{
    get
    {
        return this.GetRelatedEntity<TransactionCurrency>("transactioncurrency_contact", null);
    }
    set
    {
        this.OnPropertyChanging("transactioncurrency_contact");
        this.SetRelatedEntity<TransactionCurrency>("transactioncurrency_contact", null, value);
        this.OnPropertyChanged("transactioncurrency_contact");
    }
}

GetRelatedEntity 如何工作? - Guido Preite
GetRelatedEntity 被生成的包装器(在 get_transactioncurrency_contact 中)调用并返回 NULL。 - Sascha
你确定在你的代码中调用了它吗?你进行了调试吗?你尝试手动调用GetRelatedEntity了吗? - Guido Preite
是的,正在调用 - 我已经从包装代码中删除了自动生成的头文件(由CrmSvcUtil.exe生成),并在属性的getter中设置了断点。 - Sascha
发布您的 transactioncurrency_contact 属性代码。 - Daryl
属性代码现在已经在原始帖子中。 - Sascha
3个回答

2
CRM不会自动加载相关实体属性。您需要对每个惰性加载的属性调用LoadProperty
而LameCoder是错误的,LINQ to CRM不生成Fetch Xml,而是生成QueryExpressions,这就是为什么它受到QueryExpressions能力限制的原因。
编辑1-为什么这不能像MSDN文章中的最后一个例子那样隐式工作?
GetRelatedEntity方法定义如下:
protected virtual IEnumerable<TEntity> GetRelatedEntities<TEntity>(string relationshipSchemaName, EntityRole? primaryEntityRole) where TEntity : Entity
{
  if (string.IsNullOrWhiteSpace(relationshipSchemaName))
    throw new ArgumentNullException("relationshipSchemaName");
  Relationship key = new Relationship(relationshipSchemaName)
  {
    PrimaryEntityRole = primaryEntityRole
  };
  if (!this.RelatedEntities.Contains(key))
    return (IEnumerable<TEntity>) null;
  else
    return Enumerable.Cast<TEntity>((IEnumerable) this.RelatedEntities[key].Entities);
}

如果您的早期绑定实体继承自Entity,那么它所做的唯一事情就是访问自己内部的RelatedEntities集合。它没有做任何访问服务器来加载相关属性的操作。
如果您使用CodeGeneration.CodeCustomization生成早期绑定实体,则应按您列出的方式工作,因为它将继承自CrmEntity,后者将为您加载关系,因为它覆盖了GetRelatedEntity方法并使用上下文为您获取它。

奇怪的事情:需要在延迟加载的属性上调用LoadProperty吗? - Sascha
1
@Sascha 对我来说很有道理。你必须在上下文中调用它,这样它才能为您加载它。您不希望为检索的每个实体加载每个相关属性。 - Daryl
隐式调用LoadProperty是不可能的吗?(就像链接的MSDN页面中的最后一个示例一样) - Sascha
@LameCoder,但是 contact.transactioncurrency_contact 没有参考上下文。 - Daryl
1
微软让人感到困惑,因为SDK扩展从SDK中的相同类继承,并具有非常不同的行为。这是对Liskov替换原则的轻微违反,但足以在阅读来自不同来源的文档时引起一些混淆。 - LameCoder
显示剩余4条评论

1
我的理解是,LINQ查询只会创建FetchXML,除非你特别请求,否则不会扩展关系。 在LINQ查询中执行连接以获取所需的关系,但请注意,根据CRM 2013 SDK,LINQ查询仅支持内部连接。 因此,您将无法获取缺少关系的记录。如果您使用SVC Util使用SDK扩展程序集生成早期绑定类型(可能难以在插件中使用),则扩展具有的上下文能够在访问属性时自动展开。有关详细信息,请参见Microsoft.Xrm.Client.CrmOrganizationServiceContext类,如果尚未通过调用Attach将实体附加到上下文,则需要将其附加到上下文。请记住,这只会懒惰地查询关系,因此它将在幕后进行多个查询。如果您希望一次性获取所有内容,并且需要左连接,请尝试直接使用FetchXML。编辑:还要注意,在您指定的MSDN链接中,示例试图显示相关实体为空,除非您调用LoadProperty。因此,您可以简单地调用LoadProperty来加载所需内容。

抱歉可能问题有点愚蠢,但是例子中说:“隐式调用LoadProperty”。LoadProperty在哪里被隐式调用了? - Sascha
如果您查看MSDN示例,它是明确调用的。如果您正在使用Microsoft.Xrm.Client.CrmOrganizationServiceContext,则不使用LoadProperty,而是在GetRelatedEntity的覆盖中具有另一种机制来进行惰性加载。 - LameCoder
CRM 2013现在可以执行外连接了!您是正确的,LINQ查询或QueryExpressions不支持它,但在FetchXML中支持它。非常适合报告!http://dynamicscrmusualsuspect.blogspot.com/2013/10/crm-2013-fetchxml-enhancements.html - Josh Painter

1

对于 CRM 的 2016 更新,有一些变化。正如 Daryl 建议的那样,现在应该使用 LoadProperty 方法。这将起作用。

我使用 CodeGeneration.CodeCustomization 来生成早期绑定实体,但不幸的是,CRM 2016 SDK 不再具有所需的 Microsoft.Xrm.Client.CodeGeneration.dll。因此,自 2016 年更新以来,这种方式不再起作用。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接