如何在.net core中启用标识插入

如何在.net core中启用标识插入,.net,asp.net-mvc,entity-framework,.net,Asp.net Mvc,Entity Framework,我在EF中创建了一些表,并输入了一些种子数据,在这些数据中,我用主键为一些列赋值。运行应用程序时,我收到错误消息: 当identity_insert设置为OFF时,无法在表“Persons”中为identity列插入显式值 我如何打开它?我在这里读到使用: [DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)] 位于作为主键的属性之上。不幸的是,我仍然收到相同的错误消息。请帮忙 我将[DatabaseGenerated(Da

我在EF中创建了一些表,并输入了一些种子数据,在这些数据中,我用主键为一些列赋值。运行应用程序时,我收到错误消息:

当identity_insert设置为OFF时,无法在表“Persons”中为identity列插入显式值

我如何打开它?我在这里读到使用:

[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
位于作为主键的属性之上。不幸的是,我仍然收到相同的错误消息。请帮忙

我将
[DatabaseGenerated(DatabaseGeneratedOption.None)]
添加到所有具有主键的属性中。当我运行迁移时,我可以看到identity列已被删除,但仍然收到相同的错误消息


当我进入SQL SEO时,我仍然可以看到主键上的identity列。我试图刷新数据库。我做错了什么?我唯一能做的就是进入属性并删除标识,但是为什么我不能用上面提到的方法呢

在EF Core 1.1.2中,我使用它处理事务。在我的“数据库初始化器”中,它将种子数据放入表中。我使用的技术来自。下面是代码示例:

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.ExecuteSqlCommand("SET IDENTITY_INSERT MyDB.Users ON;");
    db.SaveChanges();
    db.Database.ExecuteSqlCommand("SET IDENTITY_INSERT MyDB.Users OFF");
    transaction.Commit();
}

另一种方法是显式打开连接,然后在上设置IDENTITY\u INSERT

var conn = context.Database.GetDbConnection();
if (conn.State != ConnectionState.Open)
    conn.Open();

 context.Database.ExecuteSqlCommand("SET IDENTITY_INSERT Posts ON");

 var post = new WeblogPost()
 {                    
               Id= oldPost.Pk,  //   <!--- explicit value to Id field
               Title = oldPost.Title,
                ...
 };
 context.Posts.Add(post);    
 conn.Close();
var conn=context.Database.GetDbConnection();
如果(连接状态!=连接状态打开)
conn.Open();
context.Database.ExecuteSqlCommand(“SET IDENTITY_INSERT Posts ON”);
var post=new WeblogPost()
{                    

Id=oldPost.Pk,//基于NinjaCross的答案改进的解决方案

此代码直接添加到数据库上下文类中,并允许通过指定特定类型(映射到表)需要标识插入来保存更改

目前,我仅将其用于集成测试

public async Task<int> SaveChangesWithIdentityInsertAsync<TEnt>(CancellationToken token = default)
{
    await using var transaction = await Database.BeginTransactionAsync(token);
    await SetIdentityInsertAsync<TEnt>(true, token);
    int ret = await SaveChangesExAsync(token);
    await SetIdentityInsertAsync<TEnt>(false, token);
    await transaction.CommitAsync(token);

    return ret;
}

private async Task SetIdentityInsertAsync<TEnt>(bool enable, CancellationToken token)
{
    var entityType = Model.FindEntityType(typeof(TEnt));
    var value = enable ? "ON" : "OFF";
    string query = $"SET IDENTITY_INSERT {entityType.GetSchema()}.{entityType.GetTableName()} {value}";
    await Database.ExecuteSqlRawAsync(query, token);
}
DbInitializer.cs (假设模型类名与表名相同)

private static void executeWithidentialityInsertRemoving(AspCoreTestContext,Action act),其中TModel:class
{
使用(var transaction=context.Database.BeginTransaction())
{
尝试
{
context.Database.ExecuteSqlCommand(“SET IDENTITY_INSERT”+typeof(TModel.Name+“ON;”);
SaveChanges();
法案(上下文);
SaveChanges();
Commit();
}
捕获(例外)
{
transaction.Rollback();
投掷;
}
最后
{
context.Database.ExecuteSqlCommand($“SET IDENTITY_INSERT”+typeof(TModel.Name+“OFF;”);
SaveChanges();
}
}
}
公共静态无效种子(AspCoreTestContext上下文)
{
ExecuteWithidentityInsertRemoving(上下文,ctx=>
{
addifynotexists(新的TestModel{TestModelId=1,ModelCode=“TestModel#1”},1);
addifynotexists(新的TestModel{TestModelId=2,ModelCode=“TestModel#2”},2);
});
}

下面的解决方案对我很有效。() 我添加了以下注释。并删除了
[Key]
注释

[KeyAttribute()]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int Id { get; set; }
命名空间可以根据实体框架版本进行更改。对于实体框架,核心命名空间是
System.ComponentModel.DataAnnotations.Schema

自从我在一个新项目中尝试以来,我没有面临过数据迁移。

@Steve Nyholm回答是可以的,但在.Net core 3中,ExecuteSqlCommand已过时,ExecuteSqlInterpolated替换了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();
}

为了使用
DbContext
添加具有对象图的相关实体,我使用了一个
DbCommandInterceptor
,它会自动为相关的表设置
INSERT\u IDENTITY ON
,然后在INSERT之后设置
OFF
。这适用于手动设置的ID和
DbContext.SaveChanges
。我在我的集成测试,但在性能优化之后,在某些情况下,它可能适用于生产代码。

在事务中使用“设置标识插入[表]开/关”

    public static void TranslateDatabase(ref BDVContext bdvContext) 
    {
        bdvContext.Foro.RemoveRange(bdvContext.Foro);
        bdvContext.SaveChanges();

        using (var transaction = bdvContext.Database.BeginTransaction()) 
        { 

            bdvContext.Database.ExecuteSqlRaw("SET IDENTITY_INSERT [dbo].[Foro] On");

            using (old_balsaContext db = new old_balsaContext()) 
            {
                long id = 0;
                foreach (ForoA77 post in db.ForoA77.Where(x => x.Fecha > new DateTime(2000,1,1) & x.IdPadre == 0 ) )
                {
                    bdvContext.Foro.Add(new Foro 
                    {
                          Id = ++id
                        , ParentId = 0
                        , EditId = 0
                        , IdDomains = 2
                        , UserNick = post.IdUsuario == 1 ? bdvContext.Users.Where(x => x.Id == 2).Single().User : post.Nick?? ""
                        , IdUsers = post.IdUsuario == 1 ? (int?)2 : null
                        , Title = post.Asunto?? ""
                        , Text = post.Texto?? ""
                        , Closed = post.Cerrado?? false
                        , Banned = post.Veto?? false
                        , Remarqued = post.Remarcado?? false
                        , Deleted = false
                        , Date = post.Fecha?? new DateTime(2001,1,1)
                    });
                }
            }

            bdvContext.SaveChanges();

            bdvContext.Database.ExecuteSqlRaw("SET IDENTITY_INSERT [dbo].[Foro] Off");

            transaction.Commit();
        }
    }

注意,我的entityframework是通过反向工程生成的

另一种方法是使用ExecuteSqlRaw。与ExecuteSqlInterpolated不同,您不必将传递的字符串转换为formattable字符串类型

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.ExecuteSqlRaw("SET IDENTITY_INSERT MyDB.Users ON");
    db.SaveChanges();
    db.Database.ExecuteSqlRaw("SET IDENTITY_INSERT MyDB.Users OFF");
    transaction.Commit();

`}

必须处理相同的问题,这似乎是一个干净的解决方案

归功于>>

我做了一些更改,因此它现在可以与.Net Core 3.1+一起使用(在.Net 5中测试),并且还添加了此方法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();
    }

}
公共静态类标识帮助器
{
公共静态任务EnableIdentityInsert(此DbContext上下文)=>SetIdentityInsert(上下文,enable:true);
公共静态任务DisableIdentityInsert(此DbContext上下文)=>SetIdentityInsert(上下文,启用:false);
私有静态任务SetIdentityInsert(DbContext上下文,bool启用)
{
var entityType=context.Model.FindEntityType(typeof(T));
var值=启用?“开”:“关”;
返回context.Database.ExecuteSqlRawAsync(
$“设置标识\u插入{entityType.GetSchema()}.{entityType.GetTableName()}{value}”);
}
公共静态void SaveChangesWithIdentityInsert(此DbContext上下文)
{
使用var transaction=context.Database.BeginTransaction();
context.EnableIdentityInsert();
SaveChanges();
context.DisableIdentityInsert();
Commit();
}
}
用法

        var data = new MyType{SomeProp= DateTime.Now, Id = 1};
            context.MyType.Add(data);
        context.SaveChangesWithIdentityInsert<MyType>();
var data=newmytype{SomeProp=DateTime.Now,Id=1};
context.MyType.Add(数据);
context.SaveChangesWithIdentityInsert();

由@sanm2009提出的解决方案包含了一些不错的想法

但是,该实现存在一些与Task/async/await的误用相关的缺陷

方法savechangeswithidentialyinsert不返回任务,也不等待调用EnableIdentityInsert和DisableIdentityInsert

这个
    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();
    }

}
        var data = new MyType{SomeProp= DateTime.Now, Id = 1};
            context.MyType.Add(data);
        context.SaveChangesWithIdentityInsert<MyType>();
#region IDENTITY_INSERT

        public static void EnableIdentityInsert<T>(this DbContext context) => SetIdentityInsert<T>(context, true);
        public static void DisableIdentityInsert<T>(this DbContext context) => SetIdentityInsert<T>(context, false);

        private static void SetIdentityInsert<T>([NotNull] DbContext context, bool enable)
        {
            if (context == null) throw new ArgumentNullException(nameof(context));
            var entityType = context.Model.FindEntityType(typeof(T));
            var value = enable ? "ON" : "OFF";
            context.Database.ExecuteSqlRaw($"SET IDENTITY_INSERT {entityType.GetSchema()}.{entityType.GetTableName()} {value}");
        }

        public static void SaveChangesWithIdentityInsert<T>([NotNull] this DbContext context)
        {
            if (context == null) throw new ArgumentNullException(nameof(context));
            using var transaction = context.Database.BeginTransaction();
            context.EnableIdentityInsert<T>();
            context.SaveChanges();
            context.DisableIdentityInsert<T>();
            transaction.Commit();
        }

        #endregion 

        #region IDENTITY_INSERT ASYNC

        public static async Task EnableIdentityInsertAsync<T>(this DbContext context) => await SetIdentityInsertAsync<T>(context, true);
        public static async Task DisableIdentityInsertAsync<T>(this DbContext context) => await SetIdentityInsertAsync<T>(context, false);

        private static async Task SetIdentityInsertAsync<T>([NotNull] DbContext context, bool enable)
        {
            if (context == null) throw new ArgumentNullException(nameof(context));
            var entityType = context.Model.FindEntityType(typeof(T));
            var value = enable ? "ON" : "OFF";
            await context.Database.ExecuteSqlRawAsync($"SET IDENTITY_INSERT {entityType.GetSchema()}.{entityType.GetTableName()} {value}");
        }

        public static async Task SaveChangesWithIdentityInsertAsync<T>([NotNull] this DbContext context)
        {
            if (context == null) throw new ArgumentNullException(nameof(context));
            await using var transaction = await context.Database.BeginTransactionAsync();
            await context.EnableIdentityInsertAsync<T>();
            await context.SaveChangesAsync();
            await context.DisableIdentityInsertAsync<T>();
            await transaction.CommitAsync();
        }


        #endregion