5

I'm writing a PostUpdate Plugin on the contact entity using early binding.
Unfortunately, all properties which should represent 1:x relations are null.
The code is pretty simple:
* CRMcontext is the generated file via CrmSvcUtil.exe,
* service is the IOrganizationService from LocalPluginContext:

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;
}

I followed this example (the last code snippet): http://msdn.microsoft.com/en-us/library/gg695791.aspx

Thanks for any help!

Edit:

/// <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");
    }
}
Sascha
  • 2,193
  • 3
  • 24
  • 38

3 Answers3

2

CRM doesn't load the related entity properties automatically. You'll need to call LoadProperty on each property that is lazily loaded.

And LameCoder is incorrect, LINQ to CRM doesn't generate Fetch Xml, but QueryExpressions, which is why it is limited to whatever capability QueryExpressions possess.

Edit 1 - Why doesn't this work implicitly as the last example in the MSDN article states?

The GetRelatedEntity method is defined as so:

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);
}

If your early bound entity inherits from Entity, then only thing it is doing is accessing it's own internal RelatedEntities collection. It is doing nothing to access the server to load the related property.

If you use the CodeGeneration.CodeCustomization to generate the early bound entities it should work as you have listed, because it'll inherit from CrmEntity, which will load the relationships for you since it overrides the GetRelatedEntity method uses the context to fetch it for you.

Daryl
  • 18,592
  • 9
  • 78
  • 145
  • Strange thing: need to call LoadProperty on lazy-loaded Properties? – Sascha Jan 21 '14 at 14:09
  • 1
    @Sascha Makes sense to me. You have to call it on the Context so it can load it for you. You don't want to be loading every related property for every entity you retrieve. – Daryl Jan 21 '14 at 14:12
  • the implicit calling of LoadProperty is not possible? (like in the last example of the linked msdn page) – Sascha Jan 21 '14 at 14:13
  • The SDK Extensions has a subclass that overrides GetRelatedEntities that is capable of doing a lazy load. So it depends on the implementation of your context – LameCoder Jan 21 '14 at 14:48
  • @LameCoder, but `contact.transactioncurrency_contact` has no reference to the context. – Daryl Jan 21 '14 at 15:18
  • @Daryl It all hooks up in the SDK Extension's subclass of Entity called CrmEntity where GetRelatedEntity is overridden. The Entity has a reference to the context it is attached to. http://pastebin.com/fFu0yzPJ – LameCoder Jan 21 '14 at 15:33
  • 1
    Microsoft makes it confusing because the SDK Extensions inherit from the same classes in the SDK and have a very different behavior. A minor violation of the Liskov Subtituation Principal, but enough to cause some confusion when reading documentation from different sources. – LameCoder Jan 21 '14 at 15:37
  • @LameCoder so rather than inheriting from Microsoft.Xrm.Sdk.Entity, you'll need to have your early bound types inherit from something else ? – Daryl Jan 21 '14 at 17:48
  • @Daryl, yes if you use the SDK Extensions CrmSvcUtil it'll generate types that inherit from CrmEntity. The code customization this example uses does that. http://msdn.microsoft.com/en-us/library/gg695820.aspx – LameCoder Jan 21 '14 at 19:30
1

My understanding is that the LINQ query is just going to create FetchXML, which will not expand relationships unless you specifically request it.

You should do a join in your LINQ query to get the relationships you need, but be aware that according to the CRM 2013 SDK LINQ queries only support inner joins. So you won't be able to get back records that are missing the relationship.

If you use the SVC Util to generate your earlybound types with the SDK extensions assemblies (which may be difficult to use within a plugin) the Context that the extension has is capable of auto-expanding when you access the property. See the Microsoft.Xrm.Client.CrmOrganizationServiceContext class for details, you'd need to attach the entity to the context if it isn't already by calling Attach. Keep in mind that this is just going to do a query for the relationship lazily, so it'll be multiple queries behind the scene.

If you want it all in one query, and need a LEFT join, try using FetchXML directly.

Edit: Also notice that in the MSDN link you specified, the example is trying to show how the related entity is null unless you call LoadProperty. So you could just simply call LoadProperty to load what you need.

LameCoder
  • 1,287
  • 7
  • 22
  • Sorry for the maybe stupid question, but: the example says "..calling LoadProperty implicitly..". Where is LoadProperty implicitly called? – Sascha Jan 21 '14 at 14:12
  • If you look at the MSDN example, it is explictly called. If you are using the Microsoft.Xrm.Client.CrmOrganizationServiceContext it doesn't use LoadProperty but has another mechanism in an override of GetRelatedEntity that will lazy load. – LameCoder Jan 21 '14 at 14:47
  • CRM 2013 can do outer joins now! You are correct that it is not supported in LINQ queries or QueryExpressions, but it is supported in FetchXML. Great for reports! http://dynamicscrmusualsuspect.blogspot.com/2013/10/crm-2013-fetchxml-enhancements.html – Josh Painter Jan 23 '14 at 18:22
1

For the 2016 update of CRM some things have changed. You should now use the LoadProperty method as Daryl suggested. This will work.

I used the CodeGeneration.CodeCustomization to generate the early bound entities, but it seems that the CRM 2016 SDK doesn't have the required Microsoft.Xrm.Client.CodeGeneration.dll anymore unfortunately. So this way doesn't work anymore as of the 2016 update.