0

I was thinking to generate EntityTypeConfiguration dynamically from run time and i don't want any EF dependency in Models[That is why i avoid Data Annotation].

So I declare a custom attribute(or can be any configuration file later on)

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

And here is one of my Models is look like:

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

Now I am going to write a generic EntityTypeConfiguration , which will create the configuration dynamically on run time based on the attribute values :

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

}

But i got the compilation error of

Error 1 The type 'TResult' must be a non-nullable value type in order to use it as parameter 'T' in the generic type or method 'System.Data.Entity.ModelConfiguration.Configuration.StructuralTypeConfiguration.Property(System.Linq.Expressions.Expression>)' D:\R&D\UpdateStorePOC\UpdateStorePOC\Data\GenericEntityConfiguration.cs 63 17 UpdateStorePOC

which for these two statements:

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

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

that means that I need to put a constraint on my method to restrict it to a value type. In C#, this is done with the ‘struct’ keyword.

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

But Its not the solution since my property type can be a class e.g string or int, bool double, etc . So it is not at all clear that I can send them into this method. Please help me to solve this issue whether there is any other way to do it.

Morshed
  • 1
  • 3
  • Please try to write a smaller example. Read this to get an idea of how to reduce your code: http://sscce.org/ – durron597 Nov 16 '12 at 21:07
  • 1
    In EF6 conventions were made public. It seems for me to be a good candidate for a convention rather then hacking with Reflection. Also, using Reflection to call public API feels wrong (it also feels wrong to use Reflection to call non-public API). To be more specific - usually reflection based solutions seem great at the beginning when you think you can do everything dynamically but often times it ends being very hard to maintain and you have to use reflection everywhere to be able to do anything... – Pawel Nov 17 '12 at 00:35

1 Answers1

0

I don't want any EF dependency in models.

With fluent mapping you're almost there and you won't come any closer. Your attributes, even though intended to be moved to a configuration file, don't make your model any more free of any EF footprint.1 Worse, they only add a second mapping layer (if you like) between your model and EF's mapping. I only see drawbacks:

  • You still have to maintain meta data for your model, probably not any less than regular fluent mapping and (probably) in awkward manually edited XML without compile-time checking.
  • You will keep expanding your code to cover cases that EF's mapping covers but yours doesn't yet.2 So it's a waste of energy: in the end you'll basically have rewritten EF's mapping methods.
  • You'll have to keep your fingers crossed when you want to upgrade EF.
  • With bugs/problems you're on your own: hard to get support from the community.

So my answer to your question help me to solve this issue would be: use fluent mapping out of the box. Keep it simple.


1 For example, you would still have to use the virtual modifier to enable proxies for lazy loading.

2 Like support for inheritance, unmapped foreign keys, max length, db data type, ... this could go on for a while.

Gert Arnold
  • 105,341
  • 31
  • 202
  • 291