Получение ранних связей

Когда я связываю аннотации с конкретным сущностью, а не создаю такие отношения:

var associateRequest = new AssociateRequest
{
    Target = new EntityReference(SalesOrder.EntityLogicalName, salesOrderGuid),
    RelatedEntities = new EntityReferenceCollection
    {
        new EntityReference(Annotation.EntityLogicalName, noteGuid),
    },
    Relationship = new Relationship("SalesOrder_Annotation")
};

Можно ли связать отношения строго типизированным способом:

var associateRequest = new AssociateRequest
{
    Target = new EntityReference(SalesOrder.EntityLogicalName, salesOrderGuid),
    RelatedEntities = new EntityReferenceCollection
    {
        new EntityReference(Annotation.EntityLogicalName, noteGuid)
    },
    Relationship = SalesOrder.Relationships.SalesOrder_Annotation // <----- ???
};

Это было бы похоже на возможность получить логическое имя во время разработки:

SalesOrder.EntityLogicalName

Можно ли ссылаться на конкретные отношения 1: N таким же образом:

SalesOrder.Relationships.SalesOrder_Annotation

Ответы

Ответ 1

Значение, которое вы ищете, хранится в атрибуте кода RelationshipSchemaNameAttribute, если вы создаете свой код с помощью стандартного приложения CrmSvcUtil.exe, предоставленного в SDK (\SDK\Bin\CrmSvcUtil.exe). Я проверил этот код в консольном приложении, используя файл класса ранней связанной сущности, предоставленный в SDK (\SDK\SampleCode\CS\HelperCode\MyOrganizationCrmSdkTypes.cs).

Вызвать метод следующим образом (в вашем примере):

var relationship = GetRelationship<SalesOrder>(nameof(SalesOrder.SalesOrder_Annotation))

Или, если вы хотите вернуть фактическое строковое значение:

var relationshipName = GetRelationshipSchemaName<SalesOrder>(nameof(SalesOrder.SalesOrder_Annotation))

Добавьте этот код в класс-помощник в вашем приложении:

public static string GetRelationshipSchemaName<T>(string relationshipPropertyName) where T:Entity
{
    return typeof (T).GetProperties()
        .FirstOrDefault(x => x.Name == relationshipPropertyName)
        .GetCustomAttributes()
        .OfType<RelationshipSchemaNameAttribute>()
        .FirstOrDefault()
        ?.SchemaName;            
}

public static Relationship GetRelationship<T>(string relationshipPropertyName) where T : Entity
{
    return new Relationship(typeof(T).GetProperties()
        .FirstOrDefault(x => x.Name == relationshipPropertyName)
        .GetCustomAttributes()
        .OfType<RelationshipSchemaNameAttribute>()
        .FirstOrDefault()
        ?.SchemaName);
}

Это будет выглядеть ваш обновленный код:

var associateRequest = new AssociateRequest
                                   {
                                       Target =
                                           new EntityReference(
                                               SalesOrder.EntityLogicalName,
                                               salesOrderGuid),
                                       RelatedEntities =
                                           new EntityReferenceCollection
                                               {
                                                   new EntityReference(
                                                       Annotation
                                                           .EntityLogicalName,
                                                       noteGuid)
                                               },
                                       Relationship = GetRelationship<SalesOrder>(nameof(SalesOrder.SalesOrder_Annotation)) ///////////????
                                   };

Ответ 2

С другой стороны, мой ответ на шаблон t4 кажется излишним Вы можете использовать деревья выражений и метод расширения, чтобы легко получить то, что вам нужно

using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;

namespace ConsoleApplication9
{
    internal class Program
    {
        private static void Main(string[] args)
        {
            Relationship r = new Class1().GetRelationShip(s => s.RelationShipProperty);
            Console.WriteLine(r.Name);
            System.Console.ReadLine();
        }
    }

    public static class MyExtention
    {
        public static Relationship GetRelationShip<T, TProperty>(this T t, Expression<Func<T, TProperty>> expression)
        {
            return new Relationship(((expression.Body as MemberExpression).Member as PropertyInfo)
                    .GetCustomAttributes(typeof(RelationshipAttribute))
                    .Select(a=>(RelationshipAttribute)a)
                    .First().Name
                    );
        }
    }

    public class RelationshipAttribute : System.Attribute
    {
        public string Name { get; set; }

        public RelationshipAttribute(string name)
        {
            Name = name;
        }
    }

    public class Relationship
    {
        public string Name { get; set; }

        public Relationship(string name)
        {
            Name = name;
        }
    }

    public class Class1
    {
        [Relationship("RelationShipA")]
        public List<int> RelationShipProperty { get; set; }
    }
}

Ответ 3

Из того, что вы сказали, ваши сгенерированные классы имеют атрибут с именем ваших отношений.
Все, что вам нужно, это шаблон t4, который генерирует класс с сильно типизированными свойствами для ваших отношений

Скажем, у вас есть следующий код в вашем проекте

namespace ConsoleApplication9
{
    public class RelationshipAttribute : System.Attribute
    {
        public string Name { get; set; }

        public RelationshipAttribute(string name) { Name = name; }
    }

    [Relationship("RelationShipA")]
    public class Class1 { }

    [Relationship("RelationShipB")]
    public class Class2 { }

    [Relationship("RelationShipC")]
    public class Class3 { }

}

Этот шаблон

<#@ template debug="false" hostspecific="false" language="C#" #>
<#@ assembly name="System" #>
<#@ assembly name="System.Core" #>
<#@ assembly name="$(TargetPath)" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System" #>
<#@ import namespace="System.Reflection" #>
<#@ output extension=".cs" #>

namespace YourNameSpace
{
   public static class Relationships
   {
      <# 
         var types = typeof(ConsoleApplication9.RelationshipAttribute).Assembly
                     .GetTypes()
                     .Select(t => new { t.Name, Value = t.GetCustomAttribute(typeof(ConsoleApplication9.RelationshipAttribute)) })
                     .Where(t => t.Value != null)
                     .Select(t=> new { t.Name,Value= ((ConsoleApplication9.RelationshipAttribute)t.Value).Name })
                     .ToList();

                 foreach (var attr in types)
  { #>
 public static class  <#=  attr.Name #>
        {
            public const string <#=  attr.Value #> = "<#=  attr.Value #>";
        }
      <# }

  #>}
}

создаст следующий файл .cs

namespace YourNameSpace
{
   public static class Relationships
   {
         public static class  Class1
         {
           public const string RelationShipA = "RelationShipA";
         }
         public static class  Class2
         {
           public const string RelationShipB = "RelationShipB";
         }
         public static class  Class3
         {
           public const string RelationShipC = "RelationShipC";
         }
   }
}

Затем вы можете использовать его как

Relationship = new Relationship(Relationships.Class1.RelationShipA )

Ответ 4

Я не уверен, правильно ли получил вопрос. Не будет ли функция С# 6.0 nameof(...) делать?

т.

new Relationship(nameof(RelationshipSalesOrder.Relationships.SalesOrder_Annotation));