假设我有一个名为 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; }
}
现在我想要纯 View ,只显示用户是否处于事件状态,以及允许我更改该值的简单复选框。我不想加载任何其他实体属性,特别是 Photo 属性,因为它太疯狂了。我创建了如下所示的 ActivateUserModel:
public class ActivateUserModel
{
[Key]
public int Id { get; set; }
public bool Active { get; set; }
}
我有一个名为 Activate 的强类型 View ,它接受 ActivateUserModel 并显示它(它只是一个复选框并且对 Id 是隐藏的)然后我有 [HttpPost] Activate 捕获 ActivateUserModel 的操作,将其转换为用户实体,然后保存对数据库的更改。这是 POST Action :
[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 Server 发出的查询,我加载的所有内容都是 ID/Active 对,我更新的所有内容也是基于 ID 的事件更改。
但是我不喜欢代码的外观。假设我的实体有 50 个属性, View 有 25 个。我不想在我说 IsModified=true 的地方写 25 行。
所以我的问题是:是否有更有效的方法来做同样的事情,而无需深入研究任何基于反射的方法?我想将数据从任何 View 模型传输到实体,然后只保存那些属性。
预先感谢您的回复,我希望我的问题足够清楚:)
最佳答案
你可以这样做:
[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
属性(property)。如果您不喜欢加载可能很大的二进制字段,我建议您使用 IsModified
以外的其他策略来解决此问题。诡计。使用 Entity Framework 进行更新强烈依赖于更改跟踪,这需要您加载完整的实体。当您尝试避免这种情况并设置 Modified
时,您将使代码复杂化。手动标记特定属性。
您可能知道,当您从数据库中获取实体时,您不能排除加载单个标量属性。我建议移动 Photo
属性(property)变成一个新的实体UserPhoto
它只有一个 Id
和 Photo
属性并放置一个导航属性 UserPhoto
进入User
类(class)。然后,您可以决定是通过延迟加载、急切加载还是显式加载来决定是否要将照片与 User
一起加载。还是不是。
您可以在 User
之间创建一对一映射和 UserPhoto
如果你想存储 UserPhoto
在一个单独的表中。或者你甚至可以离开 Photo
User
中的列表格和映射两个实体User
和 UserPhoto
通过 Table Splitting 到同一张表.
编辑2
引用您关于该方法加载“不必要的东西”的评论。我忘了提及以下内容:
实际上,在上面的代码中,您需要从数据库中加载实体。但是当你申请 model
到加载的实体 user
使用 SetValues(model)
EF 只会将这些属性标记为 Modified
与数据库中的原始值相比,它确实发生了变化。生成的 UPDATE 语句将只包含那些列。因此,编写 UPDATE 语句的成本被最小化。
如果您不想加载实体,则您不知道数据库中的当前列值,也不知道真正发生了什么变化。您唯一的机会是对所有 属性强制更新以确保数据库中的行得到正确更新。在您的示例中,您必须设置 IsModified
对于 ViewModel 中包含的所有 25 个属性到 true
.生成的 SLQ UPDATE 语句将包含所有 25 列。因此,UPDATE 语句的开销可能要大得多,而且确实 - 借用你的话 - 不必要的东西。
关于asp.net-mvc-3 - 实体和派生 View 模型 - 仅更新公共(public)属性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10370745/