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();