I made a few tables in EF and entered in some seed data where I give value to a few columns with a primary key. When I run the application I am getting the error message: <
Steve Nyholm
's answer works fine, but I will provide some extra explanation and some generic code with exception handling.
Normally the context takes care of the transaction, but in this case manually taking care of it is required. Why?
Database context will generate a BEGIN TRAN
after the SET IDENTITY_INSERT
is issued. This will make transaction's inserts to fail since IDENTITY_INSERT seems to affect tables at session/transaction level.
So, everything must be wrapped in a single transaction to work properly.
Here is some useful code to seed at key level (as opposed to table level):
[Pure]
public static bool Exists(this DbSet dbSet, params object[] keyValues) where T : class
{
return dbSet.Find(keyValues) != null;
}
public static void AddIfNotExists(this DbSet dbSet, T entity, params object[] keyValues) where T: class
{
if (!dbSet.Exists(keyValues))
dbSet.Add(entity);
}
(assumes that model class name is the same as table name)
private static void ExecuteWithIdentityInsertRemoval(AspCoreTestContext context, Action act) where TModel: class
{
using (var transaction = context.Database.BeginTransaction())
{
try
{
context.Database.ExecuteSqlCommand("SET IDENTITY_INSERT " + typeof(TModel).Name + " ON;");
context.SaveChanges();
act(context);
context.SaveChanges();
transaction.Commit();
}
catch(Exception)
{
transaction.Rollback();
throw;
}
finally
{
context.Database.ExecuteSqlCommand($"SET IDENTITY_INSERT " + typeof(TModel).Name + " OFF;");
context.SaveChanges();
}
}
}
public static void Seed(AspCoreTestContext context)
{
ExecuteWithIdentityInsertRemoval(context, ctx =>
{
ctx.TestModel.AddIfNotExists(new TestModel { TestModelId = 1, ModelCode = "Test model #1" }, 1);
ctx.TestModel.AddIfNotExists(new TestModel { TestModelId = 2, ModelCode = "Test model #2" }, 2);
});
}