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


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 2012年

2
您可以使用viewmodels并仅映射要更新的内容。
frennky 2012年

我可以。有一些解决此问题的方法。但是我想知道是否有很好的方法可以做到这一点,如果有的话,它是如何工作的。顺便说一句,对这个atm来说,最小的“解决方案”是进行另一笔交易:using (var db2 = new DataContext()) model.Token = db2.Models.Find(model.Id).Token;但是我也不满意这一笔交易。
Manuel Schweigert 2012年

3
我承认这是执行此操作的“正确”方法,但是在这种情况下有这样做的原因:a)开销,b)不敏捷,c)难以维护/容易出错。是的,除了一个属性,我拒绝创建两个相同的类。
Manuel Schweigert

Answers:


155

我们可以这样使用

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

它将更新,但没有令牌属性


2
如果您正在使用该AddOrUpdate怎么办?-您怎么知道使用EntityState.ModifiedEntityState.Added
杰西2015年

1
更新:要使其在EF 6中工作。您需要db.Model.Attach(model);
Maxi

6
请注意其他事项,这里的顺序很重要:如果在设置db.Entry(model).State = EntityState.Modified;后设置db.Entry(model).Property(x => x.Token).IsModified = false; ,则该属性将在保存时进行更新。
阿克拉

1
而且这也应该在更新模型值db.Entry(model).CurrentValues.SetValues(sourceModel);之后进行。如果不是,那么属性也会在保存时更新。
DotNet Fan's

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 2012年

您正在将您视图模型中未包含的任何东西“列入黑名单”,这不需要任何额外的编码,您仅使用EF功能。此外,当存根实体附加有“附加”时,所有属性值均设置为null /默认值。当您使用SetValues(model)时,如果您的视图模型属性为null,则由于它已附加为null,因此更改跟踪器不会将其标记为已修改,因此该属性将从保存中跳过。尝试一下。
AdmirTuzović12年

3
我不想和你争论。黑名单和白名单是不同的方法,具有相同的结果,您的方法是白名单。正如我之前说的,有很多方法,但是我特别在问一种方法。此外,您的解决方案仅适用于可为null的类型。
Manuel Schweigert 2012年

1.使用附加附加模型。2.使用db.Entry(model).Property(“ Propertyname”)。State = PropertyState.Modified;遍历属性。3.执行SaveChanges。
AdmirTuzović12年

您要做的是设置要修改的整个模型,然后将某些属性设置为“未修改”。我写给您的是先附加模型(什么都没有设置为修改),然后将要更新的属性标记为已修改,这正是您想要的=>白名单。
AdmirTuzović'12

8

寻找如何在EF Core上实现此目标的任何人。基本上是相同的,但是您需要在添加要更新的模型之后将IsModified更改为IsModified。

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

不知道为什么,但是我的EF Core只有字符串版本,我不能使用lambda。我改用nameof,但这是要走的路。感谢
Cubelaster

这个答案是如此“ Microsofty”,我知道在测试之前它会起作用。与上面的注释相反,字符串和lambda版本均产生相同的结果。也许我们正在使用不同的版本。
T3.0

2

我提供了一种简单的方法来编辑将与您共享的实体的属性。此代码将编辑实体的名称和族属性:

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

并且此代码将忽略以编辑实体的Name和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的精彩讨论:link。就像在经过验证的答案中所说的那样,最好创建视图模型以完全匹配每个视图所需的属性。
Jaime 2012年

1
使用@Html.HiddenFor会将值写入视图的HTML中,并允许用户在其浏览器中使用inspect元素并对其进行修改。他们这样做之后,它仍会传递给控制器​​,但具有不同的值,并将被更新。我刚刚测试过。
duckwizzle
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.