Entity Framework 4.1 – Change mappings at runtime

In v1 and v4 of EF I’ve been using this little gem, with a small tweak, to change the mappings of the model at runtime.

I was wondering how I could achieve something similar in version 4.1 code first. By design the OnModelCreating method is used to change the mappings of your model but it is only fired once per AppDomain since building it up does incur some performance penalty and normally your database schema does not change magically. In an application I’ve build though every customer gets his own tables, schemas etc. so hooking up some configuration logic in the OnModelCreating was out of the question since it’s only executed once.

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    //not possible, only executed once
    var tableName = _schemaService.LookupTheTableName(loggedInCustomer);
    modelBuilder.Entity<Customer>().ToTable(tableName);
    base.OnModelCreating(modelBuilder);
}

Useless approach since the model isn’t being build again.

MvcApplication1Context myContext = new MvcApplication1Context(customer1, schemaService);
var csts = context.Customers.ToList();
 
//will go to the same tables as before, because OnModelCreating is only executed once
myContext = new MvcApplication1Context(customer2, schemaService);
var otherCsts = context.Customers.ToList();

There is a constructor though on the DbContext class that takes a compiled model and using that one does allow you to tweak the mappings you are using in your application. This also allows you to work with models that have been generated with a designer from 4.0 to use with the DbContext API.

//build config from scratch
DbModelBuilder builder = new DbModelBuilder(DbModelBuilderVersion.V4_1);
builder.Configurations.Add(new EntityTypeConfiguration<Customer>());
 
//lets change something for each customer
var schemaName = schemaService.LookupTheTableName(loggedInCustomer);
builder.Entity<Customer>().ToTable(schemaName);
 
//use the constructor that takes a compiled model
context = new MvcApplication1Context(builder.Build(context.Database.Connection).Compile());

You could then cache the compiled model for each customer so you don’t need to take that hit every time. Works like a charm. I’ll try to make it a bit easier to use.

22 Replies to “Entity Framework 4.1 – Change mappings at runtime”

    1. Certainly, just use the same approach. You’ll need some way of storing your configuration though.

  1. Hi, i have been googling and found your article. Would you please take a look at this question [https://blogs.msdn.com/b/adonet/archive/2010/12/14/ef-feature-ctp5-fluent-api-samples.aspx] and say whether your solution can help me? Thanks.

  2. Wonderful post Benny! I was wondering if there is any way to discover run time schema (say user defined columns in existing table) and then map them to a lsit of values (or something similar) in relevant type.
    Consider following example
    public partial class Customer
    {
    [Key]
    public int Id {get; set;}
    public string Name {get; set;}
    }
    This type maps to an sql table Customer with two columns Id and Name. Now if the system allows user to add custom columns say (U_User1, U_User2…) to the table Customer. How this can be accomodated in EF 4.1? I believe that Customer class needs one more list that can hold these user defined values. But how this can be mapped at run time?

  3. Hello,

    I am trying to change mappings dinamically on my edmx by using DBModelBuilder, as suggested but I am getting following error:

    The type ‘MyEntityType’ was not mapped. Check that the type has not been explicitly excluded by using the Ignore method or NotMappedAttribute data annotation. Verify that the type was defined as a class, is not primitive, nested or generic, and does not inherit from EntityObject.

    I thought that for EntityObjects it will be possible, but error tells me that it is not. Why?

    I want to change mapping dinamically because I have the same sets of tables in database which are differed only for a suffix. I would like to have one model which I can switch between sets. I included in my project and I work with solution from codeplex: http://efmodeladapter.codeplex.com/

    What is solution for that?

    Vedran

    1. Are you mixing Code First with Model or DB first models? That’s not possible. Move them to other assemblies and try again.

  4. I am new to the MVC, i just wan to ask
    What is schemaService in the code given by you?
    Pls reply

    Thank you..

    1. That’s just mock code. It would be application specific. In the sample shown in the post, it would look up the name of the table in which to store the data for a specific user. I.e. for user1 the name could be User1Customers and for user2 User2Customers,

  5. hm, so in your last code fragment it seems that you’re using another context(builder.Build(context.Database.Connection).Compile()) which means that you need to create two of them each time?

  6. Can you show how is your Context class? like..

    public class MyContext : DbContext
    {
    public MyContext() : base (“stringConection”)
    {

    }

    Tks

    public DbSet name{ get; set; }
    }

  7. Very good article.. But I’m getting error with this implementation. Please help me to solve this issue. This is my code.. and the error is cannot attach the file “***” as database “***”

    public void Insert1()
    {
    using (context = new VBankSqlDbContext())
    {
    DbModelBuilder builder = new DbModelBuilder(DbModelBuilderVersion.Latest);
    builder.Configurations.Add(new EntityTypeConfiguration());

    builder.Entity().ToTable(“Test1”);

    //use the constructor that takes a compiled model
    context = new VBankSqlDbContext(builder.Build(context.Database.Connection).Compile());

    context.TempData.Add(new Temp1() { Id = 1, Name = “test1” });
    context.SaveChanges();
    }
    }

    1. Pretty sure it’s not related. You are probably attaching a database which is in your App_Data folder. Check your connectionstring or ask on Stack Overflow, it’s not really related to this post.

Leave a Reply

Your email address will not be published. Required fields are marked *