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

时间:2012-12-08 17:17:40

标签: c# entity-framework entity-framework-5 upsert

我正在使用基于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++;
}

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

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

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

2 个答案:

答案 0 :(得分:28)

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

(编辑)的

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

在您的情况下,您使用AddOrUpdate作为Slug的标识符,该标识符应该是唯一的(根据您的评论)。我不清楚为什么不更新匹配var n = new Product { ProductID = 999, ProductName = "Prod1", UnitPrice = 1.25 }; Products.AddOrUpdate(p => p.ProductName, n); SaveChanges(); s的现有记录。

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

UnitPrice

当“Prod1”尚未出现时,会插入(忽略Id 999) 如果是,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] 不同,则会更新。

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

UnitPrice

接下来(当找到匹配且update [dbo].[Products] set [UnitPrice] = 1.26 where ([ProductID] = 15) 不同时)

{{1}}

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

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

答案 1 :(得分:0)

var paidOutType = new List<PaidOutType>
                {
                    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();