生成EntityTypeConfiguration的动态方式:类型'TResult'必须是非可空值类型

时间:2012-11-16 20:45:56

标签: entity-framework code-first fluent-interface

我正在考虑从运行时动态生成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'必须是非可空值类型才能在泛型类型或方法'System.Data.Entity.ModelConfiguration.Configuration.StructuralTypeConfiguration.Property(System)中将其用作参数'T' .Linq.Expressions.Expression&gt;)'D:\ R&amp; 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等。所以我完全不清楚我是否可以将它们发送到这种方法中。无论是否有其他办法,请帮助我解决这个问题。

1 个答案:

答案 0 :(得分:0)

  

我不希望模型中有任何EF依赖。

通过流畅的映射,你几乎就在那里,你不会再靠近了。您的属性即使打算移动到配置文件,也不会使您的模型不再具有任何EF足迹。 1 更糟糕的是,它们只添加了第二个映射层(如果您愿意)您的模型和EF的映射之间。我只看到了缺点:

  • 你仍然需要维护你的模型的元数据,可能不会比常规的流畅映射和(可能)在没有编译时检查的笨拙的手动编辑的XML中。
  • 你将继续扩展你的代码,以涵盖EF的映射所涵盖的案例,但你的代码还没有。 2 所以这是浪费精力:最后你基本上会重写EF的映射方法
  • 当您想要升级EF时,您必须保持手指交叉。
  • 有自己的错误/问题:很难得到社区的支持。

所以我对你的问题帮助我解决这个问题的答案将是:使用开箱即用的流畅映射。保持简单。


1 例如,您仍然必须使用virtual修饰符来启用延迟加载的代理。

2 支持继承,未映射的外键,最大长度,db数据类型......这可能会持续一段时间。

相关问题