How to turn on identity-insert in .net core

后端 未结 9 1253
日久生厌
日久生厌 2020-12-15 07:12

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: <

相关标签:
9条回答
  • 2020-12-15 08:07

    @Steve Nyholm answer is OK, But in .Net core 3 ExecuteSqlCommand is Obsolete, ExecuteSqlInterpolated replacement of ExecuteSqlCommand:

    using (var db = new AppDbContext())
    using (var transaction = db.Database.BeginTransaction())
    {
        var user = new User {Id = 123, Name = "Joe"};
        db.Users.Add(user);
        db.Database.ExecuteSqlInterpolated($"SET IDENTITY_INSERT MyDB.Users ON;");
        db.SaveChanges();
        db.Database.ExecuteSqlInterpolated($"SET IDENTITY_INSERT MyDB.Users OFF");
        transaction.Commit();
    }
    
    0 讨论(0)
  • 2020-12-15 08:08

    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):

    Extensions.cs

    [Pure]
    public static bool Exists<T>(this DbSet<T> dbSet, params object[] keyValues) where T : class
    {
        return dbSet.Find(keyValues) != null;
    }
    
    public static void AddIfNotExists<T>(this DbSet<T> dbSet, T entity, params object[] keyValues) where T: class
    {
        if (!dbSet.Exists(keyValues))
            dbSet.Add(entity);
    }
    

    DbInitializer.cs

    (assumes that model class name is the same as table name)

    private static void ExecuteWithIdentityInsertRemoval<TModel>(AspCoreTestContext context, Action<AspCoreTestContext> 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<TestModel>(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);
        });
    }
    
    0 讨论(0)
  • 2020-12-15 08:08

    Had to deal with the same issue and this seems to be a clean solution.

    Credit to >> https://github.com/dotnet/efcore/issues/11586

    I have made some changes so it now works with .Net Core 3.1 + (Tested in .Net 5) and also added this Method SaveChangesWithIdentityInsert

        public static class IdentityHelpers
    {
        public static Task EnableIdentityInsert<T>(this DbContext context) => SetIdentityInsert<T>(context, enable: true);
        public static Task DisableIdentityInsert<T>(this DbContext context) => SetIdentityInsert<T>(context, enable: false);
    
        private static Task SetIdentityInsert<T>(DbContext context, bool enable)
        {
            var entityType = context.Model.FindEntityType(typeof(T));
            var value = enable ? "ON" : "OFF";
            return context.Database.ExecuteSqlRawAsync(
                $"SET IDENTITY_INSERT {entityType.GetSchema()}.{entityType.GetTableName()} {value}");
        }
    
        public static void SaveChangesWithIdentityInsert<T>(this DbContext context)
        {
            using var transaction = context.Database.BeginTransaction();
            context.EnableIdentityInsert<T>();
            context.SaveChanges();
            context.DisableIdentityInsert<T>();
            transaction.Commit();
        }
    
    }
    

    Usage

            var data = new MyType{SomeProp= DateTime.Now, Id = 1};
                context.MyType.Add(data);
            context.SaveChangesWithIdentityInsert<MyType>();
    
    0 讨论(0)
提交回复
热议问题