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.