C# 首先使用EntityFramework 6代码在种子设定期间插入标识
我有一个实体,它有一个C# 首先使用EntityFramework 6代码在种子设定期间插入标识,c#,entity-framework,entity-framework-6,C#,Entity Framework,Entity Framework 6,我有一个实体,它有一个自动标识(int)列。作为数据种子的一部分,我希望为系统中的“标准数据”使用特定的标识符值,然后我希望让数据库对id值进行排序 到目前为止,我已经能够将INSERT批处理中的IDENTITY\u INSERT设置为On,但是实体框架没有生成包含Id的INSERT语句。这是有意义的,因为模型认为数据库应该提供值,但在本例中,我希望提供值 模型(伪代码): 公共类引用 { [关键] [数据库生成(DatabaseGeneratedOption.Identity)] 公共int
自动标识(int)
列。作为数据种子的一部分,我希望为系统中的“标准数据”使用特定的标识符值,然后我希望让数据库对id值进行排序
到目前为止,我已经能够将INSERT批处理中的IDENTITY\u INSERT
设置为On,但是实体框架没有生成包含Id
的INSERT语句。这是有意义的,因为模型认为数据库应该提供值,但在本例中,我希望提供值
模型(伪代码):
公共类引用
{
[关键]
[数据库生成(DatabaseGeneratedOption.Identity)]
公共int Id{get;set;}
公共字符串名称{get;set;}
}
公营播种机
{
公共void种子(DbContext)
{
var myThing=新引用
{
Id=1,
Name=“Id为1的对象”
};
context.Set.Add(虚构);
context.Database.Connection.Open();
context.Database.ExecuteSqlCommand(“设置标识\u插入引用打开”)
context.SaveChanges();//如果没有第二个EF级别的模型,则无法完成-复制用于种子设定的类
正如您所说,您的元数据表示DB提供了值,而在种子设定期间它不会提供值。因此,我可能会通过生成包含Id列的自己的SQL insert语句来解决这个问题。这感觉像是一个可怕的攻击,但它可以工作:-/
public class Seeder
{
public void Seed (DbContext context)
{
var myThing = new ReferenceThing
{
Id = 1,
Name = "Thing with Id 1"
};
context.Set<ReferenceThing>.Add(myThing);
context.Database.Connection.Open();
context.Database.ExecuteSqlCommand("SET IDENTITY_INSERT ReferenceThing ON")
// manually generate SQL & execute
context.Database.ExecuteSqlCommand("INSERT ReferenceThing (Id, Name) " +
"VALUES (@0, @1)",
myThing.Id, myThing.Name);
context.Database.ExecuteSqlCommand("SET IDENTITY_INSERT ReferenceThing OFF")
}
}
公共类播种机
{
公共void种子(DbContext)
{
var myThing=新引用
{
Id=1,
Name=“Id为1的对象”
};
context.Set.Add(虚构);
context.Database.Connection.Open();
context.Database.ExecuteSqlCommand(“设置标识\u插入引用打开”)
//手动生成SQL并执行
context.Database.ExecuteSqlCommand(“插入引用内容(Id,名称)”+
“值(@0,@1)”,
myThing.Id,myThing.Name);
context.Database.ExecuteSqlCommand(“设置标识\插入引用关闭”)
}
}
如果使用database first模型,则应将ID列的StoreGeneratedPattern属性从Identity更改为None
在那之后,正如我回答的那样,这应该会有帮助:
using (var transaction = context.Database.BeginTransaction())
{
var myThing = new ReferenceThing
{
Id = 1,
Name = "Thing with Id 1"
};
context.Set<ReferenceThing>.Add(myThing);
context.Database.ExecuteSqlCommand("SET IDENTITY_INSERT ReferenceThing ON");
context.SaveChanges();
context.Database.ExecuteSqlCommand("SET IDENTITY_INSERT ReferenceThing OFF");
transaction.Commit();
}
使用(var transaction=context.Database.BeginTransaction())
{
var myThing=新引用
{
Id=1,
Name=“Id为1的对象”
};
context.Set.Add(虚构);
context.Database.ExecuteSqlCommand(“设置标识\插入引用打开”);
SaveChanges();
context.Database.ExecuteSqlCommand(“设置标识\插入引用关闭”);
Commit();
}
我为我的DbContext
创建了一个替代构造函数,该构造函数接受boolallowedtityinserts
。我在DbContext
上将该bool设置为同名的私有字段
MyOnModelCreating
然后,如果在该“模式”中创建上下文,则“未指定”标识规范
模型创建时受保护的覆盖无效(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove();
if(AllowedTityInsert)
{
modelBuilder.Entity()
.Property(x=>x.Id)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
}
}
这允许我在不更改实际数据库标识规范的情况下插入Id。我仍然需要使用您使用的标识插入开/关技巧,但至少EF会发送Id值。在试验了在该网站上找到的几个选项后,以下代码对我有效(EF 6)。请注意,如果该项已存在,它将首先尝试正常更新。如果该项不存在,则尝试正常插入,如果错误是由IDENTITY_insert引起的,则尝试解决方法。还请注意,db.SaveChanges将失败,从而导致db.Database.Connection.Open()失败语句和可选验证步骤。请注意,这不是更新上下文,但在我的情况下,这不是必需的。希望这有帮助
public static bool UpdateLeadTime(int ltId, int ltDays)
{
try
{
using (var db = new LeadTimeContext())
{
var result = db.LeadTimes.SingleOrDefault(l => l.LeadTimeId == ltId);
if (result != null)
{
result.LeadTimeDays = ltDays;
db.SaveChanges();
logger.Info("Updated ltId: {0} with ltDays: {1}.", ltId, ltDays);
}
else
{
LeadTime leadtime = new LeadTime();
leadtime.LeadTimeId = ltId;
leadtime.LeadTimeDays = ltDays;
try
{
db.LeadTimes.Add(leadtime);
db.SaveChanges();
logger.Info("Inserted ltId: {0} with ltDays: {1}.", ltId, ltDays);
}
catch (Exception ex)
{
logger.Warn("Error captured in UpdateLeadTime({0},{1}) was caught: {2}.", ltId, ltDays, ex.Message);
logger.Warn("Inner exception message: {0}", ex.InnerException.InnerException.Message);
if (ex.InnerException.InnerException.Message.Contains("IDENTITY_INSERT"))
{
logger.Warn("Attempting workaround...");
try
{
db.Database.Connection.Open(); // required to update database without db.SaveChanges()
db.Database.ExecuteSqlCommand("SET IDENTITY_INSERT[dbo].[LeadTime] ON");
db.Database.ExecuteSqlCommand(
String.Format("INSERT INTO[dbo].[LeadTime]([LeadTimeId],[LeadTimeDays]) VALUES({0},{1})", ltId, ltDays)
);
db.Database.ExecuteSqlCommand("SET IDENTITY_INSERT[dbo].[LeadTime] OFF");
logger.Info("Inserted ltId: {0} with ltDays: {1}.", ltId, ltDays);
// No need to save changes, the database has been updated.
//db.SaveChanges(); <-- causes error
}
catch (Exception ex1)
{
logger.Warn("Error captured in UpdateLeadTime({0},{1}) was caught: {2}.", ltId, ltDays, ex1.Message);
logger.Warn("Inner exception message: {0}", ex1.InnerException.InnerException.Message);
}
finally
{
db.Database.Connection.Close();
//Verification
if (ReadLeadTime(ltId) == ltDays)
{
logger.Info("Insertion verified. Workaround succeeded.");
}
else
{
logger.Info("Error!: Insert not verified. Workaround failed.");
}
}
}
}
}
}
}
catch (Exception ex)
{
logger.Warn("Error in UpdateLeadTime({0},{1}) was caught: {2}.", ltId.ToString(), ltDays.ToString(), ex.Message);
logger.Warn("Inner exception message: {0}", ex.InnerException.InnerException.Message);
Console.WriteLine(ex.Message);
return false;
}
return true;
}
publicstaticboolupdateladtime(int-ltId,int-ltDays)
{
尝试
{
使用(var db=new LeadTimeContext())
{
var result=db.LeadTimes.SingleOrDefault(l=>l.LeadTimeId==ltId);
如果(结果!=null)
{
result.LeadTimeDays=ltDays;
db.SaveChanges();
Info(“用ltDays:{1}更新了ltId:{0}。”,ltId,ltDays);
}
其他的
{
交付周期交付周期=新的交付周期();
leadtime.LeadTimeId=ltId;
leadtime.LeadTimeDays=ltDays;
尝试
{
db.提前期。添加(提前期);
db.SaveChanges();
Info(“插入的ltId:{0}和ltDays:{1}.”,ltId,ltDays);
}
捕获(例外情况除外)
{
Warn(“捕获到UpdateLeadTime({0},{1})中捕获的错误:{2}.”,ltId,ltDays,ex.Message);
Warn(“内部异常消息:{0}”,ex.InnerException.InnerException.message);
if(例如InnerException.InnerException.Message.Contains(“标识插入”))
{
logger.Warn(“正在尝试解决…”);
尝试
{
db.Database.Connection.Open();//在没有db.SaveChanges()的情况下更新数据库需要
db.Database.ExecuteSqlCommand(“设置标识插入[dbo].[LeadTime]ON”);
db.Database.ExecuteSqlCommand(
String.Format(“I
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
if(allowIdentityInsert)
{
modelBuilder.Entity<ChargeType>()
.Property(x => x.Id)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
}
}
public static bool UpdateLeadTime(int ltId, int ltDays)
{
try
{
using (var db = new LeadTimeContext())
{
var result = db.LeadTimes.SingleOrDefault(l => l.LeadTimeId == ltId);
if (result != null)
{
result.LeadTimeDays = ltDays;
db.SaveChanges();
logger.Info("Updated ltId: {0} with ltDays: {1}.", ltId, ltDays);
}
else
{
LeadTime leadtime = new LeadTime();
leadtime.LeadTimeId = ltId;
leadtime.LeadTimeDays = ltDays;
try
{
db.LeadTimes.Add(leadtime);
db.SaveChanges();
logger.Info("Inserted ltId: {0} with ltDays: {1}.", ltId, ltDays);
}
catch (Exception ex)
{
logger.Warn("Error captured in UpdateLeadTime({0},{1}) was caught: {2}.", ltId, ltDays, ex.Message);
logger.Warn("Inner exception message: {0}", ex.InnerException.InnerException.Message);
if (ex.InnerException.InnerException.Message.Contains("IDENTITY_INSERT"))
{
logger.Warn("Attempting workaround...");
try
{
db.Database.Connection.Open(); // required to update database without db.SaveChanges()
db.Database.ExecuteSqlCommand("SET IDENTITY_INSERT[dbo].[LeadTime] ON");
db.Database.ExecuteSqlCommand(
String.Format("INSERT INTO[dbo].[LeadTime]([LeadTimeId],[LeadTimeDays]) VALUES({0},{1})", ltId, ltDays)
);
db.Database.ExecuteSqlCommand("SET IDENTITY_INSERT[dbo].[LeadTime] OFF");
logger.Info("Inserted ltId: {0} with ltDays: {1}.", ltId, ltDays);
// No need to save changes, the database has been updated.
//db.SaveChanges(); <-- causes error
}
catch (Exception ex1)
{
logger.Warn("Error captured in UpdateLeadTime({0},{1}) was caught: {2}.", ltId, ltDays, ex1.Message);
logger.Warn("Inner exception message: {0}", ex1.InnerException.InnerException.Message);
}
finally
{
db.Database.Connection.Close();
//Verification
if (ReadLeadTime(ltId) == ltDays)
{
logger.Info("Insertion verified. Workaround succeeded.");
}
else
{
logger.Info("Error!: Insert not verified. Workaround failed.");
}
}
}
}
}
}
}
catch (Exception ex)
{
logger.Warn("Error in UpdateLeadTime({0},{1}) was caught: {2}.", ltId.ToString(), ltDays.ToString(), ex.Message);
logger.Warn("Inner exception message: {0}", ex.InnerException.InnerException.Message);
Console.WriteLine(ex.Message);
return false;
}
return true;
}
protected override void Seed(DBContextIMD context)
{
bool HasDefaultRecord;
HasDefaultRecord = false;
DBContext.ABCStatusList.Where(DBEntity => DBEntity.ID == 0).ToList().ForEach(DBEntity =>
{
DBEntity.ABCStatusCode = @"Default";
HasDefaultRecord = true;
});
if (HasDefaultRecord) { DBContext.SaveChanges(); }
else {
using (var dbContextTransaction = DBContext.Database.BeginTransaction()) {
try
{
DBContext.IdentityInsert<ABCStatus>(true);
DBContext.ABCStatusList.Add(new ABCStatus() { ID = 0, ABCStatusCode = @"Default" });
DBContext.SaveChanges();
DBContext.IdentityInsert<ABCStatus>(false);
dbContextTransaction.Commit();
}
catch (Exception ex)
{
// Log Exception using whatever framework
Debug.WriteLine(@"Insert default record for ABCStatus failed");
Debug.WriteLine(ex.ToString());
dbContextTransaction.Rollback();
DBContext.RollBack();
}
}
}
}
public static class ContextExtensions
{
public static string GetTableName<T>(this DbContext context) where T : class
{
ObjectContext objectContext = ((IObjectContextAdapter)context).ObjectContext;
return objectContext.GetTableName<T>();
}
public static string GetTableName<T>(this ObjectContext context) where T : class
{
string sql = context.CreateObjectSet<T>().ToTraceString();
Regex regex = new Regex(@"FROM\s+(?<table>.+)\s+AS");
Match match = regex.Match(sql);
string table = match.Groups["table"].Value;
return table;
}
}
public MyDBContext(bool _EnableIdentityInsert)
: base("name=ConnectionString")
{
EnableIdentityInsert = _EnableIdentityInsert;
}
private bool EnableIdentityInsert = false;
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
Database.SetInitializer(new MigrateDatabaseToLatestVersion<DBContextIMD, Configuration>());
//modelBuilder.Entity<SomeEntity>()
// .Property(e => e.SomeProperty)
// .IsUnicode(false);
// Etc... Configure your model
// Then add the following bit
if (EnableIdentityInsert)
{
modelBuilder.Entity<SomeEntity>()
.Property(x => x.ID)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
modelBuilder.Entity<AnotherEntity>()
.Property(x => x.ID)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
}
}
//Add this for Identity Insert
/// <summary>
/// Enable Identity insert for specified entity type.
/// Note you should wrap the identity insert on, the insert and the identity insert off in a transaction
/// </summary>
/// <typeparam name="T">Entity Type</typeparam>
/// <param name="On">If true sets identity insert on else set identity insert off</param>
public void IdentityInsert<T>(bool On)
where T: class
{
if (!EnableIdentityInsert)
{
throw new NotSupportedException(string.Concat(@"Cannot Enable entity insert on ", typeof(T).FullName, @" when _EnableIdentityInsert Parameter is not enabled in constructor"));
}
if (On)
{
Database.ExecuteSqlCommand(string.Concat(@"SET IDENTITY_INSERT ", this.GetTableName<T>(), @" ON"));
}
else
{
Database.ExecuteSqlCommand(string.Concat(@"SET IDENTITY_INSERT ", this.GetTableName<T>(), @" OFF"));
}
}
//Add this for Rollback changes
/// <summary>
/// Rolls back pending changes in all changed entities within the DB Context
/// </summary>
public void RollBack()
{
var changedEntries = ChangeTracker.Entries()
.Where(x => x.State != EntityState.Unchanged).ToList();
foreach (var entry in changedEntries)
{
switch (entry.State)
{
case EntityState.Modified:
entry.CurrentValues.SetValues(entry.OriginalValues);
entry.State = EntityState.Unchanged;
break;
case EntityState.Added:
entry.State = EntityState.Detached;
break;
case EntityState.Deleted:
entry.State = EntityState.Unchanged;
break;
}
}
}
using (var transaction = context.Database.BeginTransaction())
{
var item = new ReferenceThing{Id = 418, Name = "Abrahadabra" };
context.IdentityItems.Add(item);
context.Database.ExecuteSqlCommand("SET IDENTITY_INSERT Test.Items ON;");
context.SaveChanges();
context.Database.ExecuteSqlCommand("SET IDENTITY_INSERT [dbo].[User] OFF");
transaction.Commit();
}
public class ReferenceThing
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public string Name { get; set; }
}
public class MyDbContext : DbContext
{
public DbSet<ReferenceThing> ReferenceThing { get; set; }
}
public class MyDbContextWhichAllowsIdentityInsert : MyDbContext
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<ReferenceThing>()
.Property(x => x.Id)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
}
}
var specialDbContext = new MyDbContextWhichAllowsIdentityInsert();
Seeder.Seed(specialDbContext);
public class Branch
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int BranchId { get; set; }
public string Description { get; set; }
}