实体和派生视图模型 - 仅更新公共属性

Adm*_*vić 3 entity-framework ef-code-first asp.net-mvc-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; }
}
Run Code Online (Sandbox Code Playgroud)

现在我想要纯视图,我只显示用户是否处于活动状态,以及允许我更改该值的简单复选框.我不想加载任何其他实体属性,特别是属性Photo,因为它只是疯了.我创建了这样的ActivateUserModel:

public class ActivateUserModel
{
    [Key]
    public int Id { get; set; }
    public bool Active { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

我有一个名为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);
    }
Run Code Online (Sandbox Code Playgroud)

这就像一个魅力.我已经监视了针对SQL服务器发出的查询,并且我加载的所有内容都是对Id/Active,我更新的所有内容都是基于id的Active更改.

但是我不喜欢它看起来的代码.假设我有50个属性的实体,并且视图有25个.我不想写25行,我说IsModified = true.

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

提前谢谢你的回复,我希望我的问题足够明确:)

Sla*_*uma 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);
}
Run Code Online (Sandbox Code Playgroud)

db.Entry(user).CurrentValues.SetValues(model)将检查属性是否user也存在同名model,如果是,则将属性值复制modeluser.如果不是,它将保持属性值user不变.

我怀疑这不是基于反思的.但上面的代码是直截了当的方式,旨在完全支持您的方案.

编辑

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

您可能知道,从数据库中获取实体时,不能排除单个标量属性的加载.我建议将Photo属性移动到一个新实体UserPhoto,该实体只有一个IdPhoto属性,并将导航属性UserPhoto放入User类中.然后,如果您想要将照片User与否一起加载,您可以通过延迟,急切或显式加载来决定.

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

编辑2

请参阅您的评论,该方法加载"不必要的东西".我忘了提到以下内容:

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

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