.net 从MVC控制器类中删除数据库调用的最佳实践

.net 从MVC控制器类中删除数据库调用的最佳实践,.net,asp.net,asp.net-mvc,linq-to-sql,.net,Asp.net,Asp.net Mvc,Linq To Sql,我在ASP.NET MVC控制器类中有一个操作方法,该方法处理相当基本的“创建/编辑用户”页面中的表单帖子。我是MVC新手,所以我一直在遵循各种Microsoft教程中的代码示例,这是该方法当前的外观: [AcceptVerbs(HttpVerbs.Post)] public ViewResult Save([Bind(Prefix = "ServiceUser")]ServiceUser SUser) { if (SUser.ServiceUserId == 0) //new

我在ASP.NET MVC控制器类中有一个操作方法,该方法处理相当基本的“创建/编辑用户”页面中的表单帖子。我是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…”代码不属于控制器。如果我将更新功能移动到另一个类(就像我使用Insert方法所做的那样),我将失去控制器的UpdateModel()方法的便利性,并且可能最终不得不做一些非常详细的事情来读取现有实体、更新其属性并提交更改

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


谢谢。

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

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

  • (第32页)
编辑:

我强烈建议阅读上面链接的斯科特·古思里的整章。它有很多好的建议。也就是说,这里有一些相关的例子(本章除外)

首先,我通常喜欢对“更新”和“添加”有不同的操作。即使它们是呈现表单的同一视图,发布编辑和发布新记录时使用不同的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);
    }
}
以下是“添加”示例:

对于上述两个示例,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();
    }
}
公共类晚餐地址
{
private NerdDinnerDataContext db=new NerdDinnerDataContext();
//
//查询方法
公共可查询的FindAllDinners()
{
返回db.晚餐;
}
公共可查询的FindUpcomingDinners()
{
晚餐后返回db。晚餐
where deline.EventDate>DateTime.Now
按晚餐点菜。EventDate
选择晚餐;
}
公共晚餐(int id)
{
返回db.Dinners.SingleOrDefault(d=>d.DinnerID==id);
}
//
//插入/删除方法
公共空间添加(晚餐)
{
db.晚餐。InsertOnSubmit(晚餐);
}
公共空间删除(晚餐)
{
db.RSVPs.DeleteAllOnSubmit(晚餐.RSVPs);
db.dinters.deleteosubmit(晚餐);
}
//
//坚持
公共作废保存()
{
db.SubmitChanges();
}
}

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

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

  • (第32页)
编辑:

我强烈建议阅读上面链接的斯科特·古思里的整章。它有很多好的建议。也就是说,这里有一些相关的例子(本章除外)

首先,我通常喜欢对“更新”和“添加”有不同的操作。即使它们是呈现表单的同一视图,发布编辑和发布新记录时使用不同的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);
    }
}
以下是“添加”示例:

对于上述两个示例,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();
    }
}
公共类晚餐地址
{
private NerdDinnerDataContext db=new NerdDinnerDataContext();
//
//查询方法
公共可查询的FindAllDinners()
{
返回db.晚餐;
}
公共可查询的FindUpcomingDinners()
{
晚餐后返回db。晚餐
where deline.EventDate>DateTime.Now
按晚餐点菜。EventDate
选择晚餐;
}
公共晚餐(int id)
{
返回db.Dinners.SingleOrDefault(d=>d.DinnerID==id);
}
//
//插入/删除方法
公共空间添加(晚餐)
{
db.晚餐。InsertOnSubmit(晚餐);
}
公共空间删除(晚餐)
{
db.RSVPs.DeleteAllOnSubmit(晚餐.RSVPs);
db.dinters.deleteosubmit(晚餐);
}
//
//坚持
公共作废保存()
{
db.SubmitChanges();
}
}

我同意李的观点,我也一直在找东西。我在MVC预览的早期对反射做了类似的事情,该预览用于模型而不是控制器。这不是最好的代码,并且认为MVC最终版会添加一些东西。没有什么东西卡在控制器里。如果使用强类型视图,最好将表单或模型通过控制器传递到模型中,并让所有验证和数据移动都在那里完成。现在,即使是一个做得很差的控制器也无法传输数据。

我同意李的观点,我也一直在寻找一些东西。我在MVC预览的早期对反射做了类似的事情,该预览用于模型而不是控制器。这不是最好的代码,并且认为MVC最终版会添加一些东西。没有什么东西卡在控制器里。如果使用强类型视图,最好将表单或模型通过控制器传递到模型中,并让所有验证和数据移动都在那里完成。现在,即使是一个做得很差的控制器也无法输送数据。

您目前拥有一个我称之为2层的体系结构,其中包括MVC应用层(ie控制器)和数据访问层

您可能希望通过在控制器和DAL之间插入服务层,从而移动到3层或4层体系结构。那么你呢
  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();
                }
            }
       }
   }