Emit DbContext.OnModelCreating on every context cr

2019-02-19 04:35发布

问题:

I use entity framework code first to work with my database. I have several tables with different names but same structure, and this tables dynamically appears in database. How could I map EntityFramework to one of that tables at run-time and use data from just like I work this over entities of DbContext?

What I've done to make it work:

For example, my class what describes structure of dynamically created table is SetElement.

Here is my context:

public class DataContext : DbContext
{
    public DataContext()
        : base("RepositoryConnectionString") { }

    string setElementsTableId; // the name of table that need to be dynamicly mapped to 

    // Enforce model recreating
    public DataContext(string setElementsTableId)
        : this()
    {
        this.setElementsTableId = setElementsTableId;
    }


    /* some other entities */

    public DbSet<Entities.SetElement> SetElements { get; set; } // dynamicly mapped entity


    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        /* come configurations */

        if (!string.IsNullOrEmpty(setElementsTableId))
        {
            modelBuilder.Entity<Entities.SetElement>().Map(x => x.ToTable(setElementsTableId)); // map SetElements property to dynamicly created table
        }
    }
}

How I use this:

public static void AddSetElements(ICollection<SetElement> setElements, string tableId)
    {
     using (ctx = new DataContext(tableId)) // configere DataContext to map tableId table for entity SetElements
       try
       {
           var num = ctx.SetElements.Count();
           ctx.SetElements.AddRange(setElements);
           ctx.SaveChanges();
       }
       catch (Exception e)
       {
       }
 }

I have also some methods to get, udtate and remove data from dynamicly created tables that are same to AddSetElements.

All works just as I wish but only if AddSetElements runs first, because at the first datacontext creating DbContext.OnModelCreating runs and configure all mappings. But next instance creation doesn't call DbContext.OnModelCreating.

So, my question is: how to call DbContext.OnModelCreating everytime of creating an instance of DataContext then I use DataContext(string setElementsTableId) to create it?

I know, my question is similar to 'dynamic table mapping in EF' but I found nothing in the results.

By the way. If you know another way to solve my problem, you are welcome.

回答1:

There is a built-in feature which may address your issue : `IDbModelCacheKey ; the implementation of which is to be registered in your configuration. The point is to generate a different key for your different contexts.

I would go for something like :

First, the configuration

public class EntityFrameworkConfiguration: DbConfiguration
{
    public EntityFrameworkConfiguration()
    {
        this.SetModelCacheKey(ctx => new EntityModelCacheKey((ctx.GetType().FullName + ctx.Database.Connection.ConnectionString).GetHashCode()));
    }
}

Then the implementation of the IDbModelCacheKey

public class EntityModelCacheKey : IDbModelCacheKey
{
    private readonly int _hashCode;

    public EntityModelCacheKey(int hashCode)
    {
        _hashCode = hashCode;
    }

    public override bool Equals(object other)
    {
        if (other == null) return false;
        return other.GetHashCode() == _hashCode;
    }

    public override int GetHashCode()
    {
        return _hashCode;
    }
}

Finally, your DataContext

public class DataContext : DbContext
{

  string setElementsTableId; 

  // use the setElementsTableId as extended property of the 
  // connection string to generate a custom key
  public DataContext(string setElementsTableId)
        : base(ConfigurationManager.ConnectionStrings["RepositoryConnectionString"] 
 + "; Extended Properties=\"setElementsTableId=" + setElementsTableId + "\"")
  {
    this.setElementsTableId = setElementsTableId;
  }

  public DbSet<Entities.SetElement> SetElements { get; set; } 

  protected override void OnModelCreating(DbModelBuilder modelBuilder)
  {
    if (!string.IsNullOrEmpty(setElementsTableId))
    {
        modelBuilder.Entity<Entities.SetElement>().Map(x => x.ToTable(setElementsTableId)); 
    }
  }
}

I hope this will be of some help



回答2:

Look like nobody knows answer...

Otherwise, one man told me that my question is meaningless because of storage data in several tables will not give any achievement. More better to add indexes to database, partitioning table or something else. In other words this is Database Management System problem. But if some one knows answer I'll be very pleasured to hear something about EF hack.