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

ber*_*kyi 19 c# entity-framework upsert entity-framework-5

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

DbMigrationsConfiguration.Seed我试图用默认的虚拟数据填充数据库.为了完成这个任务,我使用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++;
}
Run Code Online (Sandbox Code Playgroud)

我尝试使用/省略Id字段以及使用Id/ Slugproperty作为更新选择器.

Update-Database运行时,Id字段被忽略,并且是由SQL Server自动生成的值和DB填充有重复; Slug选择器允许重复,并在后续运行中产生异常(Sequence contains more than one element).

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

Ger*_*old 28

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

(编辑)的

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

在你的情况下,你用作Slug标识符AddOrUpdate,它应该是唯一的(根据你的评论).我不清楚为什么不更新匹配Slugs的现有记录.

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

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

当"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]
Run Code Online (Sandbox Code Playgroud)

接下来(当匹配被发现并且UnitPrice不同时)

update [dbo].[Products]
set [UnitPrice] = 1.26
where ([ProductID] = 15)
Run Code Online (Sandbox Code Playgroud)

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

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

  • `p => new {p.ProductName,p.CategoryName}`@Jarvis (5认同)