我认为我已经达到了“分析瘫痪”的状态。 我有一个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();
}
所以有了这个,我得到了延迟加载的全部好处,我在完成后立即关闭连接,我对where子句的灵活性 - 所有的细节。 con - 我在一个方法中混合了一堆东西 - 数据检查,数据访问,UI交互。
使用Repository,我正在外部化数据访问,理论上,如果我决定使用ado.net或使用不同的数据库,则可以替换repos。 但是,我没有看到实现延迟加载的良好清洁方式,以及如何控制DbContext /连接的生命周期。 说,我有CRUD方法的IRepository接口,我如何加载属于给定用户的地址列表?像GetAddressListByUserId这样的方法看起来很难看,错了, 并且会让我创建一堆同样难看的方法,并且在使用ORM时没什么用。
我确信这个问题已经解决了数百万次,希望有一个解决方案..
关于存储库模式的另一个问题 - 如何处理属性对象?例如。用户有一个地址列表,您将如何检索该列表?为地址创建存储库?使用ORM,地址对象不必具有回复用户的引用,也不需要具有repo的Id字段 - 它必须拥有所有这些。更多代码,更多暴露属性..
答案 0 :(得分:7)
您选择的方法很大程度上取决于您要使用的项目类型。对于需要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);
}
由于上述原因,最好抽象您的数据访问(可测试性,易于切换数据提供者或ORM或其他等)。 Repository
模式是一个不错的选择,但在这里您还可以获得一些实现选项。关于通用/非通用存储库的讨论总是很多,无论是否应该返回IQueryable
等等。但最终你可以选择。
Get
方法来获取有或没有孩子的对象是可以的。例如。
public class CompanyRepository
{
Get(int Id);
Get(string name);
GetWithEmployees(int id);
...
}
看起来有点矫枉过正,你可能会选择不同的方法,但只要你有一个你遵循的模式,维护代码就容易多了。
答案 1 :(得分:5)
我个人这样做:
我有一个抽象的Domain层,它不仅包含CRUD方法,还包含专门的方法,例如UsersManager.Authenticate()等。它内部使用数据访问逻辑或数据访问层抽象(取决于抽象我需要)。
至少拥有一个抽象依赖总是更好。以下是它的一些优点:
从控制器本身开始,让它有2个构造函数:一个具有抽象域访问类(例如域的外观),另一个(空)构造函数选择默认实现。这样,您的控制器在Web应用程序运行时(调用空构造函数)和单元测试期间(注入模拟域层)就可以正常运行。
此外,为了能够在以后轻松切换到另一个域,请务必注入域创建者,而不是域本身。通过这种方式,将域图层构造本地化到域创建者,您可以随时切换到另一个实现,只需重新构建域创建者(创建者,我的意思是某种工厂)。
我希望这会有所帮助。
<强>加成强>:
答案 2 :(得分:0)
这真的归结为您想要代码的位置。如果您需要对对象进行数据访问,可以将其放在IRepository对象后面或控制器中并不重要:您仍将使用一系列GetByXXX调用或等效代码。无论哪种方式,您都可以延迟加载并控制连接的生命周期。所以现在你需要问问自己:我希望我的代码在哪里生效?
就个人而言,我会争辩说要把它从控制器中拿出来。我的意思是将它移到另一层。可能使用IRespository类型的模式,您可以在其中进行一系列GetByXXX调用。当然他们很难看。错误?我会反驳说。至少它们都包含在同一个逻辑层中,而不是分散在整个控制器中,它们与验证代码等混合在一起。