Динамический способ создания EntityTypeConfiguration: тип «TResult» должен быть типом значения, не допускающим значение NULL.

Я думал динамически генерировать EntityTypeConfiguration во время выполнения, и мне не нужна какая-либо зависимость EF в моделях [Вот почему я избегаю аннотации данных].

Поэтому я объявляю настраиваемый атрибут (или позже может быть любым файлом конфигурации)

[AttributeUsage(AttributeTargets.Property, AllowMultiple=true )]
public class PersistableMemberAttribute : Attribute
{
    public bool Iskey;
    public bool IsRequired;
    public bool IsIgnored;
    public bool IsMany;
    public string HasForeignKey;
    public bool PropertyIsRequired;
    public bool PropertyIsOptional;
}

А вот одна из моих моделей выглядит так:

 public class Blog
{
    [PersistableMember(Iskey=true)]
    public Guid BlogId { get; set; }

    [PersistableMember(PropertyIsRequired = true)]
    public string Name { get; set; }

    public string Url { get; set; }

    [PersistableMember(IsIgnored=true)]        
    public int Rating { get; set; }

    [PersistableMember(IsMany =true)]
    public ICollection<Post> Posts { get; set; }
}

Теперь я собираюсь написать общий EntityTypeConfiguration, который будет динамически создавать конфигурацию во время выполнения на основе значений атрибутов:

 public class GenericEntityConfiguration<T> : EntityTypeConfiguration<T> where T : class
{
    public GenericEntityConfiguration()
    {
        var members = typeof(T).GetProperties();
        if (null != members)
        {
            foreach (var property in members)
            {
                var attrb= property.GetCustomAttributes(typeof( PersistableMemberAttribute ),false).OfType<PersistableMemberAttribute>();
                if (attrb != null && attrb.Count() > 0)
                {
                    foreach (var memberAttributute in attrb)
                    {
                        if (memberAttributute.Iskey || memberAttributute.IsIgnored)
                        {
                            var entityMethod = this.GetType().GetMethod("Setkey");
                            entityMethod.MakeGenericMethod(property.PropertyType)
                              .Invoke(this, new object[] { property, memberAttributute });
                        }

                        if (memberAttributute.IsRequired)
                        {
                            var entityMethod = this.GetType().GetMethod("SetRequired");
                            entityMethod.MakeGenericMethod(property.PropertyType)
                              .Invoke(this, new object[] { property, memberAttributute });
                        }

                        if (memberAttributute.PropertyIsRequired || memberAttributute.PropertyIsOptional)
                        {
                            var entityMethod = this.GetType().GetMethod("SetPropertyConfiguration");
                            entityMethod.MakeGenericMethod(property.PropertyType)
                              .Invoke(this, new object[] { property, memberAttributute });
                        }
                    }
                }
            }
        }

    }

    public void SetPropertyConfiguration<TResult>(PropertyInfo propertyInfo, PersistableMemberAttribute attribute)
    {
        var functorParam = Expression.Parameter(typeof(T));
        var lambda = Expression.Lambda(
            Expression.Property(functorParam, propertyInfo)
        , functorParam);

        if (attribute.PropertyIsRequired)
        {
            this.Property<TResult>((Expression<Func<T, TResult>>)lambda).IsRequired();
        }
        if (attribute.PropertyIsOptional)
        {
            this.Property<TResult>((Expression<Func<T, TResult>>)lambda).IsOptional();

        }
    }

    public void Setkey<TResult>(PropertyInfo propertyInfo, PersistableMemberAttribute attribute)
    {
        var functorParam = Expression.Parameter(typeof(T));
        var lambda = Expression.Lambda(
            Expression.Property(functorParam, propertyInfo)
        , functorParam);

        if (attribute.Iskey)
        {
            this.HasKey<TResult>((Expression<Func<T,TResult>>)lambda);
        }
        if (attribute.IsIgnored)
        {
            this.Ignore<TResult>((Expression<Func<T, TResult>>)lambda);
        }
    }

    public void SetRequired<TResult>(PropertyInfo propertyInfo, PersistableMemberAttribute attribute) where TResult : class
    {
        var functorParam = Expression.Parameter(typeof(T));
        var lambda = Expression.Lambda(
            Expression.Property(functorParam, propertyInfo)
        , functorParam);
        if (attribute.IsRequired)
        {
            this.HasRequired<TResult>((Expression<Func<T, TResult>>)lambda);
        }
    }

}

Но я получил ошибку компиляции

Ошибка 1 Тип «TResult» должен быть типом значения, не допускающим значение NULL, чтобы использовать его в качестве параметра «T» в универсальном типе или методе «System.Data.Entity.ModelConfiguration.Configuration.StructuralTypeConfiguration.Property(System.Linq. Expressions.Expression>)' D:\R&D\UpdateStorePOC\UpdateStorePOC\Data\GenericEntityConfiguration.cs 63 17 UpdateStorePOC

что для этих двух утверждений:

        this.Property<TResult>((Expression<Func<T, TResult>>)lambda).IsRequired();

        this.Property<TResult>((Expression<Func<T, TResult>>)lambda).IsOptional();

это означает, что мне нужно наложить ограничение на мой метод, чтобы ограничить его типом значения. В C# это делается с помощью ключевого слова «struct».

public void SetPropertyConfiguration<TResult>(PropertyInfo propertyInfo, PersistableMemberAttribute attribute) Where TResult : struct

Но это не решение, поскольку тип моего свойства может быть классом, например, string или int, bool double и т. д. Так что вообще не понятно, что я могу их отправить в этот метод. Пожалуйста, помогите мне решить эту проблему, есть ли другой способ сделать это.


person Morshed    schedule 16.11.2012    source источник
comment
Пожалуйста, попробуйте написать меньший пример. Прочтите это, чтобы понять, как сократить код: sscce.org   -  person durron597    schedule 17.11.2012
comment
В EF6 соглашения были обнародованы. Мне кажется, что это хороший кандидат на соглашение, а не на взлом с помощью Reflection. Кроме того, использование Reflection для вызова общедоступного API кажется неправильным (также неправильно использовать Reflection для вызова непубличного API). Чтобы быть более конкретным - обычно решения, основанные на отражении, кажутся отличными в начале, когда вы думаете, что можете делать все динамически, но часто это заканчивается тем, что его очень сложно поддерживать, и вам нужно использовать отражение везде, чтобы иметь возможность делать что-либо...   -  person Pawel    schedule 17.11.2012


Ответы (1)


Я не хочу зависимости от EF в моделях.

С беглым картографированием вы почти у цели и не подходите ближе. Ваши атрибуты, даже если они предназначены для перемещения в файл конфигурации, не освобождают вашу модель от каких-либо следов EF.1 Хуже того, они только добавляют второй слой сопоставления (если хотите). между вашей моделью и отображением EF. Я вижу только недостатки:

  • Вам по-прежнему нужно поддерживать метаданные для вашей модели, вероятно, не меньше, чем обычное беглое отображение и (вероятно) в неудобном вручную редактируемом XML без проверки во время компиляции.
  • Вы будете продолжать расширять свой код, чтобы охватить случаи, которые покрываются картографией EF, а ваши пока нет.2 Так что это пустая трата энергии: в конце вы фактически перепишете методы сопоставления EF.
  • Вам придется держать пальцы скрещенными, когда вы хотите обновить EF.
  • С ошибками/проблемами вы сами: трудно получить поддержку от сообщества.

Таким образом, мой ответ на ваш вопрос помогите мне решить эту проблему будет таким: использовать беглое сопоставление по умолчанию. Будь проще.


1 Например, вам все равно придется использовать модификатор virtual, чтобы включить прокси для отложенной загрузки.

2 Например, поддержка наследования, несопоставленные внешние ключи, максимальная длина, тип данных db... это может продолжаться некоторое время.

person Gert Arnold    schedule 18.11.2012