son*_*ard 5 c# dependency-injection inversion-of-control
我认为普遍认为以下是不好的
public class Foo
{
private IService _service;
public Foo()
{
_service = IocContainer.Resolve<IService>();
}
}
Run Code Online (Sandbox Code Playgroud)
以下是首选(依赖注入)
public class Foo
{
private IService _service;
public Foo(IService service)
{
}
}
Run Code Online (Sandbox Code Playgroud)
但是现在由消费者提供服务.消费者当然也可以在构造函数中要求IService,但是当层次结构变得更深时,它似乎很烦人.在某些时候,有人需要从IoC容器请求IService - 但是什么时候......?
我工作场所的一位前同事为UoW/Repository模式编写了一个UnitOfWork类(使用Microsoft ServiceLocator):
public static UnitOfWork
{
public static IUnitOfWork Current
{
get { return ServiceLocator.Current.GetInstance<IUnitOfWork>(); }
}
public static void Commit()
{
Current.Commit();
}
public static void Dispose()
{
Current.Dispose();
}
public static IRepository<T> GetRepository<T>() where T : class
{
return ServiceLocator.Current.GetInstance<IRepository>();
}
}
Run Code Online (Sandbox Code Playgroud)
并使用Ninject连接IoC,因此对IRepository的请求将找到当前的UoW或在需要时创建一个新的(如果处理当前电流).使用成为
public class MyController
{
public void RunTasks()
{
var rep = UnitOfWork.GetRepository<Tasks>();
var newTasks = from t in rep.GetAll()
where t.IsCompleted == false
select t;
foreach (var task in newTasks)
{
// Do something
}
UnitOfWork.Commit();
}
}
Run Code Online (Sandbox Code Playgroud)
但它确实仍然受到静态IoC(服务定位器)类的影响,但是会有更智能的解决方案吗?在这种情况下,不需要知道内部依赖性(静态类没有逻辑),并且出于测试目的,备用IoC配置可以使用模拟设置所有内容 - 并且它易于使用.
编辑:
我将试着通过一个不同的例子澄清我的困惑.假设我有一个带有MainWindow类的标准winforms应用程序.当用户单击按钮时,我需要从数据库加载一些数据,并将其传递给将处理数据的类:
public class MainWindow : Form
{
public MainWindow()
{
}
private void OnUserCalculateClick(object sender, EventArgs args)
{
// Get UoW to connect to DB
// Get instance of processor
}
}
Run Code Online (Sandbox Code Playgroud)
我如何获得处理器的实例和工作单元?它可以注入表格类吗?
我想我的问题归结为:如果我在一个没有Ioc构建的类中,它可能是一个winform,一个ria服务类等. - 可以引用服务定位器/ IoC控制器来解析依赖关系的实例,还是有一种处理这些案件的首选方式?或者我只是做错了什么......?
我解决这个问题的方法是有一个UnitOfWorkFactory它有一个Create方法来创建你的UnitOfWork.
public interface IUnitOfWorkFactory
{
IUnitOfWork Create();
}
public interface IUnitOfWork : IDisposable
{
T GetRepository<T>();
void Commit();
}
public class MyController
{
private readonly IUnitOfWorkFactory _unitOfWorkFactory;
public MyController(IUnitOfWorkFactory unitOfWorkFactory)
{
_unitOfWorkFactory = unitOfWorkFactory;
}
public void RunTasks()
{
using (var unitOfWork = _unitOfWorkFactory.Create())
{
var rep = UnitOfWork.GetRepository<Tasks>();
var newTasks = from t in rep.GetAll()
where t.IsCompleted == false
select t;
foreach (var task in newTasks)
{
// Do something
}
unitOfWork.Commit();
}
}
}
Run Code Online (Sandbox Code Playgroud)
拥有工厂的优点是它可以让用户(控制器)控制工作单元的创建和销毁。
这也使单元测试变得更容易,因为您不需要使用 IoC 进行测试。我也不喜欢拥有可用的全局上下文(例如UnitOfWork.Current),因为很难确定 UoW 何时将被处置或提交。
如果另一个类需要 UoW 的实例来向现有上下文添加额外的工作,您可以传入特定的实例。