Reputation: 30208
So I have a drop and recreate setup in my application's core project which is consumed by all of my apps in this solution. My model changes significantly enough to where I need this drop and recreate the database tables each time I recycle my app pool (app is in rapid development right now.) The problem is that if 2+ projects are kicked off at the same time, the drop and recreate steps on each other and many times there are missing tables.
I need a way to prevent, at runtime, the creation of the model based on a condition. I tried:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
if (condition)
return;
//...
base.OnModelCreating(modelBuilder);
}
but this is not working... the tables are still being created. How can I do this?
Upvotes: 0
Views: 118
Reputation: 776
Not quite sure about your scenario.
For the development purposes, you can use different schema for your tables.
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.HasDefaultSchema("applicationX");
}
If this is not possible, you can suffix/prefix table names for your entities.
If you need shared tables (data) by both application, you can compose all the database in any application sharing the same model building code.
Then, of course, you must somehow create "critical section" for your database recreation code (https://github.com/aspnet/EntityFramework/issues/3042), using Mutex or/and combined it e.g. with your own lock synchronization object (table) in your database.
If you need to construct a model (database) dynamically, e.g., use the dependancy injection to build model dynamically at single DB context:
/// <summary>
/// Formalization of entity framework DbContext model creation <see cref="DbContext.OnModelCreating"/>
/// </summary>
/// <remarks>
/// The reason for this explicit interface of the <see cref="DbContext.OnModelCreating"/> is to make compositions of several model builders into one
/// </remarks>
public interface IEntityFrameworkModelBuilder
{
void BuildModel(ModelBuilder modelBuilder);
}
public abstract class DbContextBase : DbContext
{
protected DbContextBase(DbContextOptions options, IEntityFrameworkModelBuilder modelBuilder) : base(options)
{
_modelBuilder = modelBuilder;
}
public virtual IEntityFrameworkModelBuilder ModelBuilder
{
get { return _modelBuilder ?? (_modelBuilder = new DefaultSchemaEntityFrameworkModelBuilder("dbo")); }
protected set { _modelBuilder = value; }
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
ModelBuilder.BuildModel(modelBuilder);
}
}
Of course, then you can create e.g. composit pattern class for your IEntityFrameworkModelBuilder:
/// <summary>
/// The composition of <see cref="IEntityFrameworkModelBuilder"/> used to construct <see cref="DbContext"/> model creation from several modules (tests).
/// </summary>
/// <seealso cref="DbContextOnModelCreatingAdaptingModelBuilder"/>
public class EntityFrameworkModelBuilderComposition : IEntityFrameworkModelBuilder, IList<IEntityFrameworkModelBuilder>
{
private readonly IList<IEntityFrameworkModelBuilder> _builders;
public EntityFrameworkModelBuilderComposition() : this(new IEntityFrameworkModelBuilder[0])
{}
public EntityFrameworkModelBuilderComposition(params IEntityFrameworkModelBuilder[] builders)
{
_builders = new List<IEntityFrameworkModelBuilder>(builders);
}
/// <summary>
/// Constructor to take list of builders as underlaying list so it is initialized with these builders and sharing the same reference.
/// </summary>
/// <param name="builders"></param>
public EntityFrameworkModelBuilderComposition(IList<IEntityFrameworkModelBuilder> builders)
{
_builders = builders ?? new List<IEntityFrameworkModelBuilder>();
}
public virtual void BuildModel(ModelBuilder modelBuilder)
{
foreach (var b in _builders)
{
b.BuildModel(modelBuilder);
}
}
}
Just adding, what you could find helpful sooner or later:
/// <summary>
/// The <see cref="IEntityFrameworkModelBuilder"/> implementation of a building model on the base of <see cref="DbContext"/> instance calling its <see cref="DbContext.OnModelCreating"/> protected method.
/// </summary>
/// <seealso cref="DbContextOnModelCreatingAdaptingModelBuilder"/>
/// <seealso cref="EntityFrameworkModelBuilderComposition"/>
public class DbContextOnModelCreatingAdaptingModelBuilder : IEntityFrameworkModelBuilder
{
private readonly Func<DbContext> _dbContextFactoryMethod;
public DbContextOnModelCreatingAdaptingModelBuilder(Func<DbContext> dbContextFactoryMethod)
{
_dbContextFactoryMethod = dbContextFactoryMethod;
}
public void BuildModel(ModelBuilder modelBuilder)
{
using (var dbContext = _dbContextFactoryMethod())
{
MethodInfo onModelCreating = dbContext.GetType().GetMethod("OnModelCreating", BindingFlags.NonPublic | BindingFlags.Instance);
onModelCreating.Invoke(dbContext, new object[] {modelBuilder});
}
}
}
Of course, all the mentioned points can/must be combined to get the desired behaviour. But it's not clear, how you evaluate the model differences, what
My model changes significantly enough to where I need this drop and recreate
means exactly regarding
the drop and recreate steps on each other and many times there are missing tables
Upvotes: 1
Reputation: 30208
Sadly, according to Microsoft, there does not seem to be a way to do it: https://github.com/aspnet/EntityFramework/issues/6346
Upvotes: 0