asp.net-mvc-3 - 实体和派生 View 模型 - 仅更新公共(public)属性

标签 asp.net-mvc-3 entity-framework ef-code-first

假设我有一个名为 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它只有一个 IdPhoto属性并放置一个导航属性 UserPhoto进入User类(class)。然后,您可以决定是通过延迟加载、急切加载还是显式加载来决定是否要将照片与 User 一起加载。还是不是。

您可以在 User 之间创建一对一映射和 UserPhoto如果你想存储 UserPhoto在一个单独的表中。或者你甚至可以离开 Photo User 中的列表格和映射两个实体UserUserPhoto通过 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/

相关文章:

asp.net-mvc-3 - mvc - ActionNameAttribute 从资源获取?

c# - 如何使用 ORM 处理并发

c# - 可以在 Entity Framework 中设置列顺序

c# - 使用 Entity Framework 代码优先映射多个关系的问题

c# - 将 xml 添加到 http 重定向

asp.net-mvc - 在显示模板中使用 DisplayFor

c# - 制作通用方法来更新 Entity Framework 多对多导航属性

c# - 了解 ASP.NET MVC 中的路由

c# - 信号量异常 - 将指定的计数添加到信号量会导致它超过其最大计数