Loại trừ Thuộc tính khi Cập nhật trong Khung thực thể


79

Tôi đã tìm kiếm một cách thích hợp để đánh dấu một thuộc tính KHÔNG được thay đổi khi cập nhật một mô hình trong MVC.

Ví dụ, hãy lấy mô hình nhỏ này:

class Model
{
    [Key]
    public Guid Id {get; set;}
    public Guid Token {get; set;}

    //... lots of properties here ...
}

thì phương thức chỉnh sửa mà MVC tạo ra trông như thế này:

[HttpPost]
public ActionResult Edit(Model model)
{
    if (ModelState.IsValid)
    {
        db.Entry(model).State = EntityState.Modified;
        db.SaveChanges();
        return RedirectToAction("Index");
    }
    return View(model);
}

bây giờ nếu Chế độ xem của tôi không chứa Mã thông báo, nó sẽ bị vô hiệu hóa thông qua chỉnh sửa đó.

Tôi đang tìm kiếm một cái gì đó như thế này:

db.Entry(model).State = EntityState.Modified;
db.Entry(model).Property(x => x.Token).State = PropertyState.Unmodified;
db.SaveChanges();

Cách tốt nhất cho đến nay tôi tìm thấy là bao gồm và đặt tất cả các thuộc tính tôi muốn đưa vào bằng tay, nhưng tôi thực sự chỉ muốn nói những thuộc tính nào cần được loại trừ.



Tôi không nghĩ nó trùng lặp: Tôi muốn luôn loại trừ việc cập nhật một thuộc tính nào đó. Người dùng không có khả năng thay đổi nó.
Manuel Schweigert

2
bạn có thể sử dụng mô hình xem và chỉ lập bản đồ những gì bạn muốn cập nhật.
điên cuồng

Tôi có thể. Có một số cách xung quanh vấn đề này. Nhưng tôi muốn biết nếu có cách tốt để làm điều này, và nếu có, nó hoạt động như thế nào. btw, "giải pháp" nhỏ nhất mà tôi phải làm lúc này là mở một giao dịch khác: using (var db2 = new DataContext()) model.Token = db2.Models.Find(model.Id).Token;Nhưng tôi cũng không hài lòng với giao dịch này.
Manuel Schweigert

3
Tôi thừa nhận rằng đây là cách "thích hợp" để làm điều đó, nhưng có những lý do để không làm theo cách đó trong trường hợp này: a) chi phí cao, b) không nhanh nhẹn, c) không thể xác định được / dễ xảy ra lỗi. Vì vậy, tôi từ chối tạo hai lớp giống nhau ngoại trừ một thuộc tính.
Manuel Schweigert

Câu trả lời:


156

chúng ta có thể sử dụng như thế này

 db.Entry(model).State = EntityState.Modified;
 db.Entry(model).Property(x => x.Token).IsModified = false;
 db.SaveChanges();

nó sẽ cập nhật nhưng không có thuộc tính Token


2
Nếu bạn đang sử dụng AddOrUpdatethì sao? - Làm thế nào để bạn biết để sử dụng EntityState.Modifiedhoặc EntityState.Added?
Jess

1
CẬP NHẬT: Để làm cho nó hoạt động trong EF 6 .. bạn cần phải db.Model.Attach (model);
Maxi

6
Chỉ cần lưu ý với những người khác rằng thứ tự ở đây rất quan trọng: nếu bạn đặt db.Entry(model).State = EntityState.Modified;sau khi thiết lập db.Entry(model).Property(x => x.Token).IsModified = false; , thuộc tính sẽ được cập nhật khi lưu.
akerra

1
Và điều này cũng sẽ xảy ra sau khi cập nhật các giá trị mô hình db.Entry (model) .CurrentValues.SetValues ​​(sourceModel); Nếu không, thì thuộc tính cũng được cập nhật khi lưu.
DotNet Fan

10

Tạo mô hình mới sẽ có một số thuộc tính giới hạn mà bạn muốn cập nhật.

Tức là nếu mô hình thực thể của bạn là:

public class User
{
    public int Id {get;set;}
    public string Name {get;set;}
    public bool Enabled {get;set;}
}

Bạn có thể tạo mô hình chế độ xem tùy chỉnh cho phép người dùng thay đổi Tên, nhưng không cho phép cờ Đã bật:

public class UserProfileModel
{
   public int Id {get;set;}
   public string Name {get;set;}
}

Khi bạn muốn cập nhật cơ sở dữ liệu, bạn làm như sau:

YourUpdateMethod(UserProfileModel model)
{
    using(YourContext ctx = new YourContext())
    { 
        User user = new User { Id = model.Id } ;   /// stub model, only has Id
        ctx.Users.Attach(user); /// track your stub model
        ctx.Entry(user).CurrentValues.SetValues(model); /// reflection
        ctx.SaveChanges();
    }
}

Khi bạn gọi phương thức này, bạn sẽ cập nhật Tên, nhưng thuộc tính Đã bật sẽ không thay đổi. Tôi đã sử dụng các mô hình đơn giản, nhưng tôi nghĩ bạn sẽ hiểu được cách sử dụng nó.


cảm ơn, điều này có vẻ tốt, nhưng đây vẫn là một danh sách trắng, không phải là một danh sách đen của các thuộc tính.
Manuel Schweigert

Bạn đang "đưa vào danh sách đen" bất kỳ thứ gì không có trong mô hình chế độ xem của bạn và điều này không yêu cầu mã hóa bổ sung, bạn chỉ đang sử dụng các tính năng EF. Hơn nữa, khi thực thể sơ khai được đính kèm với Đính kèm, tất cả các giá trị thuộc tính được đặt thành null / mặc định. Khi bạn sử dụng SetValues ​​(model), nếu thuộc tính mô hình chế độ xem của bạn là null, vì nó đã được đính kèm dưới dạng null, trình theo dõi thay đổi sẽ không đánh dấu nó là đã sửa đổi và do đó thuộc tính đó sẽ bị bỏ qua khỏi việc lưu. Thử nó.
Admir Tuzović

3
Toi khong muon tranh luan voi ban. Danh sách đen và danh sách trắng là các cách tiếp cận khác nhau với cùng một kết quả, cách tiếp cận của bạn là danh sách trắng. Như tôi đã nói trước đó, có nhiều cách, nhưng tôi đang hỏi về một cách cụ thể. Hơn nữa, giải pháp của bạn chỉ hoạt động với các kiểu nullable.
Manuel Schweigert

1. Đính kèm mô hình của bạn với Attach 2. lặp qua các thuộc tính với db.Entry (model) .Property ("Tên thuộc tính"). State = PropertyState.Modified; 3. Thực hiện SaveChanges.
Admir Tuzović

Những gì bạn làm là đặt toàn bộ mô hình được sửa đổi, sau đó đặt một số thuộc tính thành Không sửa đổi. Những gì tôi đã viết cho bạn là đính kèm mô hình trước (không có gì được đặt là sửa đổi), và sau đó đánh dấu các thuộc tính bạn muốn cập nhật là Đã sửa đổi, đó chính xác là những gì bạn muốn => danh sách trắng.
Admir Tuzović

8

Bất kỳ ai đang tìm cách đạt được điều này trên EF Core. Về cơ bản nó giống nhau nhưng IsModified của bạn cần phải được sau khi bạn thêm mô hình để được cập nhật.

db.Update(model);
db.Entry(model).Property(x => x.Token).IsModified = false;
db.SaveChanges();

Không biết tại sao, nhưng EF Core của tôi chỉ có phiên bản chuỗi và tôi không thể sử dụng lambda. Tôi đã sử dụng nameof thay thế nhưng đây là cách để đi. Cảm ơn
Cubelaster

Câu trả lời này là "Microsofty" Tôi biết nó sẽ hoạt động trước khi thử nghiệm nó. Trái ngược với nhận xét ở trên, cả chuỗi và phiên bản lambda đều cho kết quả giống nhau. có lẽ chúng tôi đang sử dụng các phiên bản khác nhau.
T3.0

3

Tôi đã thực hiện một cách dễ dàng để chỉnh sửa thuộc tính của các thực thể mà tôi sẽ chia sẻ với bạn. mã này sẽ chỉnh sửa thuộc tính Tên và Họ của thực thể:

    public void EditProfileInfo(ProfileInfo profileInfo)
    {
        using (var context = new TestContext())
        {
            context.EditEntity(profileInfo, TypeOfEditEntityProperty.Take, nameof(profileInfo.Name), nameof(profileInfo.Family));
        }
    }

Và mã này sẽ bỏ qua để chỉnh sửa thuộc tính Tên và Họ của thực thể và nó sẽ chỉnh sửa các thuộc tính khác:

    public void EditProfileInfo(ProfileInfo profileInfo)
    {
        using (var context = new TestContext())
        {
            context.EditEntity(profileInfo, TypeOfEditEntityProperty.Ignore, nameof(profileInfo.Name), nameof(profileInfo.Family));
        }
    }

Sử dụng tiện ích mở rộng này:

public static void EditEntity<TEntity>(this DbContext context, TEntity entity, TypeOfEditEntityProperty typeOfEditEntityProperty, params string[] properties)
   where TEntity : class
{
    var find = context.Set<TEntity>().Find(entity.GetType().GetProperty("Id").GetValue(entity, null));
    if (find == null)
        throw new Exception("id not found in database");
    if (typeOfEditEntityProperty == TypeOfEditEntityProperty.Ignore)
    {
        foreach (var item in entity.GetType().GetProperties(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.GetProperty))
        {
            if (!item.CanRead || !item.CanWrite)
                continue;
            if (properties.Contains(item.Name))
                continue;
            item.SetValue(find, item.GetValue(entity, null), null);
        }
    }
    else if (typeOfEditEntityProperty == TypeOfEditEntityProperty.Take)
    {
        foreach (var item in entity.GetType().GetProperties(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.GetProperty))
        {
            if (!item.CanRead || !item.CanWrite)
                continue;
            if (!properties.Contains(item.Name))
                continue;
            item.SetValue(find, item.GetValue(entity, null), null);
        }
    }
    else
    {
        foreach (var item in entity.GetType().GetProperties(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.GetProperty))
        {
            if (!item.CanRead || !item.CanWrite)
                continue;
            item.SetValue(find, item.GetValue(entity, null), null);
        }
    }
    context.SaveChanges();
}

public enum TypeOfEditEntityProperty
{
    Ignore,
    Take
}

1

Tôi đoán bạn không muốn thuộc tính bị thay đổi chỉ trong một số trường hợp, bởi vì nếu bạn không sử dụng nó không bao giờ trong ứng dụng của mình, chỉ cần xóa nó khỏi mô hình của bạn.

Trong trường hợp bạn chỉ muốn sử dụng nó trong một số trường hợp và tránh bị "vô hiệu hóa" trong trường hợp trên, bạn có thể thử:

  • Ẩn tham số trong dạng xem với HiddenFor:

    @Html.HiddenFor(m => m.Token)

Điều này sẽ làm cho giá trị ban đầu của bạn không bị sửa đổi và được chuyển trở lại bộ điều khiển.

Tải lại đối tượng của bạn trong bộ điều khiển từ của bạn DBSetvà chạy phương pháp này. Bạn có thể chỉ định cả danh sách trắng và danh sách đen các tham số sẽ hoặc không được cập nhật.


Bạn có thể tìm thấy một cuộc thảo luận tốt về TryUpdateModel tại đây: liên kết . Như đã nói trong câu trả lời đã được xác thực, tốt hơn là bạn nên tạo các mô hình xem để khớp chính xác các thuộc tính cần thiết trong mỗi chế độ xem.
Jaime

1
Việc sử dụng @Html.HiddenForsẽ ghi giá trị vào HTML của chế độ xem và cho phép người dùng sử dụng phần tử kiểm tra trong trình duyệt của họ và sửa đổi nó. Sau khi họ làm điều đó, nó vẫn được chuyển đến bộ điều khiển nhưng với một giá trị khác và sẽ được cập nhật. Tôi chỉ thử nghiệm nó.
duckwizzle
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.