Entity Framework에서 업데이트시 속성 제외


79

MVC에서 모델을 업데이트 할 때 속성이 변경되지 않도록 표시하는 적절한 방법을 찾고있었습니다.

예를 들어 다음과 같은 작은 모델을 살펴 보겠습니다.

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

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

MVC가 생성하는 편집 방법은 다음과 같습니다.

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

이제 내 뷰에 토큰이 포함되어 있지 않으면 해당 편집을 통해 무효화됩니다.

나는 다음과 같은 것을 찾고 있습니다.

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

지금까지 내가 찾은 가장 좋은 방법은 포함하고 원하는 모든 속성을 손으로 설정하는 것이지만 실제로는 제외 할 속성 만 말하고 싶습니다.



중복이라고 생각하지 않습니다. 항상 특정 속성이 업데이트되지 않도록 제외하고 싶습니다 . 사용자는이를 변경할 수 없어야합니다.
— Manuel Schweigert

2
뷰 모델을 사용하고 업데이트하려는 것을 매핑 할 수 있습니다.
— frennky

할 수 있습니다. 몇 가지 방법이 있습니다 주위에 이 문제가. 그러나 나는 이것을하는 좋은 방법이 있는지, 만약 있다면 그것이 어떻게 작동하는지 알고 싶습니다. btw,이 ATM에 대한 가장 작은 "솔루션"은 다른 트랜잭션을 여는 것입니다. using (var db2 = new DataContext()) model.Token = db2.Models.Find(model.Id).Token;하지만 이것도 만족스럽지 않습니다.
— Manuel Schweigert

3
나는 이것이 "적절한"방법임을 인정하지만,이 경우에는 그렇게하지 않는 이유가 있습니다 : a) 오버 헤드, b) 민첩하지 않음, c) 유지 보수 불가능 / 오류 발생 가능성. 네, 하나의 속성을 제외하고는 두 개의 동일한 클래스를 만드는 것을 거부합니다.
— Manuel Schweigert 2012 년

답변:


156

우리는 이렇게 사용할 수 있습니다

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

업데이트되지만 Token 속성은 없습니다.


2
당신이 사용한다면 AddOrUpdate? - EntityState.Modified또는 사용 방법을 어떻게 압 EntityState.Added니까?
— Jess

1
업데이트 : EF 6.에서 작동하도록하려면 db.Model.Attach (model);
— Maxi

6
여기서 순서가 중요하다는 점을 다른 사람들에게 참고하십시오. 설정 db.Entry(model).State = EntityState.Modified;한 후에 설정 db.Entry(model).Property(x => x.Token).IsModified = false; 하면 속성이 저장시 업데이트됩니다.
— akerra

1
또한 이것은 모델 값을 업데이트 한 이후 여야합니다. db.Entry (model) .CurrentValues.SetValues ​​(sourceModel); 그렇지 않은 경우 저장시 속성도 업데이트됩니다.
— DotNet Fan

10

업데이트 할 속성 집합이 제한된 새 모델을 만듭니다.

즉, 엔티티 모델이 다음과 같은 경우 :

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

사용자가 이름을 변경할 수 있지만 사용 플래그가 아닌 사용자 정의보기 모델을 만들 수 있습니다.

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

데이터베이스 업데이트를 수행하려면 다음을 수행하십시오.

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();
    }
}

이 메서드를 호출하면 Name이 업데이트되지만 Enabled 속성은 변경되지 않습니다. 나는 단순한 모델을 사용했지만 그것을 사용하는 방법을 그림으로 얻을 것이라고 생각합니다.


감사합니다.보기에는 좋지만 여전히 속성의 블랙리스트가 아닌 화이트리스트입니다.
— Manuel Schweigert

뷰 모델에없는 것은 무엇이든 "블랙리스트"하고 있으며 추가 코딩이 필요하지 않으며 EF 기능 만 사용합니다. 또한 스터브 엔터티가 Attach로 첨부되면 모든 속성 값이 null / default로 설정됩니다. SetValues ​​(model)을 사용할 때 뷰 모델 속성이 null이면 이미 null로 첨부되었으므로 변경 추적기가 수정 된 것으로 표시하지 않으므로 해당 속성은 저장에서 건너 뜁니다. 시도 해봐.
— Admir Tuzović

3
나는 당신과 논쟁하고 싶지 않습니다. 블랙리스트 및 화이트리스트는 동일한 결과를 갖는 다른 접근 방식이며 접근 방식은 화이트리스트입니다. 앞서 말씀 드렸듯이 여러 가지 방법이 있지만 특히 하나에 대해 질문했습니다. 또한 솔루션은 nullable 형식에서만 작동합니다.
— Manuel Schweigert

1. Attach를 사용하여 모델을 연결합니다. 2. db.Entry (model) .Property ( "Propertyname"). State = PropertyState.Modified를 사용하여 속성을 반복합니다. 3. SaveChanges를 수행하십시오.
— Admir Tuzović

전체 모델을 수정하도록 설정 한 다음 일부 속성을 수정되지 않음으로 설정합니다. 내가 작성한 것은 먼저 모델을 첨부 한 다음 (아무것도 수정 된 것으로 설정되지 않음) 업데이트하려는 속성을 수정 됨으로 표시하는 것입니다. 이것은 정확히 원하는 => 화이트리스트입니다.
— Admir Tuzović

8

EF Core에서이를 달성하는 방법을 찾는 모든 사람. 기본적으로 동일하지만 업데이트 할 모델을 추가 한 후에 IsModified가 있어야합니다.

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

이유는 모르겠지만 EF Core에는 문자열 버전 만 있었고 람다를 사용할 수 없었습니다. 대신 nameof를 사용했지만 이것이 갈 길입니다. 감사합니다
— Cubelaster

이 대답은 "Microsofty"이므로 테스트하기 전에 작동 할 것임을 알았습니다. 위의 주석과 달리 문자열과 람다 버전은 모두 동일한 결과를 산출했습니다. 아마도 우리는 다른 버전을 사용하고있을 것입니다.
— T3.0

3

공유 할 엔티티의 속성을 쉽게 편집 할 수있는 방법을 만들었습니다. 이 코드는 엔티티의 이름 및 패밀리 속성을 편집합니다.

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

이 코드는 엔티티의 이름 및 패밀리 속성을 편집하는 것을 무시하고 다른 속성을 편집합니다.

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

이 확장 사용 :

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

응용 프로그램에서 사용하지 않을 경우 모델에서 제거하기 때문에 일부 경우에만 속성이 변경되는 것을 원하지 않는 것 같습니다.

일부 시나리오에서만 사용하고 위의 경우 "무효화"를 방지하려는 경우 다음을 시도 할 수 있습니다.

  • HiddenFor를 사용하여보기에서 매개 변수를 숨 깁니다.

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

이렇게하면 원래 값이 수정되지 않고 컨트롤러로 다시 전달됩니다.

컨트롤러에서 개체를 다시로드 DBSet하고이 메서드를 실행합니다. 업데이트하거나 업데이트하지 않을 매개 변수의 화이트리스트와 블랙리스트를 모두 지정할 수 있습니다.


여기에서 TryUpdateModel에 대한 좋은 토론을 찾을 수 있습니다. 링크 . 검증 된 답변에서 말했듯이 각 뷰에 필요한 속성과 정확히 일치하는 뷰 모델을 만드는 것이 좋습니다.
— 제이미

1
을 사용 @Html.HiddenFor하면 뷰의 HTML에 값이 기록되고 사용자가 브라우저 내에서 inspect 요소를 사용하고 수정할 수 있습니다. 그렇게 한 후에도 여전히 컨트롤러에 전달되지만 다른 값으로 업데이트됩니다. 방금 테스트했습니다.
— duckwizzle 19.01.25
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.