假设我有一个名为User的类,这是我的基本实体(我将它与DbContext用作DbSet用户),我用作数据访问层的基础级别。让我们说课程看起来像这样:
public class User
{
[Key]
public int Id { get; set; }
public bool Active { get; set; }
public string Description { get; set; }
public string Username { get; set; }
public string Password { get; set; }
public byte[] Photo { get; set; }
public DateTime Created { get; set; }
}
现在我想拥有纯视图,我只显示用户是否处于活动状态,以及允许我更改该值的简单复选框。我不想加载任何其他实体属性,特别是属性Photo,因为它只是疯了。我创建了这样的ActivateUserModel:
public class ActivateUserModel
{
[Key]
public int Id { get; set; }
public bool Active { get; set; }
}
我有一个名为Activate的强类型视图,它接受ActivateUserModel并显示它(它只是一个复选框并为Id隐藏)然后我有[HttpPost]激活动作捕获ActivateUserModel,将其转换为User实体,然后保存更改数据库。这是POST动作:
[HttpPost]
public ActionResult Activate(ActivateUserModel model)
{
if (ModelState.IsValid)
{
User user = new User { Id = model.Id, Active = model.Active };
db.Users.Attach(user);
db.Entry(user).Property(u => u.Active).IsModified = true;
db.SaveChanges();
return RedirectToAction("Index");
}
return View(model);
}
这就像一个魅力。我已经监视了针对SQL服务器发出的查询,我加载的所有内容都是对Id / Active,我更新的所有内容都是基于id的Active更改。
但是我不喜欢代码看起来如何。假设我有50个属性的实体,并且视图有25个。我不想写25行,我说IsModified = true。
所以我的问题是:有没有更有效的方法来做同样的事情,而不需要深入研究任何基于反射的方法?我想将数据从任何视图模型传输到实体,然后只保存那些属性。
提前感谢您的回复,我希望我提出足够明确的问题:)
答案 0 :(得分:6)
你可以这样做:
[HttpPost]
public ActionResult Activate(ActivateUserModel model)
{
if (ModelState.IsValid)
{
User user = db.Users.Single(u => u.Id == model.Id);
db.Entry(user).CurrentValues.SetValues(model);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(model);
}
db.Entry(user).CurrentValues.SetValues(model)
将检查user
中的model
中的属性是否也存在model
中的相同名称,如果是,则将属性值从user
复制到user
}。如果没有,它会使user
中的属性值保持不变。
我怀疑这不是基于反思的。但上面的代码是直截了当的方式,旨在完全支持您的场景。
修改强>
上面的代码加载了包含Photo
属性的完整IsModified
实体。如果您不想加载潜在的大二进制字段,我建议使用除Modified
技巧之外的其他策略解决此问题。使用实体框架进行更新强烈依赖于更改跟踪,这需要您加载完整实体。当您尝试避免这种情况时,您将使代码复杂化并手动为特定属性设置Photo
标志。
您可能知道,从数据库中获取实体时,不能排除单个标量属性的加载。我建议将UserPhoto
属性移动到一个新实体Id
,该实体只有Photo
和UserPhoto
属性,并将导航属性User
放入User
课程。然后,如果您想要将照片与User
一起加载,您可以通过延迟,急切或显式加载来决定。
如果要将UserPhoto
存储在单独的表中,可以在UserPhoto
和Photo
之间创建一对一映射。或者您甚至可以将User
列留在User
表格中,并通过Table Splitting将两个实体UserPhoto
和model
映射到同一张表。
修改2
请参阅您的评论,该方法会加载“不必要的东西”。我忘了提到以下内容:
实际上,在上面的代码中,您需要从数据库加载实体的成本。但是当您使用user
将SetValues(model)
应用于加载的实体Modified
时,EF只会将这些属性标记为IsModified
,与数据库中的原始值相比,这些属性确实发生了变化。生成的UPDATE语句仅包含这些列。因此,编写UPDATE语句的成本最小化。
如果您不想加载实体,则不知道数据库中的当前列值,并且您不知道实际更改了什么。您唯一的机会是强制所有属性的UPDATE以确保数据库中的行得到正确更新。在您的考试中,您必须将ViewModel中包含的所有25个属性true
设置为{{1}}。生成的SLQ UPDATE语句将包含所有25列。因此,UPDATE语句可能要贵得多,并且 - 借用你的话 - 不必要的东西。