实体框架代码第一个AddOrUpdate方法插入重复的值
本文关键字:插入 方法 框架 代码 第一个 AddOrUpdate 实体 | 更新日期: 2023-09-27 18:30:09
我有一个简单的实体:
public class Hall
{
[Key]
public int Id {get; set;}
public string Name [get; set;}
}
然后在Seed
方法中,我使用AddOrUpdate
填充表:
var hall1 = new Hall { Name = "French" };
var hall2 = new Hall { Name = "German" };
var hall3 = new Hall { Name = "Japanese" };
context.Halls.AddOrUpdate(
h => h.Name,
hall1,
hall2,
hall3
);
然后我在包管理控制台中运行:
Add-Migration Current
Update-Database
一切都很好:我在大厅的桌子上有三排。但如果我再次在包管理控制台Update-Database
中运行,我已经有五行了:
Id Name
1 French
2 Japaneese
3 German
4 French
5 Japanese
为什么?我认为应该是三排,而不是五排。我尝试使用Id
属性而不是Name
,但没有区别。
更新:
此代码产生相同的结果:
var hall1 = new Hall { Id = 1, Name = "French" };
var hall2 = new Hall { Id = 2, Name = "German" };
var hall3 = new Hall { Id = 3, Name = "Japanese" };
context.Halls.AddOrUpdate(
h => h.Id,
hall1);
context.Halls.AddOrUpdate(
h => h.Id,
hall2);
context.Halls.AddOrUpdate(
h => h.Id,
hall3);
此外,我通过nuget安装了最新的EntityFramework。
好吧,我用这个把脸从键盘上敲了一个小时。如果表的Id字段是Identity字段,那么它将不起作用,因此请为identifierExpression使用不同的字段。我使用了Name属性,还从new Hall {...}
初始值设定项中删除了Id字段。
这个对操作系统代码的调整对我来说很有效,所以我希望它能帮助到一些人:
protected override void Seed(HallContext context)
{
context.Halls.AddOrUpdate(
h => h.Name, // Use Name (or some other unique field) instead of Id
new Hall
{
Name = "Hall 1"
},
new Hall
{
Name = "Hall 2"
});
context.SaveChanges();
}
我知道这是一个老问题,但正确的答案是,如果你自己设置id#,并且你想使用AddOrUpdate,那么你需要告诉EF/SQL你不希望它生成id#。
modelBuilder.Entity<MyClass>().Property(p => p.Id)
.HasDatabaseGeneratedOption(System.ComponentModel
.DataAnnotations.Schema.DatabaseGeneratedOption.None);
不利的一面是,当你插入一个新项目时,你需要设置它的Id,所以如果这是在运行时动态完成的(而不是根据种子数据),那么你需要计算出下一个Id。Context.MyClasses.Max(c=>c.Id) + 1
运行良好。
此代码有效:
public Configuration()
{
AutomaticMigrationsEnabled = true;
}
protected override void Seed(HallContext context)
{
context.Halls.AddOrUpdate(
h => h.Id,
new Hall
{
Id = 1,
Name = "Hall 1"
},
new Hall
{
Id = 2,
Name = "Hall 2"
});
context.SaveChanges();
}
如果实体状态设置不正确,也可能导致此情况。当我运行更新数据库时,我一直收到以下错误。。。"序列包含多个匹配元素。"
例如,我在每个更新数据库命令上都创建了重复的行(当然,在播种数据时不应该发生这种情况),然后下一个更新数据库命令根本不起作用,因为它发现了多个匹配项(因此出现了序列错误,说我有多个匹配行)。这是因为我在上下文文件中用对ApplyStateChanges的方法调用重写了SaveChanges。。。
public override int SaveChanges()
{
this.ApplyStateChanges();
return base.SaveChanges();
}
我使用ApplyStateChanges来确保在添加对象图时,实体框架明确地知道对象是处于添加状态还是修改状态。关于我如何使用ApplyStateChanges的完整解释可以在这里找到。
这很有效(但需要注意的是!!)。。。如果您还使用CodeFirst迁移对数据库进行种子设定,那么上述方法将对种子方法中的AddOrUpdate()调用造成严重破坏。因此,在做任何其他事情之前,只需检查DBContext文件,并确保您没有以上述方式重写SaveChanges,否则您将在第二次运行update database命令时获得重复的数据,然后在第三次时根本无法工作,因为每个匹配项都有多行。
归根结底,您不需要在AddOrUpdate()中配置Id。。。这违背了简单和初始数据库种子设定的全部目的。它可以通过以下方式正常工作:
context.Students.AddOrUpdate(
p => p.StudentName,
new Student { StudentName = "Bill Peters" },
new Student { StudentName = "Jandra Nancy" },
new Student { StudentName = "Rowan Miller" },
new Student { StudentName = "James O'Dalley" },
只要我不调用ApplyStateChanges来覆盖上下文文件中的SaveChanges方法。希望这能有所帮助。
这些步骤对我来说很有效
- 删除表中的所有行
- 将增量标识重置为0。
DBCC CHECKIDENT (yourtablename, RESEED, 0)
(在Seed()
中指定的主键必须与数据库表中的主键匹配,这样它们才不会重复。) - 在"seed"方法中指定主键
- 多次运行
Seed()
方法,然后检查它们是否重复
我发现AddOrUpdate
可以很好地处理非ID字段。如果这对您有效:context.Halls.AddOrUpdate(h => h.Name, hall1, hall2, hall3)
您可能需要使用大厅名称,如"French_test_abc_100"、"German_test_abc_100"等。
这可以防止硬编码的测试数据在你测试应用程序时把事情搞砸。
如果对象(大厅)的id为0,则为插入。我认为您需要仔细检查大厅对象的id字段
您的id字段是Identity字段吗?我也遇到了同样的问题。当我从ID字段中删除Identity状态并将ID设置到数据库中时,问题就解决了。
这对我很有效,因为这些都是查找表,无论如何都不应该是身份字段。
我使用ID字段作为Identity/Key,并添加了不由服务器分配ID的属性。这为我解决了问题。
public class Hall
{
[Key]
[Required]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int Id {get; set;}
public string Name [get; set;}
}
Ciaren的回答是,下面重置ModelCreating上下文的代码帮助我解决了类似的问题。请确保将"ApplicationContext"更改为您的DbContext名称。
public class ApplicationContext : DbContext, IDbContext
{
public ApplicationContext() : base("ApplicationContext")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
Database.SetInitializer<ApplicationContext>(null);
base.OnModelCreating(modelBuilder);
}
}
我发现,为了实现这一点,当种子第一次运行时,标识位置应该是0。您可以使用重置
DBCC CHECKIDENT (tableName, RESEED, 0)
我认为您可能需要用Update-Database TargetMigration:0
和Update-Database
之类的东西来退出现有的数据库迁移(即从头开始数据库)。
实际上,您并不是删除现有的表或值,而是添加/更新这些值。为了得到你想要的结果,这是必须的。
您也可以这样做:
context.Halls.AddOrUpdate(new Hall[]{hall1,hall2, hall3});