Почему все ссылочные свойства пусты в моем плагине CRM?

Я пишу подключаемый модуль PostUpdate для объекта контакта, используя раннее связывание.
К сожалению, все свойства, которые должны представлять отношения 1:x, пусты.
Код довольно прост:
* CRMcontext — это сгенерированный файл. через CrmSvcUtil.exe,
* сервис — это IOrganizationService из 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;
}

Я следовал этому примеру (последний фрагмент кода): 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");
    }
}

person Sascha    schedule 20.01.2014    source источник
comment
а GetRelatedEntity работает?   -  person Guido Preite    schedule 20.01.2014
comment
GetRelatedEntity вызывается сгенерированной оболочкой (в get_transactioncurrency_contact) и возвращает NULL.   -  person Sascha    schedule 20.01.2014
comment
Вы уверены, что это называется в вашем коде? вы отладили это? а вы пробовали вызывать GetRelatedEntity вручную?   -  person Guido Preite    schedule 20.01.2014
comment
Да, он вызывается — я удалил автоматически сгенерированный заголовок из кода-оболочки (сгенерированный CrmSvcUtil.exe) и установил точку останова в геттере свойства.   -  person Sascha    schedule 20.01.2014
comment
Опубликуйте свой код свойства transactioncurrency_contact.   -  person Daryl    schedule 20.01.2014
comment
Код свойства теперь находится в исходном сообщении.   -  person Sascha    schedule 20.01.2014


Ответы (3)


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, использующий контекст, чтобы получить его для вас.

person Daryl    schedule 20.01.2014
comment
Странная вещь: нужно вызывать LoadProperty для лениво загруженных свойств? - person Sascha; 21.01.2014
comment
@Sascha Имеет смысл для меня. Вы должны вызвать его в контексте, чтобы он мог загрузить его для вас. Вы не хотите загружать каждое связанное свойство для каждой сущности, которую вы извлекаете. - person Daryl; 21.01.2014
comment
неявный вызов LoadProperty невозможен? (как в последнем примере связанной страницы msdn) - person Sascha; 21.01.2014
comment
SDK Extensions имеет подкласс, который переопределяет GetRelatedEntities, способный выполнять отложенную загрузку. Так что это зависит от реализации вашего контекста - person LameCoder; 21.01.2014
comment
@LameCoder, но contact.transactioncurrency_contact не имеет отношения к контексту. - person Daryl; 21.01.2014
comment
@Daryl Все это связано с подклассом Entity расширения SDK, называемым CrmEntity, где GetRelatedEntity переопределяется. Сущность имеет ссылку на контекст, к которому она присоединена. pastebin.com/fFu0yzPJ - person LameCoder; 21.01.2014
comment
Microsoft делает это запутанным, потому что расширения SDK наследуют одни и те же классы в SDK и имеют совершенно другое поведение. Небольшое нарушение принципа подстановки Лискова, но достаточное, чтобы вызвать некоторую путаницу при чтении документации из разных источников. - person LameCoder; 21.01.2014
comment
@LameCoder, поэтому вместо того, чтобы наследовать от Microsoft.Xrm.Sdk.Entity, вам нужно, чтобы ваши ранние связанные типы наследовались от чего-то другого? - person Daryl; 21.01.2014
comment
@Daryl, да, если вы используете SDK Extensions CrmSvcUtil, он будет генерировать типы, наследуемые от CrmEntity. Настройка кода, используемая в этом примере, делает это. msdn.microsoft.com/en-us/library/gg695820.aspx - person LameCoder; 21.01.2014

Насколько я понимаю, запрос LINQ просто создаст FetchXML, который не будет расширять отношения, если вы специально не запросите его.

Вы должны выполнить соединение в своем запросе LINQ, чтобы получить нужные отношения, но имейте в виду, что в соответствии с CRM 2013 SDK запросы LINQ поддерживают только внутренние соединения. Таким образом, вы не сможете вернуть записи, в которых отсутствует связь.

Если вы используете SVC Util для создания своих ранних типов с помощью сборок расширений SDK (которые могут быть трудными для использования в плагине), контекст, который имеет расширение, может автоматически расширяться при доступе к свойству. Подробности см. в классе Microsoft.Xrm.Client.CrmOrganizationServiceContext. Вам нужно будет присоединить сущность к контексту, если это еще не сделано, вызвав Attach. Имейте в виду, что это просто ленивый запрос для отношения, поэтому за сценой будет несколько запросов.

Если вам нужно все это в одном запросе и вам нужно ЛЕВОЕ соединение, попробуйте напрямую использовать FetchXML.

Изменить: также обратите внимание, что в указанной вами ссылке MSDN пример пытается показать, что связанный объект имеет значение null, если вы не вызовете LoadProperty. Таким образом, вы можете просто вызвать LoadProperty, чтобы загрузить то, что вам нужно.

person LameCoder    schedule 20.01.2014
comment
Извините за, может быть, глупый вопрос, но: в примере говорится ..неявно вызывать LoadProperty... Где неявно вызывается LoadProperty? - person Sascha; 21.01.2014
comment
Если вы посмотрите на пример MSDN, он явно вызывается. Если вы используете Microsoft.Xrm.Client.CrmOrganizationServiceContext, он не использует LoadProperty, но имеет другой механизм в переопределении GetRelatedEntity, который будет выполнять отложенную загрузку. - person LameCoder; 21.01.2014
comment
CRM 2013 теперь может выполнять внешние соединения! Вы правы в том, что он не поддерживается в запросах LINQ или QueryExpressions, но поддерживается в FetchXML. Отлично подходит для отчетов! dynamicscrmusualsuspect.blogspot.com/2013/10/ - person Josh Painter; 23.01.2014

В обновлении CRM 2016 года некоторые вещи изменились. Теперь вы должны использовать метод LoadProperty, как предложил Дэрил. Это сработает.

Я использовал CodeGeneration.CodeCustomization для создания ранних связанных сущностей, но, похоже, что в CRM 2016 SDK больше нет требуемой Microsoft.Xrm.Client.CodeGeneration.dll, к сожалению. Таким образом, этот способ больше не работает с обновлением 2016 года.

person Edwin Dijkstra    schedule 26.02.2016