什么是阻止更新实体框架中特定字段的最佳方法

Log*_*ard 6 entity-framework

我正在使用实体框架为我的后端逻辑编写一个带有MVC的Web应用程序.我的问题是我有一个实体,它有一些永远不应该在更新时改变的字段.我不确定解决这个问题的最佳方法是什么.在我的应用程序中将会处理大量数据,所以我不能只是破解解决方案.

是否可以在POCO实体中将字段定义为只读?或者我应该编写验证所有更新的实体框架扩展类.可以在EF和实际数据库之间的映射文件中完成吗?

我是EF的新手,所以我希望你们中的一些人能给我一些指示!

谢谢!

Nei*_*ett 12

如果您使用的是.NET 4.5和EF 5(即MVC 4),则可以在相关的各个属性上设置IsModified = false.这样做的好处是可以接近默认的开箱即用的MVC约定.

例如,如果您有更新记录时不应触及的CreatedBy字段,请在控制器中使用以下内容:

[HttpPost]
    public ActionResult Edit(Response response)
    {
        if (ModelState.IsValid)
        {
            db.Entry(response).State = EntityState.Modified;
            db.Entry(response).Property(p => p.CreatedBy).IsModified = false;
            db.SaveChanges();
            return RedirectToAction("Index");
        }
        return View(response);
    }
Run Code Online (Sandbox Code Playgroud)

请注意,IsModified行是默认控制器操作的唯一更改.

您必须将此行设置为AFTER设置.State = EntityState.Modified(它作为一个整体应用于记录并将记录添加到db上下文中).

结果是EF不会在SQL UPDATE语句中包含此列.

我仍然(非常)感到震惊的是没有类似于[ReadOnly]的[InsertOnly]或[UpdateOnly]属性.这似乎是MVC团队的一次重大监督.我错过了什么吗?

我对这个解决方案并不完全满意,因为它是一个黑客攻击:你告诉EF,当你真正想说的是"HANDS OFF"时,没有做出任何改变.这也意味着您必须在可以更新字段的任何地方使用此代码.最好在class属性上有一个属性.

(抱歉发布到一个较老的线程,但我没有在其他地方看到这个解决方案.ViewModels很强大但很多工作,EF应该让事情变得更容易,而不是更难......)


Bon*_*nyT 5

好吧,我建议不要在视图中使用 EF 类。最好的办法是构造 ViewModel 类并使用 Automapper 从 EF 类映射它们。

但是,当您更新数据库中的记录时,您可以控制 ViewModel 中的哪些字段用于更新 EF 类中的现有字段。

正常流程应该是:

  • 使用 Id 从数据库中获取现有对象的最新版本。

  • 如果您使用的是乐观并发控制,则检查自创建 ViewModel 以来对象是否未更新(例如检查时间戳)。

  • 使用 ViewModel 对象中的必填字段更新此对象。

  • 将更新后的对象保留回数据库。

更新以包含 Automapper 示例:

假设您的 POCO 是

public class MyObject 
{
   public int Id {get;set;}
   public string Field1 {get;set;}
   public string Field2 {get;set;}
}
Run Code Online (Sandbox Code Playgroud)

Field1 是您不想更新的字段。

您应该声明一个具有相同属性的视图模型:

public class MyObjectModel 
{
   public int Id {get;set;}
   public string Field1 {get;set;}
   public string Field2 {get;set;}
}
Run Code Online (Sandbox Code Playgroud)

并在您的控制器的构造函数中在它们之间进行自动映射。

Mapper.CreateMap<MyObject, MyObjectModel>();
Run Code Online (Sandbox Code Playgroud)

如果你愿意,你可以(虽然我更喜欢手动执行此操作,但也可以通过其他方式自动映射:

Mapper.CreateMap<MyObjectModel, MyObject>().ForMember(dest=>dest.Field1, opt=>opt.Ignore());
Run Code Online (Sandbox Code Playgroud)

当您将日期发送到您的网站时,您将使用:

 var myObjectModelInstance = Mapper.Map<MyObject, MyObjectModel>(myObjectInstance);
Run Code Online (Sandbox Code Playgroud)

创建视图模型。

保存数据时,您可能需要以下内容:

public JsonResult SaveMyObject(MyObjectModel myModel)
{
    var poco = Mapper.Map<MyObjectModel, MyObject>(myModel);
    if(myModel.Id == 0 ) 
    {
       //New object
       poco.Field1 = myModel.Field1 //set Field1 for new creates only

    }
}
Run Code Online (Sandbox Code Playgroud)

尽管我可能会删除上面对 Field1 的排除并执行以下操作:

public JsonResult SaveMyObject(MyObjectModel myModel)
{
   var poco;
   if(myModel.Id == 0)
   {
     poco = Mapper.Map<MyObjectModel, MyObject>(myModel);
   }        
   else
   {
     poco = myDataLayer.GetMyObjectById(myModel.Id);
     poco.Field2 = myModel.Field2;
   }
   myDataLayer.SaveMyObject(poco);
}
Run Code Online (Sandbox Code Playgroud)

请注意,我相信最佳实践不会让您从 ViewModel 自动映射,而是始终手动执行此操作,包括新项目。