2012-12-08 19 views
17

Tôi đang sử dụng thiết lập EF5 dựa trên DBContext đầu tiên.AddOrUpdate hoạt động không như dự kiến ​​và tạo ra các bản sao

Trong DbMigrationsConfiguration.Seed Tôi đang cố gắng điền DB với dữ liệu giả định mặc định. Để thực hiện tác vụ này, tôi sử dụng phương thức DbSet.AddOrUpdate.

Mã đơn giản nhất để minh họa cho mục tiêu của tôi:

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++; 
} 

Tôi đã cố gắng sử dụng/bỏ qua Id lĩnh vực cũng như sử dụng Id/Slug tài sản như cập nhật selector.

khi Update-Database được chạy, trường Id bị bỏ qua và giá trị được tạo tự động bởi SQL Server và DB được lấp đầy với các bản sao; Bộ chọn Slug cho phép trùng lặp và các lần chạy tiếp theo tạo ra ngoại lệ (Sequence contains more than one element).

Phương thức AddOrUpdate có hoạt động theo cách này không? Tôi có nên thực hiện upsert bằng tay?

Trả lời

26

đầu tiên (không có câu trả lời nào), AddOrUpdate có thể được gọi với một mảng của các đối tượng mới, vì vậy bạn chỉ có thể tạo ra một loạt các loại City[] và gọi context.CitySet.AddOrUpdate(cc => cc.Id, cityArray); một lần.

(chỉnh sửa)

Thứ hai, AddOrUpdate sử dụng biểu thức định danh (cc => cc.Id) để tìm các thành phố với cùng Id như những người trong mảng. Các thành phố này sẽ được cập nhật. Các thành phố khác trong mảng sẽ được chèn vào, nhưng giá trị Id của chúng sẽ được tạo ra bởi cơ sở dữ liệu, bởi vì Id là một cột nhận dạng. Nó không thể được thiết lập bởi một câu lệnh chèn. (Trừ khi bạn đặt Nhận dạng Chèn vào). Vì vậy, khi sử dụng AddOrUpdate cho các bảng có cột nhận dạng, bạn nên tìm một cách khác để xác định các bản ghi vì các giá trị Id của các bản ghi hiện có là không thể đoán trước.

Trong trường hợp bạn đã sử dụng Slug làm số nhận dạng cho AddOrUpdate, số này phải là duy nhất (theo nhận xét của bạn). Nó không phải là rõ ràng với tôi tại sao mà không cập nhật hồ sơ hiện tại với phù hợp với Slug s.

tôi thiết lập một thử nghiệm nhỏ: thêm hoặc cập nhật một thực thể với một Id (iedntity) và một tên duy nhất:

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

Khi "Prod1" không phải là có được nêu ra, nó được chèn vào (bỏ qua Id 999).
Nếu có và UnitPrice là khác nhau, nó được cập nhật.

Nhìn vào các truy vấn phát ra tôi thấy rằng EF đang tìm kiếm một kỷ lục độc đáo theo tên:

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] 

Và tiếp theo (khi kết hợp được tìm thấy và UnitPrice là khác nhau)

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

này cho thấy EF đã tìm thấy một bản ghi và giờ đây sử dụng trường khóa để thực hiện cập nhật.

Tôi hy vọng rằng việc xem ví dụ này sẽ làm sáng tỏ tình trạng của bạn. Có lẽ bạn cũng nên theo dõi các câu lệnh sql và xem có bất cứ điều gì bất ngờ xảy ra ở đó hay không.

+0

Gert, cảm ơn bạn đã cố giúp đỡ! Trên thực tế, Thành phố đã được đưa ra 'id = j' trong khoảng từ 0 đến 9. Bạn chỉ bị lừa bởi các tên biến tương tự. Tiếp theo, tôi đã đề cập đến hiệu quả của việc sử dụng Name (trong trường hợp của tôi, Slug, vì nó thực sự độc đáo) - nó cho phép nhân đôi vào DB. Việc truyền tất cả các đối tượng dưới dạng mảng không mang lại tiến bộ nào. – berezovskyi

+0

Cảm ơn bạn đã cập nhật câu trả lời của mình. Tôi không thể xác minh nó nữa. Nếu bất cứ ai từ cộng đồng sẽ bình luận theo câu trả lời của bạn rằng nó đã giúp anh ta/cô ấy, tôi sẽ đánh dấu câu trả lời của bạn là được chấp nhận. Xin lỗi vì mất quá nhiều thời gian. – berezovskyi

+1

Bất kỳ ý tưởng nào biểu thức nhận diện sẽ trông như thế nào nếu nó yêu cầu hai trường để nhận dạng duy nhất bản ghi? Tôi đang nghĩ p => p.ProductName, p.CategoryName (nhưng tất nhiên là không hiệu quả). – Jarvis

1
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(); 
Các vấn đề liên quan