I am looking into Migrations in an effort to clean up our deployment processes. The less manual intervention required when pushing a change to production the better.
I have run into 3 major snags with the migrations system. They are show stoppers if I can not figure out a clean way around them.
1. How do I add Seed data per migration:
I execute the command "add-migration" which scaffolds a new migration file with Up and Down functions. Now, I want to automatically make changes to the data with both Up and Down changes. I don't want to add the Seed data to the Configuration.Seed method as this runs for all migrations which ends in all sorts of duplication problems.
2. If the above is not possible, how do I avoid duplications?
I have an enum that I loop through to add the values to the database.
foreach(var enumValue in Enum.GetValues(typeof(Access.Level)))
{
context.Access.AddOrUpdate(
new Access { AccessId = ((int)enumValue), Name = enumValue.ToString() }
);
}
context.SaveChanges();
Even though I am using AddOrUpdate, I still get duplicates in the database. The above code brings me to my 3rd and final problem:
3. How can I seed Primary Keys?
My enumerable with the above code is:
public class Access
{
public enum Level
{
None = 10,
Read = 20,
ReadWrite = 30
}
public int AccessId { get; set; }
public string Name { get; set; }
}
I am specifying the values that I want as my primary key, but Entity Framework seems to ignore it. They still end up being 1,2,3. How do I get it to be 10,20,30?
Are these limitations of EF at the moment or are they intentional constraints to prevent some other kind of catastrophe I am not seeing?
As a possible solution to item 1, I made an implementation of the
IDatabaseInitializer
strategy which will run the Seed method of each pending migration only, you will need to implement a customIMigrationSeed
interface in each of yourDbMigration
classes, theSeed
method will then be implemented right afterUp
andDown
methods of every migration class.This helps to solve two problems for me:
The interface looks like this
Below is the new implementation that will call this
Seed
methodThe good thing here is you have a real EF context to manipulate Seed Data, just like standard EF Seed implementation. However this can get strange if for example you decide to delete a table that was Seeded in a previous migration, you will have to refactor your existing Seed code accordingly.
EDIT: As an alternative to implement the seed method after the Up and Down, you can create a partial class of the same Migration class, I found this useful as it allows me to safely delete the migration class when I want to re-seed the same migration.
Hi I have found a very useful information for your problem in this link: Safari Books Online
"1. How do I add Seed data per migration:" As you see in the example you need to create a new confiugration for seeding. This seed Configuration must be called after migration.
"2. If the above is not possible, how do I avoid duplications?"
AddOrUpdate Must help you exactly to avoding the duplicates if you get an error here you might have a configuration error post the call stack please. See the example!
"3. How can I seed Primary Keys?"
Here it is also on your key definition. If your key
DatabaseGenerated(DatabaseGeneratedOption.Identity)
than you do not have to provide it. In some other senarios you need to create a new one it is depending on the key type."Are these limitations of EF at the moment or are they intentional constraints to prevent some other kind of catastrophe I am not seeing?"
Not that I know!
OK, so with a bit of bashing I have managed to bash EF into submission. Here is what I did:
1. There is no way that I found to see data for a specific migration. It all must go into the common Configuration.Seed method.
2. To avoid duplicates I had to do 2 things. For my enums, I wrote the following seed code:
So basically, just checking if it exists and adding if not
3. In order for the above to work, you need to be able to insert Primary Key Values. Luckily for me this table will always have the same static data so I could deactivate the auto increment. To do that, the code looks like:
Sql("Insert ...")
. See the note halfway down this page: how to insert fixed data[DatabaseGenerated(DatabaseGeneratedOption.None)]
attributeI think this is a good explanation of Initializer and Seed methods
Here is an example of how to use the AddOrUpdate method: