我的应用程序使用的是SQL Server 2012,EF6,MVC和Web API。
它还使用了存储库和各种文件,例如:
DatabaseFactory.cs
Disposable.cs
IDatabaseFactory.cs
IRepository.cs
IUnitOfWork.cs
RepositoryBase.cs
UnitOfWork.cs
我们已经在控制器和存储库之间使用了服务层 对于一些复杂的业务逻辑。我们没有计划改变到不同的数据库,并且已经指出了 在我看来,最近的想法是EF6是一个存储库,所以为什么要构建 另一个存储库和为什么我上面有所有文件。
我开始认为这是一种明智的做法。
有没有人知道那些没有a实现EF6的例子 存储库,与服务层。我在网上的搜索显示了很多 复杂的代码示例,看起来过于复杂,完全没有任何理由。
我的问题也在使用服务层时,我在哪里放置:
context = new EFDbContext()
在控制器,服务层或两者中?我读到我可以通过依赖注入来做到这一点。我已经将Unity用作IOC,但我不知道如何做到这一点。
答案 0 :(得分:8)
实体框架 IS 已经是工作单元模式实现以及通用存储库实现(DbContext是UoW,DbSet是通用存储库)。我同意大多数应用程序在其上设置另一个UoW或Generic Repository的方式有点过分(此外,GenericRepsitory被一些人认为是反模式)。
服务层可以充当具体的存储库,它具有封装特定于您的业务需求的数据逻辑的许多好处。如果使用这个,那么就没有必要在它上面构建一个存储库(除非你希望能够改变你的后端服务技术,比如说从WCF到WebApi或其他......)
我会将您的所有数据访问权限放在服务层中。不要在控制器中进行数据访问。这会将您的数据层泄漏到您的UI层,这只是糟糕的设计。它违反了许多核心SOLID概念。
但除非您的应用程序非常复杂并且打算在多个环境中工作,否则您不需要额外的UnitOfWork或其他层,除非您的应用程序非常复杂,并且打算在多个环境中工作...
答案 1 :(得分:6)
如果您安装Unity.Mvc*
和Unity.WebAPI*
Nuget包并将其添加到项目中,则为ASP.NET MVC和WebAPI设置Unity非常简单。 (*
是版本号,例如3或4或5.查找适合您项目的版本。例如,链接to the Unity.Mvc 5 package和to the Untity.WebAPI 5 package。)
this blog post中解释了这些软件包的用法。
构建块大致如此:
构建一个统一容器并在那里注册所有依赖项,尤其是EF上下文:
private static IUnityContainer BuildContainer()
{
var container = new UnityContainer();
container.RegisterType<MyContext>(new HierarchicalLifetimeManager());
container.RegisterType<IOrderService, OrderService>();
container.RegisterType<ICustomerService, CustomerService>();
container.RegisterType<IEmailMessenger, EmailMessenger>();
// etc., etc.
return container;
}
MyContext
是您派生的DbContext
课程。使用HierarchicalLifetimeManager
注册上下文非常重要,因为它将确保每个Web请求的新上下文将在每个请求结束时由容器实例化和处理。
如果您没有服务的接口,但只是具体的类,您可以删除注册接口的上面的行。如果需要将服务注入控制器,Unity将只创建具体服务类的实例。
构建容器后,您可以在Application_Start
global.asax
protected void Application_Start()
{
var container = ...BuildContainer();
// MVC
DependencyResolver.SetResolver(
new Unity.MvcX.UnityDependencyResolver(container));
// WebAPI
GlobalConfiguration.Configuration.DependencyResolver =
new Unity.WebApiX.UnityDependencyResolver(container);
}
中将其注册为MVC和WebAPI的依赖项解析器:
DependencyResolver
设置CustomerController
后,如果可以使用已注册的类型解析参数,则框架能够实例化在其构造函数中获取参数的控制器。例如,您现在可以创建一个CustomerService
来注入EmailMessenger
和public class CustomerController : Controller
{
private readonly ICustomerService _customerService;
private readonly IEmailMessenger _emailMessenger;
public CustomerController(
ICustomerService customerService,
IEmailMessenger emailMessenger)
{
_customerService = customerService;
_emailMessenger = emailMessenger;
}
// now you can interact with _customerService and _emailMessenger
// in your controller actions
}
:
ApiController
这同样适用于WebAPI的派生public class CustomerService // : ICustomerService
{
private readonly MyContext _myContext;
public CustomerService(MyContext myContext)
{
_myContext = myContext;
}
// now you can interact with _myContext in your service methods
}
。
服务可以依赖上下文实例与Entity Framework进行交互,如下所示:
context = new MyContext()
当MVC / WebAPI框架实例化控制器时,它将注入已注册的服务实例并解析它们自己的依赖关系,即将注册的上下文注入服务构造函数。您将注入控制器的所有服务将在单个请求期间接收相同的上下文实例。
使用此设置,您通常不需要context.Dispose()
或{{1}},因为IOC容器将管理上下文生命周期。
答案 2 :(得分:1)
如果您没有使用存储库,那么我假设您可以在某处编写您的服务操作将使用的逻辑/处理。我将在该逻辑/进程类方法中创建Context的新实例并直接使用其方法。最后,可能在“使用”之后立即将其丢弃。
处理方法最终会将返回/处理的数据转换为服务返回控制器的数据/消息合同。
保持数据逻辑与Controller完全分离。还要将视图模型与数据合同分开。
答案 3 :(得分:0)
如果您继续使用此架构,您将把Entity Framework与您的服务或控制器紧密耦合。存储库抽象为您提供了几个方面:
1)您将来可以轻松交换数据访问技术
2)您可以模拟数据存储,使您可以轻松地对数据访问代码进行单元测试
您想知道在哪里放置您的EF上下文。使用实体框架的一个好处是,它上面的所有操作都已注册到事务中。您需要确保任何数据访问代码都使用相同的上下文来享受此优势。
解决该问题的设计模式是工作单元模式,根据事物的外观,您已经在使用。我强烈建议继续使用它。否则,您需要在控制器中初始化您的上下文,将其传递给您的服务,该服务需要将其传递给与其交互的任何其他服务。
查看您列出的对象,似乎是尝试使用企业架构最佳实践构建此应用程序。虽然抽象确实引入了复杂性,但毫无疑问它们提供的好处。