AddOrUpdate工作不符合预期,并产生重复

我正在使用基于Code-First DBContext的EF5设置。

DbMigrationsConfiguration.Seed我试图用默认的虚拟数据填充DB。 要完成此任务,我使用DbSet.AddOrUpdate方法。

最简单的代码来说明我的目标:

 j = 0; var cities = new[] { "Berlin", "Vienna", "London", "Bristol", "Rome", "Stockholm", "Oslo", "Helsinki", "Amsterdam", "Dublin" }; var cityObjects = new City[cities.Length]; foreach (string c in cities) { int id = r.NextDouble() > 0.5 ? 0 : 1; var city = new City { Id = j, Name = c, Slug = c.ToLowerInvariant(), Region = regions[id], RegionId = regions[id].Id, Reviewed = true }; context.CitySet.AddOrUpdate(cc => cc.Id, city); cityObjects[j] = city; j++; } 

我尝试使用/省略Id字段以及使用Id / Slug属性作为更新选择器。

运行Update-Database时,将忽略Id字段,SQL Server会自动生成该值,并且DB将填充重复项; Slug选择器允许重复,并在后续运行中产生exception( Sequence contains more than one element )。

AddOrUpdate方法是否打算以这种方式工作? 我应该手动执行upsert吗?

首先(尚未回答),可以使用新对象数组调用AddOrUpdate ,因此您只需创建City[]类型的数组并调用context.CitySet.AddOrUpdate(cc => cc.Id, cityArray); 一旦。

(编辑)的

其次, AddOrUpdate使用标识符表达式( cc => cc.Id )来查找与数组中的Id相同的城市。 这些城市将会更新。 将插入arrays中的其他城市,但它们的Id值将由数据库生成,因为Id是标识列。 它不能通过insert语句设置。 (除非您设置标识插入)。 因此,当对具有标识列的表使用AddOrUpdate ,您应该找到另一种标识记录的方法,因为现有记录的Id值是不可预测的。

在您的情况下,您使用Slug作为AddOrUpdate标识符,该标识符应该是唯一的(根据您的注释)。 我不清楚为什么不用匹配的Slug更新现有记录。

我设置了一个小测试:添加或更新具有Id(iedntity)和唯一名称的实体:

 var n = new Product { ProductID = 999, ProductName = "Prod1", UnitPrice = 1.25 }; Products.AddOrUpdate(p => p.ProductName, n); SaveChanges(); 

当“Prod1”还没有时,它被插入(忽略Id 999)。
如果是,且UnitPrice不同,则会更新。

查看发出的查询,我看到EF正在寻找名称的唯一记录:

 SELECT TOP (2) [Extent1].[ProductID] AS [ProductID], [Extent1].[ProductName] AS [ProductName], [Extent1].[UnitPrice] AS [UnitPrice] FROM [dbo].[Products] AS [Extent1] WHERE N'Prod1' = [Extent1].[ProductName] 

接下来(当找到匹配且UnitPrice不同时)

 update [dbo].[Products] set [UnitPrice] = 1.26 where ([ProductID] = 15) 

这表明EF找到了一条记录,现在使用密钥字段进行更新。

我希望看到这个例子可以说明你的情况。 也许你应该监视sql语句,看看是否有意外的事情发生。

 var paidOutType = new List { new PaidOutType { PaidOutTypeID = 1, Code = "001", Description = "PAID OUT 1", PType = "1", Amount = 0, IsSalesSummery = true,DayFrom=1,DayTo=31 }, new PaidOutType { PaidOutTypeID = 2, Code = "002", Description = "PAID OUT 2", PType = "1", Amount = 0, IsSalesSummery = true,DayFrom=1,DayTo=31 }, new PaidOutType { PaidOutTypeID = 3, Code = "002", Description = "PAID OUT 3", PType = "1", Amount = 0, IsSalesSummery = true,DayFrom=1,DayTo=31 }, }; paidOutType.ForEach(u => smartPOSContext.PaidOutType.AddOrUpdate(u)); smartPOSContext.SaveChanges();