C# 服务定位器和依赖注入

C# 服务定位器和依赖注入,c#,dependency-injection,inversion-of-control,C#,Dependency Injection,Inversion Of Control,我想大家都同意跟随是不好的 public class Foo { private IService _service; public Foo() { _service = IocContainer.Resolve<IService>(); } } 然而,现在由消费者来提供服务。消费者当然也可能需要构造函数中的IService,但当层次结构变得更深时,它似乎会让人恼火。在某个时刻,有人需要从IoC容器请求iSeries设备-但是什么时候

我想大家都同意跟随是不好的

public class Foo
{
    private IService _service;
    public Foo()
    {
        _service = IocContainer.Resolve<IService>();
    }
}
然而,现在由消费者来提供服务。消费者当然也可能需要构造函数中的IService,但当层次结构变得更深时,它似乎会让人恼火。在某个时刻,有人需要从IoC容器请求iSeries设备-但是什么时候

我工作场所的一位前同事为UoW/存储库模式编写了UnitOfWork类,如下所示(使用Microsoft ServiceLocator):

如何获取处理器实例和工作单元?可以将其注入forms类吗


我想我的问题可以归结为:如果我在一个没有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();
        }
    }
}  
公共接口IUnitOfWorkFactory
{
i工作单元创建();
}
公共接口IUnitOfWork:IDisposable
{
T GetRepository();
无效提交();
}
公共类MyController
{    
私有只读IUnitOfWorkFactory\u unitOfWorkFactory;
公共MyController(IUnitOfWorkFactory unitOfWorkFactory)
{
_unitOfWorkFactory=unitOfWorkFactory;
}
公共void RunTasks()
{
使用(var unitOfWork=\u unitOfWorkFactory.Create())
{
var rep=UnitOfWork.GetRepository();
var newTasks=来自rep.GetAll()中的t
其中t.IsCompleted==false
选择t;
foreach(newtask中的var任务)
{
//做点什么
}               
unitOfWork.Commit();
}
}
}  
拥有工厂的好处是,它允许用户(控制器)控制工作单元的创建和销毁

这也使得单元测试更容易,因为您不需要使用IoC进行测试。我也不喜欢使用全局上下文(比如
UnitOfWork.Current
),因为很难确定UoW何时被释放或提交


如果另一个类需要一个UoW实例来向现有上下文添加额外的工作,您可以传入一个特定的实例。

使用第一个示例,容器将同时构造
IFoo
IService
。下面是一些真实的代码来说明:

        container.RegisterType<ISubmittingService, GnipSubmittingService>(
            new DisposingTransientLifetimeManager(),
            new InjectionConstructor(
                typeof (IGnipHistoricConnection),
                typeof (IUserDataInterface),
                new EstimateVerboseLoggingService.TitleBuilder(),
                new EstimateVerboseLoggingService.FixedEndDateBuilder(),
                typeof (ISendEmailService),
                addresses,
                typeof (ILog)
                )
            );

        container.RegisterType<IEstimateService, EstimateVerboseLoggingService>(
            new DisposingTransientLifetimeManager(),
            new InjectionConstructor(
                typeof(IEstimateDataInterface),
                typeof(ISubmittingService),
                typeof(ILog)
                )
            );

公共提交服务(
点火连接gnip,
IUserDataInterface用户数据库,
Ibilder titleBuilder,
IBuilder endDateBuilder,
ISendEmailService电子邮件服务,
IEnumerable errorEmailRecipients,
ILog日志)
{
_gnip=gnip;
_userDb=userDb;
_标题生成器=标题生成器;
_endDateBuilder=endDateBuilder;
_emailService=emailService;
_errorEmailRecipients=errorEmailRecipients;
_log=log;
}

在此代码中,
EstimateVerboseLoggingService
使用
ISubmitingService
。这两种实现都在容器中指定。

关于问题的第一部分:

消费者当然可能需要构造函数中的IService 同样,但当层次结构变得复杂时,这似乎令人恼火 更深

否,消费者不需要
iSeries设备
,它需要
IFoo
。它不知道它将得到的
IFoo
依赖于
IService
,只有您的DI配置知道这一点。因此,不要担心,您不会得到您描述的依赖层次结构

在某个时候,需要有人向国际奥委会申请许可证 容器-但何时

这只会发生在你的生活中。因此,如果它是一个MVC应用程序,您已经以某种方式配置了MVC框架,以便在需要实例化控制器时使用DI配置,因此框架内部决定(通过路由)它需要一个
MyController
,它执行类似于
resolver.Get()
的操作。所以服务位置只在上面使用,而不是在控制器或其他任何地方

关于问题的
MyController
部分:

无法真正获得与前一部分的连接,但仍然可以使用构造函数注入。没有静态类(没有注入,因此不能为测试目的交换或模拟),没有服务位置


[作为旁注,您甚至可以避免使用关于工作单元的额外代码(可能您使用的ORM有一个,并且您已经在实现
IRepositories
时使用了它)。也许您的存储库可以有一个
SaveChanges
方法,该方法将调用unitOfWork的
SaveChanges
-但这是一个偏好问题,与前面的讨论不相关]。

我有点明白您的意思,但它仍然让我对您所处的类没有通过IoC实例化的情况感到困惑。我编辑了我的问题以反映这一点。有数千个类的解决方案怎么样?如果所有类都需要一个接口,并且必须绑定到IoC中,那么组合不是变得非常复杂,可能会因为缺少绑定和/或循环依赖而导致运行时错误吗?@sondergard有一些方法可以通过使用约定来避免将接口显式绑定到实现(检查DI框架的操作方法)。您可以在不同的类/模块中组织DI配置,以便每个类/模块都可以管理。未从组合根实例化的类,例如通过fac实例化的类
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();
    }
}
public class MainWindow : Form
{
    public MainWindow()
    {
    }

    private void OnUserCalculateClick(object sender, EventArgs args)
    {
        // Get UoW to connect to DB
        // Get instance of processor
    }
}
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();
        }
    }
}  
        container.RegisterType<ISubmittingService, GnipSubmittingService>(
            new DisposingTransientLifetimeManager(),
            new InjectionConstructor(
                typeof (IGnipHistoricConnection),
                typeof (IUserDataInterface),
                new EstimateVerboseLoggingService.TitleBuilder(),
                new EstimateVerboseLoggingService.FixedEndDateBuilder(),
                typeof (ISendEmailService),
                addresses,
                typeof (ILog)
                )
            );

        container.RegisterType<IEstimateService, EstimateVerboseLoggingService>(
            new DisposingTransientLifetimeManager(),
            new InjectionConstructor(
                typeof(IEstimateDataInterface),
                typeof(ISubmittingService),
                typeof(ILog)
                )
            );
    public EstimateVerboseLoggingService(
        IEstimateDataInterface estimateData,
        ISubmittingService submittingService,
        ILog log)
    {
        _estimateData = estimateData;
        _submittingService = submittingService;
        _log = log;
    }
    public GnipSubmittingService(
        IGnipHistoricConnection gnip,
        IUserDataInterface userDb,
        IBuilder<string, int> titleBuilder,
        IBuilder<DateTime, DateTime> endDateBuilder,
        ISendEmailService emailService,
        IEnumerable<MailAddress> errorEmailRecipients,
        ILog log)
    {
        _gnip = gnip;
        _userDb = userDb;
        _titleBuilder = titleBuilder;
        _endDateBuilder = endDateBuilder;
        _emailService = emailService;
        _errorEmailRecipients = errorEmailRecipients;
        _log = log;
    }