2015-10-14 53 views
6

目前我在做这样的:最佳途径只更新修改的字段与实体框架

例如:

public update(Person model) 
{ 
    // Here model is model return from form on post 
    var oldobj = db.Person.where(x=>x.ID = model.ID).SingleOrDefault(); 
    db.Entry(oldobj).CurrentValues.SetValues(model); 
} 

它的工作原理,但例如,

我有50列我的表格,但我只显示了我的表格中的25个字段(我需要部分更新我的表,其余25列保留相同的旧值)

我知道它可以通过“绘图柱一个接一个“或通过为剩余的25列创建”隐藏字段“。

只是想知道是否有任何优雅的方式,以较少的努力和最佳性能做到这一点?

+0

创建视图模型..只添加你想要的领域..设定值 – JamieD77

+0

即使我创建视图模型;更新到我的表“Person”时仍存在同样的问题。如果我缺少一些东西,请纠正我的问题 –

+0

您的viewmodel是否只显示了您显示的25个字段? – JamieD77

回答

5

这是一个非常好的问题。默认情况下,我发现只要启用了更改跟踪(这是默认情况下除非关闭),Entity Framework将会很好地向数据库应用您要求更改的内容。

因此,如果您只改变对象的1个字段,然后调用SaveChanges(),EF只会在调用SaveChanges()时更新该字段。

这里的问题是,当您将视图模型映射到实体对象时,所有的值都会被覆盖。这里是我的处理是这样的:

在这个例子中,有一个名为的人一个单一的实体:

Person 
====== 
Id - int 
FirstName - varchar 
Surname - varchar 
Dob - smalldatetime 

现在让我们说,我们要创建一个将只更新杜伯视图模型,并留下所有其他领域究竟如何,这是我如何做到这一点。

首先,创建一个视图模型:

public class PersonDobVm 
{ 
    public int Id { get; set; } 
    public DateTime Dob { get; set; } 

    public void MapToModel(Person p) 
    { 
     p.Dob = Dob; 
    } 
} 

现在写的代码大致如下(你必须改变它来匹配您的上下文的名称等):

DataContext db = new DataContext(); 
Person p = db.People.FirstOrDefault(); 

// you would have this posted in, but we are creating it here just for illustration 
var vm = new PersonDobVm 
{ 
    Id = p.Id, // the Id you want to update 
    Dob = new DateTime(2015, 1, 1) // the new DOB for that row 
}; 

vm.MapToModel(p); 
db.SaveChanges(); 

的MapToModel方法可能会更复杂,并在将视图模型字段分配给实体对象之前执行各种附加检查。

无论如何,当调用SaveChanges结果是下面的SQL:

exec sp_executesql N'UPDATE [dbo].[Person] 
SET [Dob] = @0 
WHERE ([Id] = @1) 
',N'@0 datetime2(7),@1 int',@0='2015-01-01 00:00:00',@1=1 

所以,你可以清楚地看到,实体框架并没有试图更新任何其他领域 - 只是杜伯领域。

我知道你的例子中你想避免用手来编码每个任务,但我认为这是最好的方法。你把它全部放在你的虚拟机中,这样它就不会抛弃你的主代码,这样你就可以满足特定的需求(即那里的复合类型,数据验证等)。另一种选择是使用AutoMapper,但我不认为它们是安全的。如果您在虚拟机中使用AutoMapper并将“Dob”拼写为“Doob”,则它不会将“Doob”映射到“Dob”,也不会告诉您!它会默默地失败,用户会认为一切正常,但变化不会被保存。如果你在虚拟机中拼写“Dob”为“Doob”,编译器会提醒你MapToModel()引用了“Dob”,但你的虚拟机中只有一个名为“Doob”的属性。

我希望这可以帮助你。

+0

感谢您的建议,但一对一的映射是当你有大量coulmns相当忙碌。 –

0

我已使用的FormCollection列出了在形式使用元素,只有改变数据库的那些列解决我的问题。

我提供我下面的代码示例;伟大的,如果它可以帮助别人

// Here 
// collection = FormCollection from Post 
// model = View Model for Person 

var result = db.Person.Where(x => x.ID == model.ID).SingleOrDefault(); 
if (result != null) 
{ 
    List<string> formcollist = new List<string>(); 
    foreach (var key in collection.ToArray<string>()) 
    { 
     // Here apply your filter code to remove system properties if any 
     formcollist.Add(key); 
    } 
    foreach (var prop in result.GetType().GetProperties()) 
    { 
      if(formcollist.Contains(prop.Name)) 
      { 
        prop.SetValue(result, model.GetType().GetProperty(prop.Name).GetValue(model, null)); 
      } 
    } 
    db.SaveChanges(); 
} 
+2

虽然这可能会起作用并为您节省了一些编码,但您需要考虑的问题是:当有人在您的视图模型中更改属性名称时,此代码的工作性能如何?想象一下,在重构期间有人将“Dob”更改为“DateOfBirth”。你的代码将如何工作呢?你根本没有编译器的安全性。将会发生什么是用户将在“DateOfBirth”中输入一个值,但它不会被应用到Dob字段。用户说他们进入了Dob,但是你的数据库会说他们没有。 –

4

我发誓通过EntityFramework.Extended。 Nuget Link

它可以让你写:

db.Person 
    .Where(x => x.ID == model.ID) 
    .Update(p => new Person() 
    { 
    Name = newName, 
    EditCount = p.EditCount+1 
    }); 

,这是非常清楚的转换成SQL。

+0

我认为.Update在实体框架 –

+4

EntityFramework.Extended中不可用。这是一个nuget包。 – Visser

+0

这是漂亮的,但我发现它在使用Web API 2结合使用EF时,并没有真正的帮助,因为'.Update()'方法来实例化类型(否则你会得到一个'MemberInitException')。如果我要做到这一点,我需要通过我的入站对象的属性循环以实例所述方法:( – LeeCambl