9 .net asp.net-mvc entity-framework
我在EF中制作了几个表并输入了一些种子数据,我用一个主键给几列提供了值.当我运行应用程序时,我收到错误消息:
当IDENTITY_INSERT设置为OFF时,无法在表'Persons'中为identity列插入显式值.
我该如何开启?我在这里阅读使用:
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
Run Code Online (Sandbox Code Playgroud)
在作为主键的属性上方.遗憾的是,我仍然收到相同的错误消息.请帮忙.
我添加[DatabaseGenerated(DatabaseGeneratedOption.None)]
到所有具有主键的属性中.当我运行迁移时,我可以看到标识列已被删除,但我仍然收到相同的错误消息.
当我进入SQL SEO时,我仍然可以在主键上看到标识列.我尝试刷新数据库.我究竟做错了什么?我唯一能做的就是进入属性并删除身份,但为什么我不能按照上面提到的方式去做呢?
SAN*_*009 11
不得不处理同样的问题,这似乎是一个干净的解决方案。
归功于 >> https://github.com/dotnet/efcore/issues/11586
我进行了一些更改,因此它现在适用于 .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();
}
}
Run Code Online (Sandbox Code Playgroud)
用法
var data = new MyType{SomeProp= DateTime.Now, Id = 1};
context.MyType.Add(data);
context.SaveChangesWithIdentityInsert<MyType>();
Run Code Online (Sandbox Code Playgroud)
sny*_*m42 10
在EF Core 1.1.2中,我使用它来处理事务.在我的"数据库初始化程序"中,将种子数据放入表中.我使用了EF6答案中的技术.以下是代码示例:
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();
}
Run Code Online (Sandbox Code Playgroud)
Steve Nyholm
的答案很好,但我会提供一些额外的解释和一些通用代码与异常处理.
通常情况下,上下文负责交易,但在这种情况下需要手动处理它.为什么?
数据库上下文将BEGIN TRAN
在SET IDENTITY_INSERT
发布后生成.这将使事务的插入失败,因为IDENTITY_INSERT似乎影响会话/事务级别的表.
因此,所有内容都必须包含在单个事务中才能正常工作.
以下是一些在密钥级别(与表级别相对)进行种子处理的有用代码:
[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);
}
Run Code Online (Sandbox Code Playgroud)
(假设模型类名与表名相同)
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);
});
}
Run Code Online (Sandbox Code Playgroud)
@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();
}
Run Code Online (Sandbox Code Playgroud)
另一种方法是显式打开连接 then SET IDENTITY_INSERT <table> ON
。
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();
Run Code Online (Sandbox Code Playgroud)
显然,一旦在 EF 请求之前显式打开连接,EF 不会自动关闭该连接,因此该设置将应用于相同的连接上下文。
这与 Steve 对事务的响应在事务保持连接活动时起作用的原因相同。
注意:如果您计划稍后在应用程序/请求中再次使用相同的上下文,您不希望将连接放入
using
语句中。连接必须存在,因此清除连接上下文的最佳方法是针对它,从而使 EF 返回其默认行为,即每次操作打开和关闭连接。.Close()
另一种方法是使用 ExecuteSqlRaw。与 ExecuteSqlInterpolated 不同,您不必将传递的字符串转换为可格式化的字符串类型。
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();
}
Run Code Online (Sandbox Code Playgroud)
@sanm2009 提出的解决方案包含一些不错的想法。
然而,该实现存在一些与 Task/async/await 误用相关的缺陷。
SaveChangesWithIdentityInsert 方法不返回 Task,也不等待对 EnableIdentityInsert 和 DisableIdentityInsert 的调用。
这可能会导致不希望的副作用。
以下实现同时支持 async/await 和 non-awaitable 范式。
#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
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
8423 次 |
最近记录: |