简体   繁体   English

在实体框架中更新时排除属性

[英]Exclude Property on Update in Entity Framework

I've been looking for a proper way to mark a property to NOT be changed when updating a model in MVC.我一直在寻找一种正确的方法来标记在 MVC 中更新模型时不会更改的属性。

For example, let's take this small model:例如,让我们以这个小模型为例:

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

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

then the edit method MVC creates looks like this:那么 MVC 创建的编辑方法如下所示:

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

now if my View does not contain the Token, it will be nullified through that edit.现在,如果我的视图不包含令牌,它将通过该编辑无效。

I'm looking for something like this:我正在寻找这样的东西:

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

The best way so far I found is to be inclusive and set all properties I want included by hand, but I really only want to say which ones to be excluded.到目前为止我发现的最好方法是包容并手动设置我想要包含的所有属性,但我真的只想说要排除哪些属性。

we can use like this我们可以这样使用

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

Anyone looking for how to achieve this on EF Core.任何正在寻找如何在 EF Core 上实现这一目标的人。 It's basically the same but your IsModified needs to be after you add the model to be updated.基本相同,但您的 IsModified 需要在添加要更新的模型之后进行。

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

Create new model that will have limited set of properties that you want to update.创建具有要更新的有限属性集的新模型。

Ie if your entity model is:即,如果您的实体模型是:

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

You can create custom view model that will allow user to change Name, but not Enabled flag:您可以创建自定义视图模型,允许用户更改名称,但不允许更改标志:

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

When you want to do database update, you do the following:当您想要进行数据库更新时,请执行以下操作:

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

When you call this method, you will update the Name, but Enabled property will remain unchanged.当您调用此方法时,您将更新 Name,但 Enabled 属性将保持不变。 I used simple models, but I think you'll get the picture how to use it.我使用了简单的模型,但我想你会知道如何使用它。

I made an easy way to edit properties of entities I will share with you.我做了一个简单的方法来编辑我将与你分享的实体的属性。 this code will edit Name and Family properties of entity:此代码将编辑实体的名称和家庭属性:

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

And this code will ignore to edit Name and Family properties of entity and it will edit another properties:并且此代码将忽略编辑实体的名称和家庭属性,它将编辑另一个属性:

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

Use this extension:使用这个扩展:

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
}

I guess you don't want the property to be changed just in some cases, because if you are not going to use it never in your application, just remove it from your model.我猜您不希望仅在某些情况下更改属性,因为如果您不打算在应用程序中使用它,只需将其从模型中删除即可。

In case you want to use it just in some scenarios and avoid its "nullification" in the case above, you can try to:如果您只想在某些情况下使用它并避免在上述情况下“无效”,您可以尝试:

  • Hide the parameter in the view with HiddenFor:用 HiddenFor 在视图中隐藏参数:

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

This will make your original value to be kept unmodified and passed back to the controller.这将使您的原始值保持不变并传递回控制器。

Load again your object in the controller from your DBSet and run this method.从您的DBSet再次将您的对象加载到控制器中并运行此方法。 You can specify both a white list and a blacklist of parameters that shall or shall not be update.您可以指定应更新或不应更新的参数的白名单和黑名单。

I use dapper but my solution will work for EF too.我使用 dapper,但我的解决方案也适用于 EF。 If you are potentially going to change your ORM in the future my solution might be better for you.如果您将来可能要更改您的 ORM,我的解决方案可能对您更好。

class Model
{
    public Foo { get; set; }
    public Boo { get; set; }
    public Bar { get; set; }
    // More properties...

    public void SafeUpdate(Model updateModel, bool updateBoo = false)
    {
        // Notice Foo is excluded

        // An optional update
        if (updateBoo)
            Boo = updateModel.Boo;

        // A property that is always allowed to be updated
        Bar = updateModel.Bar;
        
        // More property mappings...
    }
}

As you can observe I allow updates for only the properties that I wish.如您所见,我只允许更新我希望的属性。

A downside of my approach is that you'll need to manually update this method if you introduce new properties (that are allowed to be updated) to your model.我的方法的一个缺点是,如果您向模型引入新属性(允许更新),则需要手动更新此方法。 But I believe this in not always a downside but sometimes an advantage, in the sense that you'll need to be aware of what is being updated, this might be beneficial in terms of security.但我相信这并不总是一个缺点,但有时是一个优点,因为您需要了解正在更新的内容,这在安全性方面可能是有益的。

Let us see a demonstration of this approach.让我们看一下这种方法的演示。

// Some code, DI etc...

public IActionResult Put([FromBody] Model updateModel)
{
   var safeModel = new Model();
   safeModel.Update(updateModel);

   // Add validation logic for safeModel here...

   _modelRepository.Update(safeModel);
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM