MVC,ORM和数据访问模式

Evg*_*eni 8 c# asp.net-mvc orm design-patterns asp.net-mvc-3

我想我已经达到了"分析瘫痪"的状态.我有一个MVC应用程序,使用EF作为ORM.所以我正在尝试决定最好的数据访问模式,到目前为止我认为将所有数据访问逻辑放入控制器是要走的路......但它听起来有点不对劲.另一种选择是创建外部存储库,处理数据交互.这是我的利弊:

如果嵌入数据访问控制器,我将得到这样的代码:

using (DbContext db = new DbContext())
{
    User user = db.Users.Where(x=>x.Name == "Bob").Single();
    user.Address.Street = "some st";
    db.SaveChanges();
}
Run Code Online (Sandbox Code Playgroud)

所以有了这个,我得到了延迟加载的全部好处,我在完成后立即关闭连接,我对where子句的灵活性 - 所有的细节.con - 我在一个方法中混合了一堆东西 - 数据检查,数据访问,UI交互.

使用Repository,我正在外部化数据访问,理论上,如果我决定使用ado.net或使用不同的数据库,则可以替换repos.但是,我没有看到实现延迟加载的良好清洁方式,以及如何控制DbContext /连接的生命周期.说,我有CRUD方法的IRepository接口,我如何加载属于给定用户的地址列表?像GetAddressListByUserId这样的方法看起来很丑陋,错误,并且会让我创建一堆同样丑陋的方法,并且在使用ORM时没什么意义.

我确信这个问题已经解决了几百万次,希望有一个解决方案..


关于存储库模式的另一个问题 - 如何处理属性对象?例如,用户有一个地址列表,您将如何检索该列表?为地址创建存储库?使用ORM,地址对象不必具有回复用户的引用,也不需要具有repo的Id字段 - 它必须拥有所有这些.更多代码,更多暴露属性..

Yak*_*ych 7

您选择的方法很大程度上取决于您要使用的项目类型.对于需要a Rapid Application Development(RAD)方法的小型项目,在MVC项目中直接使用EF模型并在控制器中访问数据几乎可以,但项目越多,它就会变得越来越混乱你会开始遇到越来越多的问题.如果您需要良好的设计和可维护性,有几种不同的方法,但一般来说,您可以坚持以下内容:

保持控制器和视图清洁.控制器应该只控制应用程序流,不包含数据访问甚至业务逻辑.视图应该仅用于表示 - 给它一个ViewModel,它将它呈现为Html(没有业务逻辑或计算).一个ViewModel每看法是这样做的一个非常干净的方式.

典型的控制器操作如下所示:

public ActionResult UpdateCompany(CompanyViewModel model)
{
    if (ModelState.IsValid)
    {
        Company company = SomeCompanyViewModelHelper.
                          MapCompanyViewModelToDomainObject(model);
        companyService.UpdateCompany(company);
        return RedirectToRoute(/* Wherever you go after company is updated */);
    }
    // Return the same view with highlighted errors
    return View(model);
}
Run Code Online (Sandbox Code Playgroud)

由于上述原因,最好抽象您的数据访问(可测试性,易于切换数据提供者或ORM或其他等).该Repository模式是一个不错的选择,但在这里您还可以获得一些实现选项.关于通用/非通用存储库一直有很多讨论,无论是否应该返回IQueryables等等.但最终它是由您选择的.

顺便问一下,你为什么要加载延迟?通常,您确切地知道特定视图所需的数据,那么为什么要选择以延迟方式获取数据,从而进行额外的数据库调用,而不是在一次调用中急需加载所需的所有内容?就个人而言,我认为有多种Get方法来获取有或没有孩子的对象是可以的.例如

public class CompanyRepository
{
    Get(int Id);
    Get(string name);
    GetWithEmployees(int id);
    ...
}
Run Code Online (Sandbox Code Playgroud)

它可能看起来有点矫枉过正,你可能会选择不同的方法,但只要你有一个你遵循的模式,维护代码就容易多了.

  • 对于初学者,我没有说"The Repository模式增加了可维护性".我说如果你想要大型项目的长期可维护性,你可能不希望在控制器中访问数据(参见之前的评论之一).在这种情况下,存储库模式只是被称为打破数据访问代码的方法之一.在任何情况下,可维护性都会增加,因为您的数据访问代码,业务逻辑和表示逻辑不会混合到一段代码中.我们通常会尝试使其干净并具有Web,服务,存储库层.(并没有1500线回购;)) (2认同)
  • 此外,在控制器中进行数据访问会严重限制您的数据访问代码的拒绝.最罕见的情况是,当你有一个Web应用程序,然后想要创建一个基本上做同样的Windows应用程序.在这种情况下,您将从Web应用程序中复制大部分数据访问代码,而不是重用服务或repo程序集中的现有方法. (2认同)

Ten*_*giz 5

我个人这样做:

我有一个抽象的Domain层,它不仅包含CRUD方法,还包含专门的方法,例如UsersManager.Authenticate()等.它内部使用数据访问逻辑或数据访问层抽象(取决于我需要的抽象级别)具有).

至少有一个抽象依赖总是更好.以下是它的一些优点:

  • 您可以在以后用另一个实现替换一个实现.
  • 您可以在需要时对控制器进行单元测试.

从控制器本身开始,让它有2个构造函数:一个具有抽象域访问类(例如域的外观),另一个(空)构造函数选择默认实现.这样,您的控制器在Web应用程序运行时(调用空构造函数)和单元测试期间(注入模拟域层)就可以正常运行.

此外,为了能够在以后轻松切换到另一个域,请务必注入域创建者,而不是域本身.通过这种方式,将域图层构造本地化到域创建者,您可以随时切换到另一个实现,只需重新构建域创建者(创建者,我的意思是某种工厂).

我希望这有帮助.

增加:

  • 我不建议在域层中使用CRUD方法,因为每当您丰富单元测试阶段时,这将成为一场噩梦,甚至更多,当您需要稍后将实现更改为新的时.