Có ai có đề xuất về cách hiệu quả nhất để triển khai logic "cập nhật hàng nếu nó tồn tại khác" bằng cách sử dụng Entity Framework không?
Có ai có đề xuất về cách hiệu quả nhất để triển khai logic "cập nhật hàng nếu nó tồn tại khác" bằng cách sử dụng Entity Framework không?
Câu trả lời:
Nếu bạn đang làm việc với đối tượng đính kèm (đối tượng được tải từ cùng một thể hiện của bối cảnh), bạn có thể chỉ cần 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 nào về khóa 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 Id của nó, bạn phải thực hiện truy vấn tra cứu:
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();
using
khối ngắn . Có thể để lại bối cảnh trong bộ nhớ trong một thời gian? Ví dụ, trong vòng đời của một mẫu Windows? Tôi thường cố gắng và dọn sạch 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 đề chờ đợi để phá hủy bối cảnh EF của tôi?
Trong Entity Framework 4.3, có một AddOrUpdate
phương thức tại không gian tên System.Data.Entity.Migrations
:
public static void AddOrUpdate<TEntity>(
this IDbSet<TEntity> set,
params TEntity[] entities
)
where TEntity : class
mà theo tài liệu :
Thêm hoặc cập nhật các thực thể theo khóa khi SaveChanges được gọi. Tương đương với một hoạt động "nâng cấp" từ thuật ngữ cơ sở dữ liệu. Phương pháp này có thể hữu ích khi gieo dữ liệu bằng cách sử dụng Di chuyển.
Để trả lời nhận xét của @ Smashing1978 , tôi sẽ dán các 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 các bản sao khi bạn tạo dữ liệu trong quá trình phát triển.
Đầu tiên, nó sẽ thực thi một truy vấn trong cơ sở dữ liệu của bạn để tìm một bản ghi trong đó bất cứ thứ gì bạn cung cấp làm khóa (tham số đầu tiên) khớp với giá trị cột (hoặc giá trị) được ánh xạ được cung cấp trong AddOrUpdate. Vì vậy, đây là một chút lỏng lẻo để phù hợp nhưng hoàn toàn tốt cho dữ liệu thời gian thiết kế gieo hạt.
Quan trọng hơn, nếu một trận đấu được tìm thấy thì bản cập nhật sẽ cập nhật tất cả và loại bỏ mọi thứ không có trong AddOrUpdate của bạn.
Điều đó nói rằng, tôi có một tình huống tôi đang lấy dữ liệu từ một dịch vụ bên ngoài và chèn hoặc cập nhật các giá trị hiện có 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) - đã được sử dụng AddOrUpdate
trong sản xuất hơn 6 tháng nay và vì vậy không có vấn đề gì
Phép thuật 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 có 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 InsertOrUpdate()
phương pháp 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();
}
}
Nếu bạn không thể kiểm tra Id = 0
để xác định xem đó có phải là một thực thể mới hay không, hãy kiểm tra câu trả lời của Ladislav Mrnka .
Nếu bạn biết rằng bạn đang sử dụng cùng một bối cảnh và không tách rời bất kỳ thực thể nào, bạn có thể tạo một phiên bản chung 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
Tất nhiên có thể là một trường lớp hoặc phương thức có thể được tạo tĩnh và mở rộng, nhưng đây là điều cơ bản.
Câu trả lời của Ladislav rất gần nhưng tôi đã phải thực hiện một vài sửa đổi để làm cho nó hoạt động trong EF6 (cơ sở dữ liệu đầu tiên). Tôi đã mở rộng bối cảnh dữ liệu của mình bằng phương thức AddOrUpdate và cho đến nay nó dường như hoạt động tốt với các đối tượng tách rời:
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);
}
}
[....]
Theo tôi, điều đáng nói là với EntityGraphOperations cho Entity Framework Code Trước tiên, bạn có thể tự cứu mình khỏi việc viết một số mã lặp đi 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 trình diễn từng bước và một dự án mẫu đã sẵn sàng để tải xuống) và nuget .
Nó sẽ tự động đặt trạng thái của các thực thể thành Added
hoặc Modified
. Và bạn sẽ chọn thủ công những thực thể phải 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ó một Person
đối tượng. Person
có thể có nhiều điện thoại, một Tài liệu và có thể có người phối ngẫu.
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();
});
Ngoài ra, 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. Đối với các mục đích đặc biệt như vậy, chúng tôi có ExtendedEntityTypeConfiguration<>
lớp, kế thừa từ EntityTypeConfiguration<>
. Nếu chúng ta muốn sử dụng các cấu hình đặc biệt như vậy thì chúng ta phải kế thừa các lớp ánh xạ từ đó ExtendedEntityTypeConfiguration<>
chứ không phải 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ả.
Chèn khác cập nhật cả hai
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();
}
Kiểm tra hàng hiện có với Any.
public static void insertOrUpdateCustomer(Customer customer)
{
using (var db = getDb())
{
db.Entry(customer).State = !db.Customer.Any(f => f.CustomerId == customer.CustomerId) ? EntityState.Added : EntityState.Modified;
db.SaveChanges();
}
}
Thay thế cho câu trả lời @LadislavMrnka. Điều này nếu cho Entity Framework 6.2.0.
Nếu bạn có một mục cụ thể DbSet
và một mục cần được cập nhật hoặc tạo:
var name = getNameFromService();
var current = _dbContext.Names.Find(name.BusinessSystemId, name.NameNo);
if (current == null)
{
_dbContext.Names.Add(name);
}
else
{
_dbContext.Entry(current).CurrentValues.SetValues(name);
}
_dbContext.SaveChanges();
Tuy nhiên, điều này cũng có thể được sử dụng cho một chung DbSet
với một khóa chính duy nhất hoặc một khóa chính tổng hợp.
var allNames = NameApiService.GetAllNames();
GenericAddOrUpdate(allNames, "BusinessSystemId", "NameNo");
public virtual void GenericAddOrUpdate<T>(IEnumerable<T> values, params string[] keyValues) where T : class
{
foreach (var value in values)
{
try
{
var keyList = new List<object>();
//Get key values from T entity based on keyValues property
foreach (var keyValue in keyValues)
{
var propertyInfo = value.GetType().GetProperty(keyValue);
var propertyValue = propertyInfo.GetValue(value);
keyList.Add(propertyValue);
}
GenericAddOrUpdateDbSet(keyList, value);
//Only use this when debugging to catch save exceptions
//_dbContext.SaveChanges();
}
catch
{
throw;
}
}
_dbContext.SaveChanges();
}
public virtual void GenericAddOrUpdateDbSet<T>(List<object> keyList, T value) where T : class
{
//Get a DbSet of T type
var someDbSet = Set(typeof(T));
//Check if any value exists with the key values
var current = someDbSet.Find(keyList.ToArray());
if (current == null)
{
someDbSet.Add(value);
}
else
{
Entry(current).CurrentValues.SetValues(value);
}
}
Đã sửa
public static void InsertOrUpdateRange<T, T2>(this T entity, List<T2> updateEntity)
where T : class
where T2 : class
{
foreach(var e in updateEntity)
{
context.Set<T2>().InsertOrUpdate(e);
}
}
public static void InsertOrUpdate<T, T2>(this T entity, T2 updateEntity)
where T : class
where T2 : class
{
if (context.Entry(updateEntity).State == EntityState.Detached)
{
if (context.Set<T2>().Any(t => t == updateEntity))
{
context.Set<T2>().Update(updateEntity);
}
else
{
context.Set<T2>().Add(updateEntity);
}
}
context.SaveChanges();
}