3

假设我有一个名为 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] Activate 捕获 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 个属性的视图。我不想在我说 IsModified=true 的地方写 25 行。

所以我的问题是:有没有更有效的方法来做同样的事情,而不深入研究任何基于反射的方法?我想将数据从任何视图模型传输到实体,然后只保存这些属性。

提前感谢您的回复,我希望我的问题足够清楚:)

4

1 回答 1

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)将检查 in 中是否user也存在同名model的属性,如果是,则将属性值从 复制modeluser。如果不是,它使属性值user保持不变。

我怀疑这不是基于反射的。但是上面的代码是直接的方法,旨在完全支持您的方案。

编辑

上面的代码加载了包括属性user在内的完整实体。Photo如果您不喜欢加载可能很大的二进制字段,我建议您使用除IsModified技巧之外的其他策略来解决此问题。使用 Entity Framework 进行更新强烈依赖于更改跟踪,这需要您加载完整的实体。当您试图避免这种情况并Modified手动设置特定属性的标志时,您的代码将会复杂化。

您可能知道,当您从数据库中获取实体时,不能排除加载单个标量属性。我建议将该属性移动到一个只有一个和属性Photo的新实体中,并将​​导航属性放入类中。然后,您可以通过延迟加载、急切加载或显式加载来决定是否要同时加载照片。UserPhotoIdPhotoUserPhotoUserUser

如果要将其存储在单独的表中,您可以在User和之间创建一对一的映射。或者您甚至可以将列保留在表中并通过Table Splitting将两个实体映射到同一个表。UserPhotoUserPhotoPhotoUserUserUserPhoto

编辑 2

参考您的评论,该方法会加载“不必要的东西”。我忘了提及以下内容:

实际上,在上面的代码中,您需要从数据库中加载实体。但是,当您使用EF 应用于model加载的实体时,只会将那些属性标记为与数据库中的原始值相比确实发生了变化。生成的 UPDATE 语句将只包含那些列。因此,编写 UPDATE 语句的成本最小化。userSetValues(model)Modified

如果您不想加载实体,则您不知道数据库中的当前列值,也不知道真正发生了什么变化。您唯一的机会是强制对所有属性进行更新,以确保数据库中的行得到正确更新。在您的示例中,您必须将IsModifiedViewModel 中包含的所有 25 个属性设置为true. 生成的 SLQ UPDATE 语句将包含所有 25 列。所以,UPDATE 语句可能要贵得多,而且确实——借用你的话——不必要的东西。

于 2012-04-29T12:41:02.047 回答