Cập nhật hàng nếu nó tồn tại khác Chèn logic với khung thực thể


125

Có ai có đề xuất về cách hiệu quả nhất để triển khai "hàng cập nhật nếu hàng tồn tại khác chèn" logic bằng cách sử dụng khung thực thể không?

+2

Đây là cái gì đó nên được thực hiện ở cấp cơ sở dữ liệu, trong một thủ tục lưu trữ. Nếu không, bạn sẽ phải bọc phát hiện/cập nhật/chèn trong một giao dịch. 06 apr. 112011-04-06 09:12:11

+1

@Stephen: Điều này, trên thực tế, là những gì tôi đã kết thúc. Cảm ơn. 06 apr. 112011-04-06 14:18:43

  0

Jonathan, câu hỏi của bạn rất hữu ích đối với tôi. Tại sao bạn chuyển sang một thủ tục được lưu trữ? 15 jan. 142014-01-15 09:54:35

+2

@Anar: Nó dễ dàng hơn và tôi mong đợi hiệu quả hơn nhiều. 15 jan. 142014-01-15 16:58:03

  0

Bạn có phải viết một thủ tục lưu trữ cho mỗi bảng? 03 jan. 182018-01-03 05:00:58

130

Nếu bạn đang làm việc với đối tượng đính kèm (đối tượng được nạp từ cùng một ví dụ của ngữ cảnh) bạn chỉ có thể sử dụng:

if (context.ObjectStateManager.GetObjectStateEntry(myEntity).State == EntityState.Detached) 
{ 
    context.MyEntities.AddObject(myEntity); 
} 

// Attached object tracks modifications automatically 

context.SaveChanges(); 

Nếu bạn có thể sử dụng bất kỳ kiến ​​thức về chủ chốt của đối tượng bạn có thể sử dụng một cái gì đó như thế này :

if (myEntity.Id != 0) 
{ 
    context.MyEntities.Attach(myEntity); 
    context.ObjectStateManager.ChangeObjectState(myEntity, EntityState.Modified); 
} 
else 
{ 
    context.MyEntities.AddObject(myEntity); 
} 

context.SaveChanges(); 

Nếu bạn không thể quyết định sự tồn tại của đối tượng bằng cách Id của nó bạn phải exectue truy vấn tìm kiếm:

var id = myEntity.Id; 
if (context.MyEntities.Any(e => e.Id == id)) 
{ 
    context.MyEntities.Attach(myEntity); 
    context.ObjectStateManager.ChangeObjectState(myEntity, EntityState.Modified); 
} 
else 
{ 
    context.MyEntities.AddObject(myEntity); 
} 

context.SaveChanges(); 
  0

Cảm ơn. Có vẻ như những gì tôi cần. Tôi có thể hỏi bạn một câu hỏi đã làm phiền tôi một thời gian không? Thông thường, tôi đặt ngữ cảnh của mình trong một khối 'sử dụng' ngắn. Liệu có được rời khỏi bối cảnh trong bộ nhớ một lúc không? Ví dụ, trong suốt cuộc đời của một hình thức Windows?Tôi thường thử và dọn dẹp các đối tượng cơ sở dữ liệu để đảm bảo tải tối thiểu trên cơ sở dữ liệu. Không có vấn đề gì đang chờ đợi để phá hủy bối cảnh EF của tôi? 06 apr. 112011-04-06 02:16:58

  0

Kiểm tra điều này: http://stackoverflow.com/questions/3653009/entity-framework-and-connection-pooling/3653392#3653392 ngữ cảnh đối tượng nên sống càng ngắn càng tốt nhưng trong trường hợp winforms hoặc wpf điều này có nghĩa là ngữ cảnh là sống miễn là người trình bày. Câu hỏi được liên kết có chứa liên kết đến bài viết msdn về việc sử dụng phiên nhibernate trong winforms. Cách tiếp cận tương tự có thể được sử dụng cho ngữ cảnh. 06 apr. 112011-04-06 05:34:14

  0

Nhưng nếu tôi cần phải làm điều này với một danh sách các đối tượng ... trong cơ sở dữ liệu của tôi có một danh sách các hàng với cùng một id và tôi muốn thay thế nếu thew tồn tại hoặc chèn nếu họ không .. làm thế nào tôi làm điều đó? cảm ơn! 14 oct. 112011-10-14 11:45:52

+1

Câu trả lời này LOOKS tuyệt vời, nhưng tôi đang chạy vào vấn đề này khi cập nhật: Một đối tượng có cùng khóa đã tồn tại trong ObjectStateManager. ObjectStateManager không thể theo dõi nhiều đối tượng bằng cùng một khóa. 29 nov. 122012-11-29 21:58:14

+1

Có vẻ như tôi chỉ gặp vấn đề với tìm nạp đối tượng hiện có để lấy khóa của nó trước khi thực hiện cập nhật; tách ra rằng đối tượng tra cứu đầu tiên đã giúp sửa chữa nó. 29 nov. 122012-11-29 22:15:28

  0

@JohnZ để bạn tách ra rồi được đính kèm, tôi có đúng không? Đó có phải là hành vi đắt tiền khôn ngoan không? Tôi gặp vấn đề tương tự. 15 jan. 142014-01-15 09:56:36

  0

@ Có, tôi đã tìm nạp để lấy phiên bản cơ sở dữ liệu của đối tượng và rút ra một số giá trị của nó, sau đó tôi tách phiên bản đó, sau đó gắn đối tượng được truyền vào phương thức, thay đổi trạng thái đối tượng và lưu. Và vâng, nó khá tốn kém, bởi vì bạn chọn một từ cơ sở dữ liệu cho mỗi mục bạn đang cập nhật (nhưng chỉ trong cách tôi làm điều đó). 15 jan. 142014-01-15 15:49:03

  0

@JohnZ Tôi gặp vấn đề lạ khi tôi lấy một bản ghi hiện có, thay đổi các giá trị trường của nó, sau đó khi tôi gọi 'SaveChanges()' bằng cách nào đó không gửi các giá trị đã thay đổi này đến cơ sở dữ liệu. Thay vào đó, nó sẽ gửi đối tượng với các giá trị trường không thay đổi. Tôi có cảm giác bạn đã biết giải pháp cho vấn đề này. Xin hãy xem, tôi sẽ rất nhiều đánh giá cao nó. http://stackoverflow.com/questions/21088398/cannot-update-entity-framework-model 15 jan. 142014-01-15 15:56:00

  0

Nó sẽ không thất bại nếu các quá trình bên ngoài khác có thể sửa đổi/thêm cùng một bản ghi ngay sau khi bạn đã truy xuất dbcontext (ở trạng thái kết nối) .. 24 jun. 152015-06-24 07:39:53

  0

@LadislavMrnka cảm ơn bạn đời - sự thay đổi ở trên bây giờ mà chúng ta có changetracker trong ef6 như thế nào? 14 dec. 162016-12-14 05:05:45

  0

hi cảm ơn bạn - một câu hỏi khác cho ví dụ cuối cùng được đưa ra: nó sẽ nhận ra những thay đổi trong các đối tượng con và cập nhật các đối tượng đó? 20 dec. 162016-12-20 05:01:06

  0

@BKSpurgeon: Không. Bạn cần phát hiện các thay đổi đối với các đối tượng con cũng như khi làm việc với cây thực thể tách rời. 24 dec. 162016-12-24 08:45:44


7

Nếu bạn biết rằng bạn đang sử dụng bối cảnh tương tự và không tách bất kỳ tổ chức, bạn có thể tạo ra một phiên bản generic như thế này:

public void InsertOrUpdate<T>(T entity, DbContext db) where T : class 
{ 
    if (db.Entry(entity).State == EntityState.Detached) 
     db.Set<T>().Add(entity); 

    // If an immediate save is needed, can be slow though 
    // if iterating through many entities: 
    db.SaveChanges(); 
} 

db lon dĩ nhiên là một trường lớp, hoặc phương pháp có thể được thực hiện tĩnh và phần mở rộng, nhưng đây là những điều cơ bản.


25

Tính đến Entity Framework 4.3, có một phương pháp AddOrUpdate tại namespace System.Data.Entity.Migrations:

public static void AddOrUpdate<TEntity>(
    this IDbSet<TEntity> set, 
    params TEntity[] entities 
) 
where TEntity : class 

mà theo các doc:

Thêm hoặc cập nhật các đối tượng bằng phím khi SaveChanges được gọi. Tương đương cho một hoạt động "upsert" từ thuật ngữ cơ sở dữ liệu. Phương pháp này có thể là hữu ích khi tạo dữ liệu bằng cách sử dụng Di chuyển.


Để trả lời các comment by @Smashing1978, tôi sẽ dán phần có liên quan từ liên kết được cung cấp bởi @Colin

Công việc của AddOrUpdate là để đảm bảo rằng bạn không tạo bản sao khi bạn gieo rắc dữ liệu trong quá trình phát triển.

Đầu tiên, nó sẽ thực hiện truy vấn trong cơ sở dữ liệu của bạn để tìm bản ghi trong đó bất kỳ thứ gì bạn cung cấp dưới dạng khóa (tham số đầu tiên) khớp với giá trị cột được ánh xạ được cung cấp trong AddOrUpdate. Vì vậy, điều này là một chút lỏng lẻo-ngỗng cho phù hợp nhưng hoàn toàn tốt cho hạt giống dữ liệu thời gian thiết kế.

Quan trọng hơn, nếu tìm thấy kết quả trùng khớp thì bản cập nhật sẽ cập nhật tất cả và hủy bất kỳ nội dung nào không có trong AddOrUpdate của bạn.

Điều đó nói rằng, tôi có tình huống khi tôi lấy dữ liệu từ dịch vụ bên ngoài và chèn hoặc cập nhật giá trị hiện tại bằng khóa chính (và dữ liệu cục bộ của tôi cho người tiêu dùng là chỉ đọc) - đang sử dụng AddOrUpdate hơn 6 tháng nay và cho đến nay vẫn không có vấn đề gì.

+6

Không gian tên System.Data.Entity.Migrations chứa các lớp liên quan đến di chuyển dựa trên mã và cấu hình của chúng. Có lý do nào khiến chúng ta không nên sử dụng điều này trong kho của chúng tôi cho thực thể không di trú AddOrUpdates không? 24 feb. 152015-02-24 20:58:55

+5

Hãy quan tâm đến phương thức AddOrUpdate: http://thedatafarm.com/data-access/take-care-with-ef-4-3-addorupdate-method/ 14 may. 152015-05-14 08:43:46


3

Câu trả lời của Ladislav là gần nhưng tôi phải thực hiện một vài sửa đổi để làm việc này trong EF6 (cơ sở dữ liệu đầu tiên).Tôi mở rộng bối cảnh dữ liệu của tôi với trên AddOrUpdate phương pháp của tôi và cho đến nay điều này dường như được làm việc tốt với các đối tượng tách ra:

using System.Data.Entity; 

[....] 

public partial class MyDBEntities { 

    public void AddOrUpdate(MyDBEntities ctx, DbSet set, Object obj, long ID) { 
     if (ID != 0) { 
      set.Attach(obj); 
      ctx.Entry(obj).State = EntityState.Modified; 
     } 
     else { 
      set.Add(obj); 
     } 
    } 
[....] 

3

Sự kỳ diệu xảy ra khi gọi SaveChanges() và phụ thuộc vào hiện tại EntityState. Nếu thực thể có một EntityState.Added, nó sẽ được thêm vào cơ sở dữ liệu, nếu nó có một EntityState.Modified, nó sẽ được cập nhật trong cơ sở dữ liệu. Vì vậy, bạn có thể thực hiện một phương pháp InsertOrUpdate() như sau:

public void InsertOrUpdate(Blog blog) 
{ 
    using (var context = new BloggingContext()) 
    { 
     context.Entry(blog).State = blog.BlogId == 0 ? 
            EntityState.Added : 
            EntityState.Modified; 

     context.SaveChanges(); 
    } 
} 

More about EntityState

Nếu bạn không thể kiểm tra trên Id = 0 để xác định xem đó là một thực thể mới hay không, kiểm tra answer of Ladislav Mrnka.


1

Theo tôi, điều đáng nói là với số mới được phát hành EntityGraphOperations for Entity Framework Code First bạn có thể tự mình viết một số mã lặp lại để xác định trạng thái của tất cả các thực thể trong biểu đồ. Tôi là tác giả của sản phẩm này. Và tôi đã xuất bản nó trong github, code-project (bao gồm bản trình diễn từng bước và dự án mẫu sẵn sàng để tải xuống)nuget.

Nó sẽ tự động đặt trạng thái của các đối tượng thành Added hoặc Modified. Và bạn sẽ tự chọn các thực thể nào sẽ bị xóa nếu nó không còn tồn tại nữa.

Ví dụ:

Hãy nói rằng tôi có nhận được một đối tượng Person. Person có thể có nhiều điện thoại, một Tài liệu và có thể có vợ/chồng.

public class Person 
{ 
    public int Id { get; set; } 
    public string FirstName { get; set; } 
    public string LastName { get; set; } 
    public string MiddleName { get; set; } 
    public int Age { get; set; } 
    public int DocumentId {get; set;} 

    public virtual ICollection<Phone> Phones { get; set; } 
    public virtual Document Document { get; set; } 
    public virtual PersonSpouse PersonSpouse { get; set; } 
} 

Tôi muốn xác định trạng thái của tất cả các thực thể được bao gồm trong biểu đồ.

context.InsertOrUpdateGraph(person) 
     .After(entity => 
     { 
      // Delete missing phones. 
      entity.HasCollection(p => p.Phones) 
       .DeleteMissingEntities(); 

      // Delete if spouse is not exist anymore. 
      entity.HasNavigationalProperty(m => m.PersonSpouse) 
        .DeleteIfNull(); 
     }); 

Cũng như bạn biết các thuộc tính khóa duy nhất có thể đóng vai trò trong khi xác định trạng thái của thực thể Điện thoại. Với những mục đích đặc biệt như vậy, chúng tôi có lớp học ExtendedEntityTypeConfiguration<>, được kế thừa từ EntityTypeConfiguration<>. Nếu chúng tôi muốn sử dụng các cấu hình đặc biệt như vậy thì chúng tôi phải kế thừa các lớp ánh xạ của chúng tôi từ ExtendedEntityTypeConfiguration<>, thay vì EntityTypeConfiguration<>. Ví dụ:

public class PhoneMap: ExtendedEntityTypeConfiguration<Phone> 
    { 
     public PhoneMap() 
     { 
      // Primary Key 
      this.HasKey(m => m.Id); 
       … 
      // Unique keys 
      this.HasUniqueKey(m => new { m.Prefix, m.Digits }); 
     } 
    } 

Đó là tất cả.


2

Chèn khác cập nhật cả

public void InsertUpdateData() 
{ 
//Here TestEntities is the class which is given from "Save entity connection setting in web.config" 
TestEntities context = new TestEntities(); 

var query = from data in context.Employee 
      orderby data.name 
      select data; 

foreach (Employee details in query) 
{ 
    if (details.id == 1) 
    { 
     //Assign the new values to name whose id is 1 
     details.name = "Sanjay"; 
     details. Surname="Desai"; 
     details.address=" Desiwadi"; 
    } 
    else if(query==null) 
    { 
    details.name="Sharad"; 
    details.surname=" Chougale "; 
    details.address=" Gargoti"; 
} 

//Save the changes back to database. 
context.SaveChanges(); 
} 
  0

Tôi đã sử dụng cách tiếp cận này nhưng đã kiểm tra (sau lần đầu tiên hoặc mặc định) if (truy vấn == null) 27 jan. 182018-01-27 16:40:00