Dynamically Building a Model with Code First

Posted on March 26, 2012. Filed under: Entity Framework, Visual Studio | Tags: , , , , |

I’ve answered a few emails recently on this topic so it felt like time to turn it into a blog post.

In this scenario the set of classes that make up your model isn’t known at compile time and is discovered dynamically at runtime. An example of such a scenario is a WordPress/Orchard/etc. style website that allows modules to be plugged in. These modules live in a separate assembly and may contain classes that represent data to be persisted in the application database. These classes are all pulled into a central Code First model to be used for data access.

 

Finding the Classes

There are lots of different approaches for identifying the classes to be included in the model. For this example lets use a [Persistent] attribute, something like this:

[AttributeUsage(AttributeTargets.Class)]
public class PersistentAttribute : Attribute
{
}

Now we can add some logic to the OnModelCreating method of our context to scan assemblies and add any classes with the [Persist] attribute. We’re going to assume that the assemblies that may contain classes are all loaded into the current AppDomain, of course you may have some other mechanism that provides a list of the assemblies to check.

public class MyContext : DbContext
{
  protected override void OnModelCreating(DbModelBuilder modelBuilder)
  {
    var entityMethod = typeof(DbModelBuilder).GetMethod("Entity");

    foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
    {
      var entityTypes = assembly
        .GetTypes()
        .Where(t =>
          t.GetCustomAttributes(typeof(PersistentAttribute), inherit: true)
          .Any());

      foreach (var type in entityTypes)
      {
        entityMethod.MakeGenericMethod(type)
          .Invoke(modelBuilder, new object[] { });
      }
    }
  }
}

In this example the entire model is dynamically discovered, but you could also have some static parts of the model that are registered with modelBuilder too.

 

An Alternative Using EntityConfiguration<TEntity>

Code First allows you to create a class that derives from EntityTypeConfiguration<TEntity> to encapsulate the Fluent API configuration for an entity. If your using this approach to configuration you can just look for these configuration classes and register them, instead of finding the entities to register. Notice that we are filtering out the EntityFramework assembly since it has some configuration classes that it uses internally.

public class MyContext : DbContext
{
  protected override void OnModelCreating(DbModelBuilder modelBuilder)
  {
    var addMethod = typeof(ConfigurationRegistrar)
      .GetMethods()
      .Single(m => 
        m.Name == "Add" 
        && m.GetGenericArguments().Any(a => a.Name == "TEntityType"));

    foreach (var assembly in AppDomain.CurrentDomain
      .GetAssemblies()
      .Where(a => a.GetName().Name != "EntityFramework"))
    {
      var configTypes = assembly
        .GetTypes()
        .Where(t => t.BaseType != null
          && t.BaseType.IsGenericType
          && t.BaseType.GetGenericTypeDefinition() == typeof(EntityTypeConfiguration<>));

      foreach (var type in configTypes)
      {
        var entityType = type.BaseType.GetGenericArguments().Single();

        var entityConfig = assembly.CreateInstance(type.FullName);
        addMethod.MakeGenericMethod(entityType)
          .Invoke(modelBuilder.Configurations, new object[] { entityConfig });
      }
    }
  }
}

 

What if the Model Changes?

Code First Migrations to the rescue… You may not be able to use Migrations from the Package Manager Console because the logic to discover your model may require you full application to be running. Fortunately those commands are just thin wrappers over an API. Here is some code to automatically change the database when new classes or properties are added to the model. My recent blog post has more details on invoking Migrations from code.

var config = new DbMigrationsConfiguration<MyContext> { AutomaticMigrationsEnabled = true };
var migrator = new DbMigrator(config);
migrator.Update();
About these ads

Make a Comment

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

9 Responses to “Dynamically Building a Model with Code First”

RSS Feed for RoMiller.com Comments RSS Feed

I have a system that needs custom fields per client. I have two approaches:
* XML columns to serialize client extension objects
* Client specific add-on tables using above dynamic model

What does the DbContext look like if it is dynamic? Do you access the dynamic entities using the DbContext.Entities?

Which approach do you suggest?

i have a similar approuch with my application. a core module and any pluggable modules with entities and entity-mappings. but my core module doesnt know anything about the comming pluggable modules. how can i uses migration in this scenario without using AutomaticMigrationEnabled = true ? the nuget-console doesnt work in this case and i want to have migrations per module. thank you

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 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 : EntityTypeConfiguration 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();
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(PropertyInfo propertyInfo, PersistableMemberAttribute attribute)
{
var functorParam = Expression.Parameter(typeof(T));
var lambda = Expression.Lambda(
Expression.Property(functorParam, propertyInfo)
, functorParam);

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

}
}

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

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

public void SetRequired(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((Expression<Func>)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<System.Func>)’ D:\R&D\UpdateStorePOC\UpdateStorePOC\Data\GenericEntityConfiguration.cs 63 17 UpdateStorePOC

which for :

if (attribute.PropertyIsRequired)
{
this.Property((Expression<Func>)lambda).IsRequired();
}
if (attribute.PropertyIsOptional)
{
this.Property((Expression<Func>)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(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. I already posted this in MSDN forum weeks ago , still not getting any response fron anyone: http://social.msdn.microsoft.com/Forums/en-US/adodotnetentityframework/thread/cd9df9d1-2b47-4676-9070-58cbb6ae0083

Another Question I have : I would like to create a Dynamic Code-Based DBMigration ………which will create Table and extended properties on the fly using Reflection (By loading some external assembly of Upgrade version)………..I can compare them and find out the changes of properties and added persitable classes………….But CreateTable Method in DbMigration is asking me for expression ..

protected internal TableBuilder CreateTable(
string name,
Func columnsAction,
Object anonymousArguments
)

……any example to create such Table-Expression on the fly .

Hi, I’m using the EntityConfiguration approach as above together with MEF. I have a core application and modules that export the entities which are discovered and loaded at runtime. Exporting the model classes from the core application works with no problems but exporting them from the respective modules causes the following exception on the “addMethod.MakeGeneric…….” part:

Object of type ‘Forums.Data.Model.ForumConfig’ cannot be converted to type ‘System.Data.Entity.ModelConfiguration.EntityTypeConfiguration`1[Forums.Data.Model.Forum]‘.

In this scenarion the Entity is “Forums.Data.Model.Forum” and the configuration class is “Forums.Data.Model.ForumConfig’ : EntityTypeConfiguration”

Will you be able to provide some insight into what’s going on and how to go about to make it work as intended?

Hi,

After model building and db migrations how can I preform actions on entity (read, write, delete, update).
If I adding class Product

public class Product
{
public int Id { get; set; }
public string Name { get; set; }
}

And trying to read/write/delete/update it I can’t.

using(MyContext _db = new MyContext())
{
_db.Product ???
}

Thanks,
Andrey

Get it work:

List l = _db.Set().ToList();

Hi Andrey,
Yes, that’s the correct approach, glad you found it.
~Rowan

Hi,

Great article. Thanks for valuable information. I have a question regarding DbSet.Cast(TDerivedType). I am using MEF to load my dependencies and everything works fine until I need to call DbSet.Cast() to convert my dynamic DbSet to a generic one. The type is unknown in compile time but it is loaded using MEF and by the time I execute DbSet.Cast() i can provide the exact implementation. However I get the following error message:
Cannot create a DbSet from a non-generic DbSet for objects of type ‘User’

Any ideas?


Where's The Comment Form?

    About

    Rowan works as a Program Manager for the ADO.NET Entity Framework team at Microsoft. He speaks at technical conferences and blogs at romiller.com. Rowan lives in Seattle, Washington with his wife Athalie. Prior to moving to the US he resided in the small state of Tasmania in Australia. Outside of technology Rowan's passions include snowboarding, mountain biking, horse riding, rock climbing and pretty much anything else that involves being active. The primary focus of his life, however, is to follow Jesus.

    RSS

    Subscribe Via RSS

    • Subscribe with Bloglines
    • Add your feed to Newsburst from CNET News.com
    • Subscribe in Google Reader
    • Add to My Yahoo!
    • Subscribe in NewsGator Online
    • The latest comments to all posts in RSS

    Meta

Liked it here?
Why not try sites on the blogroll...

Follow

Get every new post delivered to your Inbox.

Join 146 other followers

%d bloggers like this: