【问题标题】:Best practice for removing Database calls from MVC Controller classes从 MVC 控制器类中删除数据库调用的最佳实践
【发布时间】:2009-03-24 19:53:59
【问题描述】:

我在 ASP.NET MVC 控制器类中有一个 Action 方法,用于处理来自相当基本的“创建/编辑用户”页面的表单帖子。我是 MVC 的新手,所以我一直在关注各种 Microsoft 教程中的代码示例,这就是该方法目前的样子:

[AcceptVerbs(HttpVerbs.Post)]
public ViewResult Save([Bind(Prefix = "ServiceUser")]ServiceUser SUser)
{
        if (SUser.ServiceUserId == 0) //new service user
            ServiceUserHelper.AddServiceUser(SUser);
        else //update to existing service user
        {
            using (ProjectDataContext db = DatabaseHelper.CreateContext())
            {
                this.UpdateModel(db.ServiceUsers.Single(su => su.ServiceUserId == SUser.ServiceUserId), "ServiceUser");
                db.SubmitChanges();
            }
        }

        //return a confirmation view
}

这很好用;但是我的直觉告诉我“ProjectDataContext ...”代码不属于控制器。如果我将 Update 功能移动到另一个类(以我使用 Insert 方法的方式),我将失去 Controller 的 UpdateModel() 方法的便利性,并且可能最终不得不做一些非常冗长的事情读取现有实体,更新其属性,然后提交更改。

所以我的问题是,实现这一目标的最佳方法是什么? LINQ中是否有类似UpdateModel()的方法可以在提交前合并两个相同类型的实体?

谢谢。

【问题讨论】:

    标签: .net asp.net asp.net-mvc linq-to-sql


    【解决方案1】:

    大多数人会建议使用“存储库模式”将数据访问代码移出控制器(并使用模拟对象而不是真实数据库进行单元测试)。

    这里有一些地方可以阅读更多内容:

    编辑:

    我强烈建议您阅读上面链接的整个 Scott Guthrie 章节。它有很多好的建议。也就是说,这里有一些相关的例子(本章除外)......

    首先,我通常喜欢对“更新”和“添加”有不同的操作。即使它们是呈现表单的相同视图,使用不同的 URL 来发布编辑与发布新记录通常感觉更清晰。因此,控制器更新操作中使用的存储库模式如下所示:

    [AcceptVerbs(HttpVerbs.Post)]
    public ActionResult Edit(int id, FormCollection formValues)
    {
        //get the current object from the database using the repository class
        Dinner dinner = dinnerRepository.GetDinner(id);
        try
        {
            //update the object with the values submitted
            UpdateModel(dinner);
            //save the changes
            dinnerRepository.Save();
            //redirect the user back to the read-only action for what they just edited
            return RedirectToAction("Details", new { id = dinner.DinnerID });
        }
        catch
        {
            //exception occurred, probably from UpdateModel, so handle the validation errors
            // (read the full chapter to learn what this extention method is)
            ModelState.AddRuleViolations(dinner.GetRuleViolations());
            //render a view that re-shows the form with the validation rules shown
            return View(dinner);
        }
    }
    

    这是“添加”示例:

    [AcceptVerbs(HttpVerbs.Post)]
    public ActionResult Create()
    {
        //create a new empty object
        Dinner dinner = new Dinner();
        try
        {
            //populate it with the values submitted
            UpdateModel(dinner);
            //add it to the database
            dinnerRepository.Add(dinner);
            //save the changes
            dinnerRepository.Save();
            //redirect the user back to the read-only action for what they just added
            return RedirectToAction("Details", new { id = dinner.DinnerID });
        }
        catch
        {
            //exception occurred, probably from UpdateModel, so handle the validation errors
            // (read the full chapter to learn what this extention method is)
            ModelState.AddRuleViolations(dinner.GetRuleViolations());
            //render a view that re-shows the form with the validation rules shown
            return View(dinner);
        }
    }
    

    对于上述两个示例,DinnerRepository 如下所示:

    public class DinnerRepository
    {
        private NerdDinnerDataContext db = new NerdDinnerDataContext();
        //
        // Query Methods
        public IQueryable<Dinner> FindAllDinners()
        {
            return db.Dinners;
        }
        public IQueryable<Dinner> FindUpcomingDinners()
        {
            return from dinner in db.Dinners
                   where dinner.EventDate > DateTime.Now
                   orderby dinner.EventDate
                   select dinner;
        }
        public Dinner GetDinner(int id)
        {
            return db.Dinners.SingleOrDefault(d => d.DinnerID == id);
        }
        //
        // Insert/Delete Methods
        public void Add(Dinner dinner)
        {
            db.Dinners.InsertOnSubmit(dinner);
        }
        public void Delete(Dinner dinner)
        {
            db.RSVPs.DeleteAllOnSubmit(dinner.RSVPs);
            db.Dinners.DeleteOnSubmit(dinner);
        }
        //
        // Persistence
        public void Save()
        {
            db.SubmitChanges();
        }
    }
    

    【讨论】:

    • 我查看了 Rob Conery 的店面应用程序,但找不到任何示例来说明如何实现我正在寻找的具体内容;那里有很多视频可以浏览。我只需要一个示例来说明如何在控制器之外实现我提到的更新功能。
    • 帮自己一个忙,“涉足”RobCon 的东西。所有的。即使您不选择采用他更“高级”的架构(与 Nerdinner 示例相比),您也会对 MVC 有更好的完整感觉。作为记录,我使用了 Rob 方法 - 单独的服务层和 OO 域模型(而不是使用 LINQ 类)。
    • 通过将 GetDinner 和 SaveDinner 移动到服务(另一个间接级别),您可以消除在控制器中访问数据的需要。这可以说是更具可测试性并且消除了对服务的数据访问责任,即使在您编写它时它可能看起来只是一个薄薄的外观。
    【解决方案2】:

    我同意 Lee D 我一直在寻找的东西。在模型中使用的 MVC 预览中,我对反射做了类似的事情,而不是控制器。这不是最好的代码,并认为会在 MVC final 中添加一些东西。没有东西卡在控制器中。如果使用强类型视图,最好将表单或模型通过控制器传递到模型中,并在那里完成所有验证和数据移动。现在,即使是做得不好的控制器也无法处理数据。

    【讨论】:

      【解决方案3】:

      您目前拥有我所说的 2 层架构,其中包括您的 MVC 应用程序层(即控制器)和数据访问层。

      您可能希望通过在控制器和 DAL 之间插入服务层来迁移到 3 或 4 层架构。所以你最终会得到:

      控制器 -> 服务 -> DAL

      4 层架构可能包括存储库层

      控制器 -> 服务 -> 存储库 -> DAL

      你的控制器只负责三件事

      1) 参数处理 2)调用你的服务层做工作 2) 申请流程

      您上面的示例可能类似于:

      [AcceptVerbs(HttpVerbs.Post)]
      public ViewResult Save([Bind(Prefix = "ServiceUser")]ServiceUser SUser)
      {
              // validate arguments
              if (SUser == null)
              {
                    throw new ArgumentException("SUser can not be null");
              }
      
              // process form fields / query params / etc.
              this.TryUpdateModel(SUser, "ServiceUser");
      
              // update your model 
              // (toss in a try/catch to deal with validation errors etc)
              _userService.Save(SUser);
      
              //return a confirmation view
      }
      

      那么你的服务层将负责做实际的工作:

        public class UserService : IUserService
         {
             public void Save(ServiceUser SUser)
             {
                  // insert or update user info
      
                  if (SUser.ServiceUserId == 0) //new service user
                      ServiceUserHelper.AddServiceUser(SUser);
                  else //update to existing service user
                  {
                      using (ProjectDataContext db = DatabaseHelper.CreateContext())
                      {
                          db.ServiceUsers.Single(su => su.ServiceUserId == 
                                                 SUser.ServiceUserId);
                          db.SubmitChanges();
                      }
                  }
             }
         }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-10-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多