web-dev-qa-db-ja.com

Entity Frameworkの更新時にプロパティを除外する

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

これまでのところ私が見つけた最善の方法は、包括的であり、手動で含めるすべてのプロパティを設定することですが、実際には除外するプロパティのみを言いたいです。

70

このように使用できます

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

更新されますが、Tokenプロパティはありません

125
Nitin Dominic

更新するプロパティのセットが限られている新しいモデルを作成します。

つまりエンティティモデルが次の場合:

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プロパティは変更されません。私は単純なモデルを使用しましたが、その使用方法を理解できると思います。

8
Admir Tuzović

私はあなたと共有するエンティティのプロパティを編集する簡単な方法を作りました。このコードは、エンティティの名前と家族のプロパティを編集します。

    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
}
2
Ali Yousefi

場合によってはプロパティを変更したくないと思います。アプリケーションで使用しない場合は、モデルからプロパティを削除するだけです。

一部のシナリオでのみ使用し、上記の場合の「無効化」を避けたい場合は、次のことを試してください。

  • HiddenForを使用してビューのパラメーターを非表示にします。

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

これにより、元の値が変更されずに保持され、コントローラーに渡されます。

DBSetからコントローラーにオブジェクトを再度ロードし、このメソッドを実行します。更新するかしないパラメータのホワイトリストとブラックリストの両方を指定できます。

1
Jaime