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

时间:2012-07-23 08:30:12

标签: entity-framework

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

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

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

谢谢!

2 个答案:

答案 0 :(得分: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);
    }

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

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

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

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

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

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

答案 1 :(得分:3)

我建议不要在View中使用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;}
}

和Field1是您不想更新的字段。

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

public class MyObjectModel 
{
   public int Id {get;set;}
   public string Field1 {get;set;}
   public string Field2 {get;set;}
}

并在Controller的构造函数中对它们进行Automap。

Mapper.CreateMap<MyObject, MyObjectModel>();

如果你愿意,你可以(虽然我更喜欢手动执行此操作,但也可以自动化另一种方式:

Mapper.CreateMap<MyObjectModel, MyObject>().ForMember(dest=>dest.Field1, opt=>opt.Ignore());

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

 var myObjectModelInstance = Mapper.Map<MyObject, MyObjectModel>(myObjectInstance);

创建viewModel。

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

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

    }
}

虽然我可能会删除上面的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);
}

请注意,我认为最佳做法是让您永远不会从ViewModel自动执行,而是始终手动执行此操作,包括新项目。