I'm trying to use EF 4.3 migrations with multiple code-first DbContexts. My application is separated into several plugins, which possibly have their own DbContext regarding their domain. The application should use one single sql-database.
When I try to auto migrate the contexts in an empty database, this is only successful for the first context. Every other context needs the AutomaticMigrationDataLossAllowed-Property set to true but then tries to drop the tables of the previous one.
So my question is:
How can I tell the migration-configuration just to look after the tables defined in their corresponding context and leave all others alone?
What is the right workflow to deal with multiple DbContexts with auto-migration in a single database?
Thank you!
Here is what you can do. very simple.
You can create Configration Class for each of your context.
e.g
internal sealed class Configuration1 : DbMigrationsConfiguration<Context1>{
public Configuration1 (){
AutomaticMigrationsEnabled = false;
MigrationsNamespace = "YourProject.Models.ContextNamespace1";
}
}
internal sealed class Configuration2 : DbMigrationsConfiguration<Context2>{
public Configuration2 (){
AutomaticMigrationsEnabled = false;
MigrationsNamespace = "YourProject.Models.ContextNamespace2";
}
}
Now you add migration. You dont need to enable migration since you already did with the 2 classed above.
Add-Migration -configuration Configuration1 Context1Init
This will create migration script for context1. your can repeat this again for other Contexts.
Add-Migration -configuration Configuration2 Context2Init
To Update your database
Update-Database -configuration Configuration1
Update-Database -configuration Configuration2
This can be done in any order. Except you need to make sure each configration is called in sequence.
Code First Migrations assumes that there is only one migrations configuration per database (and one context per configuration).
I can think of two possible solutions:
Create an aggregate context that includes all the entities of each context and reference this "super" context from your migrations configuration class. This way all the tables will be created in the user's database, but data will only be in the ones that they've installed plugins for.
Use separate databases for each context. If you have shared entities between the contexts, add a custom migration and replace the CreateTable(...) call with a Sql("CREATE VIEW ...") call to get the data from the entity's "originating" database.
I would try #1 since it keeps everything in a single database. You could create a seperate project in your solution to contain your migrations and this "super" context. Just add the project, reference all of your plugins' projects, create a context that includes all of the entities, then call Enable-Migrations on this new project. Things should work as expected after that.
I have a working site with multiple contexts using migrations. However, you do need to use a separate database per context, and it's all driven off of a *Configuration class in the Migrations namespace of your project, so for example CompanyDbContext points to Company.sdf using CompanyConfiguration. update-database -configurationtypename CompanyConfiguration. Another LogDbContext points to Log.sdf using LogConfiguration, etc.
Given this works, have you tried creating 2 contexts pointing at the same database and telling the modelbuilder to ignore the other context's list of tables?
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Ignore<OtherContextsClass>();
// more of these
}
Since the migrations work with the ModelBuilder, this might do the job.
The crappy alternative is to avoid using Automatic Migrations, generate a migration each time and then manually sift through and remove unwanted statements, then run them, although there's nothing stopping you from creating a simple tool that looks at the Contexts and generated statements and does the migration fixups for you.
Ok, I have been struggling with this for a day now, and here is solution for those seeking the answer...
I am assuming that most people reading this post are here because they have a large DbContext class with a lot of DbSet<> properties and it takes a long time to load. You probably thought to yourself, gee, that makes sense, I should split up the context, since I won't be using all of the dbsets at once, and I will only load a "Partial" context based on the situation where I need it. So you split them up, only to find out that Code First migrations don't support your way of revolutionary thinking.
So your first step must have been splitting up the contexts, then you added the MigrationConfiguration class for each of the new contexts, you added the connection strings named exactly the same as your new Context classes.
Then you tried running the newly split up contexts one by one, by doing Add-Migration Context1 then doing Update-Database -Verbose...
Everything seemed to work fine, but then you notice that every subsequent Migration deleted all tables from the Previous migration, and only left the tables in from the very last migration.
This is because, the current Migrations model expects Single DbContext per Database, and it has to be a mirror match.
What I also tried, and someone suggested here doing that, is create a single SuperContext, which has All the Db sets in it. Create a single Migration Configuration class and run that in. Leave your partial Context classes in place, and try to Instantiate and use them. The EF complains that the Backing model has changed. Again, this is because the EF compares your partial dbcontext to the All-Sets context signature that was left over from your Super Context migration.
This is a major flaw in my opinion.
In my case, I decided that PERFORMANCE is more important than migrations. So, what I ended up doing, is after I ran in the Super context and had all the tables in place, I went into the database and Manually deleted _MigrationHistory table.
Now, I can instantiate and use my Partial Contexts without EF complaining about it. It doesn't find the MigrationHistory table and just moves on, allowing me to have a "Partial" view of the database.
The trade off of course is that any changes to the model will have to be manually propagated to the database, so be careful.
It worked for me though.
As mentioned above by Brice, the most practical solution is to have 1 super DbContext per application/database.
Having to use only 1 DbContext for an entire application seems to be a crucial technical and methodological disadvantage, cause it affects Modularity among other things. Also, if you are using WCF Data Services, you can only use 1 DataService per application since a DataService can map to only 1 DbContext. So this alters the architecture considerably.
On the plus side, a minor advantage is that all database-related migration code is centralized.
I just came across this problem and realised the reason I had split them into different contexts was purely to have grouping of related models in manageable chunks and not for any other technical reason. Instead I have declared my context as a partial class and now different code files with different models in them can add DbSets to the DbContext.
This way the automigration magic still works.
I've got it working with manual migrations, but you can't downgrade as it can't discrimitate between configurations in the __MigrationHistory table. If I try and downgrade then it treats the migrations from the other configurations as automatic and since I don't allow data loss it fails. We will only ever be using it to upgrade though so it works for our purposes.
It does seem like quite a bit ommision though, I'm sure it wouldn't be hard to support it provided there was no overlap between DbContexts.
Surely the solution should be a modification by the EntityFramework team to change the API to support the direct modification of the _MigrationHistory table to a table name of your choice like _MigrationHistory_Context1 such that it can handle the modification of independent DbContext entities. That way they're all treated separately, and its up to the developer to ensure that the names of entities don't collide.
Seems like there are a lot of people who share my opinion that a duplicate DbContext with references to the superset of entities is a bogus non-enterprise friendly way to go about things. Duplicate DbContexts fail miserably for modular (Prism or similar) based solutions.
I want people to know that the answer with this below is what worked for me but with one caveat: don't use the MigrationsNamespace line.
internal sealed class Configuration1 : DbMigrationsConfiguration<Context1>{
public Configuration1 (){
AutomaticMigrationsEnabled = false;
MigrationsNamespace = "YourProject.Models.ContextNamespace1";
}
}
internal sealed class Configuration2 : DbMigrationsConfiguration<Context2>{
public Configuration2 (){
AutomaticMigrationsEnabled = false;
MigrationsNamespace = "YourProject.Models.ContextNamespace2";
}
}
However, I already had the 2 databases established with their own contexts defined so I found myself getting an error saying "YourProject.Models namespace already has ContextNamespace1 defined". This was because the "MigrationsNamespace = "YourProject.Models.ContextNamespace2";" was causing the dbcontext to be defined under the YourProjects.Models namespace twice after I tried the Init (once in the migration Context1Init file and once where I had it defined before).
So, I found that what I had to do at that point was start my database and migrations from scratch (thankfully I did not have data I needed to keep) via following the directions here:
http://pawel.sawicz.eu/entity-framework-reseting-migrations/
Then I changed the code to NOT include the MigrationsNamespace line.
internal sealed class Configuration1 : DbMigrationsConfiguration<Context1>{
public Configuration1 (){
AutomaticMigrationsEnabled = false;
}
}
internal sealed class Configuration2 : DbMigrationsConfiguration<Context2>{
public Configuration2 (){
AutomaticMigrationsEnabled = false;
}
}
Then I ran the Add-Migration -configuration Configuration1 Context1Init command again and the Update-Database -configuration Configuration1 line again (for my 2nd context too), and finally, everything seems to be working great now.
Related
I want to implement repository pattern in my ASP.NET MVC project. I've seen a lot of implementations when searching google and I'm a little bit confused. Most of them created their own Context class which inherited from DBContext class and then injected it in a repository (or repositories) constructor.
None of the articles I found explain why the custom DBContext class is created and why they just don't use default ApplicationDbContext and HOW to change your app code to adapt to the new Context class. They don't even mention the class.
I'd rather use default ApplicationDbContext which I would inject in repository classes, because at the moment I don't see a point in creating a new DBContext class. Am I missing something? Is it a bad practice?
If, for some reason, I will have to add new DbContext class, how to edit my code (web.config and others) to adapt it? If I already made migrations to database, would change of DBContext class affect the database? Would it be necessary to do migrations once again?
To give you an example look at the MSDN documentation:
https://learn.microsoft.com/en-us/aspnet/mvc/overview/older-versions/getting-started-with-ef-5-using-mvc-4/implementing-the-repository-and-unit-of-work-patterns-in-an-asp-net-mvc-application
I just have no idea why the author uses SchoolContext instead of ApplicationDbContext.
it's a broad question, a guess that's the reason for the down votes.
You do seem confused. First off, I'm not an expert (see points). I did get a little bit in the weeds with the default ApplicationDbContext. Maybe you use a template where it pops up? Anyway, I launched the MVC-template and tehre's no default context class provided.
In short you create your own Context-class. You give it any name you want, given that in ends with Context AND IT INHERITS from DbContext. If not the Entity framework will not recognise it, and will not know what to do with it when building the database an so on. See your article:
The main class that coordinates Entity Framework functionality for a given data model is the database context class. You create this class by deriving from the System.Data.Entity.DbContext class. In your code you specify which entities are included in the data model. You can also customize certain Entity Framework behavior. In this project, the class is named SchoolContext.
Blockquote
By default you have only one Context-class.
It is self evident you need to create your own context-class, since you can't access the DbContext-class. (And even if you could, you should not want to do that, you need to separate the abstract and the implemented.)
I don't understand §2. ApplicationDbContext is just a way to write MySelfNamedContextClass.
§3: I don't know if working with two contect-classes is possible/feasible, but the simple implication is that you would be working with two separate databases. That's already pretty complicated, and what you're wrtiting about is not heading in that direction.
If I already made migrations to database, would change of DBContext
class affect the database? Would it be necessary to do migrations once
again?
Yes, absolutely it would. Your adding a table or a field or a constraint or a relationship to it ... .
You can do two things depending on the changes you bring:
(i) add a supplemental migration
(ii) You would have to rewind your migrations (remove-migration). It could be that you need to reset your db to your first migration (update-database 'here name of first migration without the quotation marks'. If you get stuck with that, and you're still on local db, just remove db by deleting it outright (maybe after closing solution) and rewind and rebuild the migrations and rebuild the database.
Especially for scenario (ii), if you have already data in the database (that isn't seeded into it), you would need to take measures to store it somewhere !!!
I hope this somehow answers your questions.
Kind regards.
The goal
My goal is to create a hierarchy of DbContexts each one residing in different assembly. Whatever references are required are established correctly so that the C# code compiles successfully.
The scenario
Let's say I create a db context, e.g. DbChild, inheriting from a base one, say DbBase.
The base context has some entities defined in OnModelCreating(). A migration for these entities is created and successfully applied to the database, thus the db schema contains the DbBase model mapped.
Needless to say DbChild uses the very same connection string and therefore the same database (I tried a number of ways to supply the connection string, the last one specifying it directly in optionsBuilder.UseSqlServer("<conection string>");).
Actual result
Creating a migration for the child context, however, includes the base model as part of the child one, which results in duplicate SQL objects in the DB.
Required result
A "clean" migration including only SQL objects (EF entities) from the child context is required.
Any ideas how to achieve this?
Thanks in advance.
PS: calling Ignore(...) in OnModelCreating() might be a workaround but it needs everything referenced in DbBase to be referenced in DbChild which is not an option.
PS2: Totally ignoring the base model while create the child one is not an option too - child uses an entity from base as a relationship.
EDIT: The snapshot <ContextName>ModelSnapshot.cs contains a "copy" of the model which gets updated with each migration. This is where every migration starts up. In complex models, however, it would be much easier, and what is much more important - safe, to have the snapshot file generated programmatically out of the existing database instead of copying, changing namespaces, renaming so as to have the context name reflected etc.
So, the questions may be transformed to "How to generate database snapshot when applying the first migration?".
Any ideas are welcome.
When creating a new derived context, after setting up the DB connection string but before adding any derived types to the context, you should create a migration that saves the snapshot of the pre-existing base context types.
In EF6, this would be done by creating a migration with Add-Migration PreExisting –IgnoreChanges. This produces a migration, where the internal model contains an updated snapshot, but the Up and Down methods of the migration are empty.
I'm not up to date on whether EF Core does support the -IgnoreChanges switch currently. According to What is the equivalent of the -IgnoreChanges switch for entity-framework core in CLI?, an alternative is, to manually clear the Up/Down methods after creating a migration.
Only after the first snapshot migration is created, start with adding additional entities to the derived context and creating migrations to add them to the database etc.
We are starting a new application with with .NET Core, EF Core and Postgres - we're really liking the migrations :-)
However, I've found a problem that I can't figure out how to work around. In some of our migrations, we use the DbContext to load records, run some business logic, and resave the records. We also run database.Context.Migrate() when the web server starts, so it always runs.
This all works fine (so far).
Unfortunately it works right up until the model is changed at a later point. Consider the following migration order:
Create Customer, Order, and OrderLine models. Add the schema migrations to load the structures
A business rule changes, so we need to set a value on the Order record based on some business logic . We create a data migration where we load the DbContext, run the code, and run SaveChangesAsync()
At this point, everything is fine.
We need to add a schema migration with a new field to the Customer record.
Here, things break. If the database has not had migration 2 applied, the Migrate() command errors as when applying migration 2, EF is trying to generate a select statement that includes the new field in 3.
I'm not sure where the problem actually is in the process though: should we not be using the DbContext in the migration (and hand-code SQL statements)? Should we be explicitly be doing projections on every record read so that we know exactly what we're doing?
Something else?
In your example, 1 and 3 are Schema Migrations and 2 is a Data Migration.
Since Entity Framework Core will generate queries based off the current models in code, and not the current state of the database, there is two options for handling Data Migrations.
Perform Data Migrations In-Order with Schema Migrations
Use something like Dapper or ADO.NET to perform data migrations based on the schema at the time the migration is written. You can get the DbConnection object from the EF Context using context.Database.GetDbConnection()
Pros: Migration code will never need to be changed
Cons: You won't get any of the benefits of EF Core
Perform Data Migrations After Schema Migrations
EF performs migrations in the ascending order of the MigrationAttribute.Id string value. EF generates this based off a timestamp when calling dotnet ef migrations add xxx. You can change this attribute to ensure Data Migrations are run after all Schema Migrations. Change the filename as well for readability so that they stay in the order they will be applied.
Pros: You get all the benefits of EF Core
Cons: If the schema changes in the future, such as removing a column referenced in the data migration, you will need to modify your migration
Example MigrationAttribute.Id change:
// change
[Migration("20191002171530_DataMigration")]
// to
[Migration("99999999000001_DataMigration")]
I faced a similar issue and landed on this question; I thought I'd share the technique I used to resolve my problem for posterity.
In my case, we had a model Something that initially had a field/database column property, but the field was deprecated because property ended up being stored elsewhere. We wrote a data migration that referenced Something.property and moved it to its new location.
We wanted to update the domain model to remove property from Something as well. However, doing so would break the typechecking in the data migration.
We must have a type that has property on it to allow the data migration to typecheck. However, we must not have property on the domain model.
The solution was to introduce MigrationDbContext, which subclasses the standard DbContext. MigrationDbContext exposes a second projection of the Somethings table, called Somethings_WithProperty. This projection is a public DbSet<SomethingWithProperty>. The SomethingWithProperty class is a record of the historical type of the model with its contemporary properties; and importantly, we only allow the Type to show up in our Migrations folder. SomethingWithProperty is stored in Migrations/HistoricalModels.
Now, my data migration acquires a MigrationDbContext from dependency injection instead of a DbContext. I can replace the reference to dbcontext.Somethings with dbcontext.Somethings_WithProperty, and the data migration can typecheck. My domain model on the other hand will never be procured from the MigrationDbContext, so the only Somethings it can get are from the Somethings DbSet, which correctly omits the property that we dropped.
A few caveats; if your DbContext constructor takes a generic DbContextOptions<MyDbContext> parameter, then to satisfy dependency injection, you will need to introduce a protected constructor on MyDbContext that takes a DbContextOptions<MigrationDbContext> parameter; the child class can invoke this in its constructor. Also, MigrationDbContext will initially be confused that two projections are mapped to the same table; in its SetupSomethingsWithProperty method you should include a foreign key referencing the natural projection to clear up the confusion.
private static void SetupSomethingsWithProperty(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Something_WithProperty>(s =>
{
s.ToTable("Somethings");
s.HasOne<Something>().WithOne().HasForeignKey<Something_WithProperty>(r => r.ID);
});
}
In our software, we have a customer base with existing databases. The databases are currently accessed via EntitySpaces, but we'd like to switch to EntityFramework (v6), as EntitySpaces is no longer supported. We'd also like to make use of the migrations feature. Automatic migrations are disabled, since we only want to allow database migration to an admin user.
We generated the EF model from an existing database. It all works pretty well, but the real problem we have, is, programmatically distinguishing between existing databases that match the model but have not yet been converted to EF (missing MigrationsHistory table), and empty/new databases. Converting existing databases works well with an empty migration, but for new databases we also need a migration containing the full model. Having an initial migration in the migration chain always clashes with existing databases. Of course we could create a workaround with external SQL scripts or ADO commands, creating and populating the MigrationsHistory table. But that is something we'd like to avoid, because some of our clients use MsSql databases, some use Oracle. So we'd really like to keep the abstraction layer provided by EF.
Is there a way to get EF to handle both existing, and new databases through code-based migrations, without falling back to non-EF workarounds?
My original suggestion was to trap the exception raised by CreateTable, but it turns out this is executed in a different place so this cannot be trapped within the exception.
The simplest method of proceeding will be to use the Seed method to create your initial database if it is not present. To do this...
Starting from a blank database, add an Initial Create migration and grab the generated SQL
Add-Migration InitialCreate
Update-Database -Script
Save this script. You could add it to a resource, static file or even leave it inline in your code if you really want, it's up to you.
Delete all of the code from the InitialCreate migration (leaving it with a blank Up() and Down() function). This will allow your empty migration to be run, causing the MigrationHistory table to be generated.
In your Migration configuration class, you can query and execute SQL dynamically using context.Database.SqlQuery and context.Database.ExecuteSqlCommand. Test for the existence of your main tables, and if it's not present, execute the script generated above.
This isn't very neat, but it's simple to implement. Test it well, as the Seed method runs after EVERY migration runs, not just the initial one. This is why you need to test for the existence of a main table before you do anything.
The more complicated approach would be to write a "CreateTableIfNotExists" method for migrations, but this will involve use of Reflection to call internal methods in the DbMigration class.
I'm investigating using Code-Based EF Migrations for a product that does not use EF. Everything generally works well, except that the command:
Add-Migration MyTestMigration
outputs the following message:
Unable to generate an explicit migration because the following explicit migrations are pending: [201206260845338_DannyTest]. Apply the pending explicit migrations before attempting to generate a new explicit migration.
The reason for this is that the connection string is not known at build time, and EF has randomly created a database called "MyContextName" on .\SQLExpress. I cannot apply the pending migration, because it references database tables that do not exist in this database - we're just trying to use migrations as a way of executing our scripts;
So the questions are:
If we're not using Automatic Migrations (we have EnableAutomaticMigrations=false), why does Add-Migration require that the database is up-to-date even though it has absolutely no impact on the generated (empty) migration? I find it hard to believe MS don't intend this use case when so much of it works; the only "broken" thing is validation that doesn't affect any behaviour.
Is there any way around this other than creating our own Add-Migration command that just duplicates what the EF one does, but skips the (seemingly needless) DB up-to-date check? I've tried passing various arguments, but so far not managed to make it work.
Edit:
I actually found a better way to solve this problem, but it's not really an answer to these questions, so adding it here. Hopefully will get time to turn this into a blog post!
The only reason I wanted to use Add-Migration was because of all the guff that went along with the DbMigration; but I realised that with a base class, we could basically eliminate the need for all this by having the base class auto-generate the migration ID from an attribute. The Target is identical for all our migrations, as the model state doesn't change. Now, we just manually create our migrations like this (the date is required to build the ID such that EF will apply them in the correct order):
[Migration(2012, 6, 27, 12, 00, "Add new xxx fields for yyy")]
internal class MyNewMigration : MyDbMigration
{
public override Up()
{
// ...
}
public override Down()
{
// ...
}
}
The MyDbMigration class has the Target/Source/Id properties. Target is hard-coded (the same value that Add-Migration created with the first migration), Source is null, and Id is some reflection that reads the MigrationAttribute. This means we can now just manually create these classes; which isn't a lot of effort now we don't have to worry about all the IMigrationMetadata stuff :-)
Try commenting out your existing migrations (the ones that haven't been applied to the database created on .\SQLExpress) and re-running your app. That should populate the local database with the initial tables it needs.
Once the local database has the correct structure you should be able to uncomment your migrations and then use update-database to bring the local one up to date. Then you'll be able to add a new migration.
Also remember that there's a -connectionString parameter on the update-database command, so you can target your migrations at a specific server/db.
I was seeing this error until I deleted from the Solution the original auto-generated migration code that the Package Manager had initially created.
This would have been 201206260845338_DannyTest.cs in Danny's case.