从MVC Controller类中删除数据库调用的最佳实践

时间:2009-03-24 19:53:59

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

我在ASP.NET MVC Controller类中有一个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()的方法可以在提交之前将两个相同类型的实体合并在一起?

感谢。

3 个答案:

答案 0 :(得分:5)

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

以下是一些阅读更多内容的地方:

修改:

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

首先,我通常喜欢对“更新”与“添加”采取不同的操作。即使它们是用于呈现表单的相同View,通常也可以使用不同的URL来发布编辑和POST新记录。因此,以下是控制器更新操作中使用的存储库模式:

[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();
    }
}

答案 1 :(得分:0)

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

答案 2 :(得分:0)

您目前拥有我称之为2层架构的内容,其中包括您的MVC应用层(即控制器)和您的数据访问层。

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

控制器 - &gt;服务 - &gt; DAL

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

控制器 - &gt;服务 - &gt;存储库 - &gt; DAL

您的控制器只负责3件事

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();
                }
            }
       }
   }