洋葱架构,工作单元和通用存储库模式

时间:2022-03-14 02:17:25

This is the first time I am implementing a more domain-driven design approach. I have decided to try the Onion Architecture as it focuses on the domain rather than on infrastructure/platforms/etc.

这是我第一次实施更多以域驱动的设计方法。我决定尝试使用Onion Architecture,因为它专注于域而不是基础架构/平台/等。

洋葱架构,工作单元和通用存储库模式

In order to abstract away from Entity Framework, I have created a generic repository with a Unit of Work implementation.

为了从实体框架中抽象出来,我创建了一个带有工作单元实现的通用存储库。

The IRepository<T> and IUnitOfWork interfaces:

IRepository 和IUnitOfWork接口:

public interface IRepository<T>
{
    void Add(T item);

    void Remove(T item);

    IQueryable<T> Query();
}

public interface IUnitOfWork : IDisposable
{
    void SaveChanges();
}

Entity Framework implementations of IRepository<T> and IUnitOfWork:

IRepository 和IUnitOfWork的实体框架实现:

public class EntityFrameworkRepository<T> : IRepository<T> where T : class
{
    private readonly DbSet<T> dbSet;

    public EntityFrameworkRepository(IUnitOfWork unitOfWork)
    {
        var entityFrameworkUnitOfWork = unitOfWork as EntityFrameworkUnitOfWork;

        if (entityFrameworkUnitOfWork == null)
        {
            throw new ArgumentOutOfRangeException("Must be of type EntityFrameworkUnitOfWork");
        }

        dbSet = entityFrameworkUnitOfWork.GetDbSet<T>();
    }

    public void Add(T item)
    {
        dbSet.Add(item);
    }

    public void Remove(T item)
    {
        dbSet.Remove(item);
    }

    public IQueryable<T> Query()
    {
        return dbSet;
    }
}

public class EntityFrameworkUnitOfWork : IUnitOfWork
{
    private readonly DbContext context;

    public EntityFrameworkUnitOfWork()
    {
        this.context = new CustomerContext();;
    }

    internal DbSet<T> GetDbSet<T>()
        where T : class
    {
        return context.Set<T>();
    }

    public void SaveChanges()
    {
        context.SaveChanges();
    }

    public void Dispose()
    {
        context.Dispose();
    }
}

The Customer repository:

客户存储库:

public interface ICustomerRepository : IRepository<Customer>
{

}

public class CustomerRepository : EntityFrameworkRepository<Customer>, ICustomerRepository 
{
    public CustomerRepository(IUnitOfWork unitOfWork): base(unitOfWork)
    {
    }
}

ASP.NET MVC controller using the repository:

使用存储库的ASP.NET MVC控制器:

public class CustomerController : Controller
{
    UnityContainer container = new UnityContainer();

    public ActionResult List()
    {
        var unitOfWork = container.Resolve<IUnitOfWork>();
        var customerRepository = container.Resolve<ICustomerRepository>();

        return View(customerRepository.Query());
    }

    [HttpPost]
    public ActionResult Create(Customer customer)
    {
        var unitOfWork = container.Resolve<IUnitOfWork>();
        var customerRepository = container.Resolve<ICustomerRepository>();; 

        customerRepository.Add(customer);

        unitOfWork.SaveChanges();

        return RedirectToAction("List");
    }
}

Dependency injection with unity:

统一依赖注入:

container.RegisterType<IUnitOfWork, EntityFrameworkUnitOfWork>();
container.RegisterType<ICustomerRepository, CustomerRepository>();

Solution:

解:

洋葱架构,工作单元和通用存储库模式

PROBLEMS?

问题?

  • Repository implementation (EF code) is very generic. It all sits in side the EntityFrameworkRepository<T> class. Concrete model repositories do not contain any of this logic. This saves me from writing tons of redundant code, but possibly sacrifices flexibility?

    存储库实现(EF代码)非常通用。它们都位于EntityFrameworkRepository 类的旁边。具体模型存储库不包含任何此逻辑。这使我免于编写大量冗余代码,但可能会牺牲灵活性?

  • The ICustomerRepository and CustomerRepository classes are basically empty. They are purely there to provide abstraction. As far as I understand, this fits with the vision of the Onion Architecture, where infrastructure and platform-dependent code sits on the outside of your system, but having empty classes and empty interfaces feels wrong?

    ICustomerRepository和CustomerRepository类基本上是空的。它们纯粹是为了提供抽象。据我所知,这符合洋葱架构的愿景,其中基础架构和平台相关的代码位于系统的外部,但是空类和空接口感觉不对?

  • To use a different persistence implementation (say Azure Table Storage), then a new CustomerRepository class would need to be created and would inherit a AzureTableStorageRepository<T>. But this could lead to redundant code (multiple CustomerRepositories)? How would this effect mocking?

    要使用不同的持久性实现(比如Azure表存储),需要创建一个新的CustomerRepository类,并继承AzureTableStorageRepository 。但这可能导致冗余代码(多个CustomerRepositories)?这种效果会如何嘲弄?

  • Another implementation (say Azure Table Storage) has limitations on transnational support so the a AzureTableStorageUnitOfWork class wouldn't work in this context.

    另一个实现(比如Azure表存储)对跨国支持有限制,因此AzureTableStorageUnitOfWork类在此上下文中不起作用。

Are there any other issues with the way I have done this?

我这样做的方式还有其他问题吗?

(I have taken most of my inspiration from this post)

(我从这篇文章中获取了大部分灵感)

3 个解决方案

#1


23  

I can say that this code is good enough for the first time try but it does have some places to improve.

我可以说这个代码第一次尝试已经足够好但是确实有一些地方需要改进。

Let's go through some of them.

让我们来看看其中的一些。

1. Dependency injection (DI) and usage of IoC.

1.依赖注入(DI)和IoC的使用。

You use the simplest version of Service Locator pattern - container instance itself.

您使用最简单的Service Locator模式 - 容器实例本身。

I suggest you use 'constructor injection'. You can find more information here (ASP.NET MVC 4 Dependency Injection).

我建议你使用'构造函数注入'。您可以在此处找到更多信息(ASP.NET MVC 4依赖注入)。

public class CustomerController : Controller
{
    private readonly IUnitOfWork unitOfWork;
    private readonly ICustomerRepository customerRepository;

    public CustomerController(
        IUnitOfWork unitOfWork, 
        ICustomerRepository customerRepository)
    {
        this.unitOfWork = unitOfWork;
        this.customerRepository = customerRepository;
    }

    public ActionResult List()
    {
        return View(customerRepository.Query());
    }

    [HttpPost]
    public ActionResult Create(Customer customer)
    {
        customerRepository.Add(customer);
        unitOfWork.SaveChanges();
        return RedirectToAction("List");
    }
}

2. Unit of Work (UoW) scope.

2.工作单位(UoW)范围。

I can't find lifestyle of IUnitOfWork and ICustomerRepository. I am not familiar with Unity but msdn says that TransientLifetimeManager is used by default. It means that you'll get a new instance every time when you resolve type.

我找不到IUnitOfWork和ICustomerRepository的生活方式。我不熟悉Unity,但msdn说默认使用TransientLifetimeManager。这意味着每次解析类型时都会获得一个新实例。

So, the following test fails:

因此,以下测试失败:

[Test]
public void MyTest()
{
    var target = new UnityContainer();
    target.RegisterType<IUnitOfWork, EntityFrameworkUnitOfWork>();
    target.RegisterType<ICustomerRepository, CustomerRepository>();

    //act
    var unitOfWork1 = target.Resolve<IUnitOfWork>();
    var unitOfWork2 = target.Resolve<IUnitOfWork>();

    // assert
    // This Assert fails!
    unitOfWork1.Should().Be(unitOfWork2);
} 

And I expect that instance of UnitOfWork in your controller differs from the instance of UnitOfWork in your repository. Sometimes it may be resulted in bugs. But it is not highlighted in the ASP.NET MVC 4 Dependency Injection as an issue for Unity.

我希望您的控制器中的UnitOfWork实例与您的存储库中的UnitOfWork实例不同。有时可能会导致错误。但它并没有在ASP.NET MVC 4依赖注入中突出显示为Unity的一个问题。

In Castle Windsor PerWebRequest lifestyle is used to share the same instance of type within single http request.

在Castle Windsor中,PerWebRequest生活方式用于在单个http请求*享相同的类型实例。

It is common approach when UnitOfWork is a PerWebRequest component. Custom ActionFilter can be used in order to invoke Commit() during invocation of OnActionExecuted() method.

当UnitOfWork是PerWebRequest组件时,这是常见的方法。可以使用自定义ActionFilter,以便在调用OnActionExecuted()方法期间调用Commit()。

I would also rename the SaveChanges() method and call it simply Commit as it is called in the example and in the PoEAA.

我还将重命名SaveChanges()方法,并在示例和PoEAA中调用它,就像调用它一样。

public interface IUnitOfWork : IDisposable
{
    void Commit();
}

3.1. Dependencies on repositories.

3.1。对存储库的依赖性。

If your repositories are going to be 'empty' it is not needed to create specific interfaces for them. It is possible to resolve IRepository<Customer> and have the following code in your controller

如果您的存储库将“空”,则不需要为它们创建特定的接口。可以解析IRepository 并在控制器中包含以下代码

public CustomerController(
    IUnitOfWork unitOfWork, 
    IRepository<Customer> customerRepository)
{
    this.unitOfWork = unitOfWork;
    this.customerRepository = customerRepository;
}

There is a test that tests it.

有一个测试它的测试。

[Test]
public void MyTest()
{
    var target = new UnityContainer();
    target.RegisterType<IRepository<Customer>, CustomerRepository>();

    //act
    var repository = target.Resolve<IRepository<Customer>>();

    // assert
    repository.Should().NotBeNull();
    repository.Should().BeOfType<CustomerRepository>();
}

But if you would like to have repositories that are 'layer of abstraction over the mapping layer where query construction code is concentrated.' (PoEAA, Repository)

但是,如果您希望拥有的存储库是映射层的抽象层,而查询构造代码则集中在该层上。 (PoEAA,Repository)

A Repository mediates between the domain and data mapping layers, acting like an in-memory domain object collection. Client objects construct query specifications declaratively and submit them to Repository for satisfaction.

存储库在域和数据映射层之间进行调解,其作用类似于内存中的域对象集合。客户端对象以声明方式构造查询规范,并将其提交到存储库以获得满意。

3.2. Inheritance on EntityFrameworkRepository.

3.2。 EntityFrameworkRepository的继承。

In this case I would create a simple IRepository

在这种情况下,我会创建一个简单的IRepository

public interface IRepository
{
    void Add(object item);

    void Remove(object item);

    IQueryable<T> Query<T>() where T : class;
}

and its implementation that knows how to work with EntityFramework infrastructure and can be easily replaced by another one (e.g. AzureTableStorageRepository).

及其实现,知道如何使用EntityFramework基础结构,并可以很容易地被另一个(例如AzureTableStorageRepository)替换。

public class EntityFrameworkRepository : IRepository
{
    public readonly EntityFrameworkUnitOfWork unitOfWork;

    public EntityFrameworkRepository(IUnitOfWork unitOfWork)
    {
        var entityFrameworkUnitOfWork = unitOfWork as EntityFrameworkUnitOfWork;

        if (entityFrameworkUnitOfWork == null)
        {
            throw new ArgumentOutOfRangeException("Must be of type EntityFrameworkUnitOfWork");
        }

        this.unitOfWork = entityFrameworkUnitOfWork;
    }

    public void Add(object item)
    {
        unitOfWork.GetDbSet(item.GetType()).Add(item);
    }

    public void Remove(object item)
    {
        unitOfWork.GetDbSet(item.GetType()).Remove(item);
    }

    public IQueryable<T> Query<T>() where T : class
    {
        return unitOfWork.GetDbSet<T>();
    }
}

public interface IUnitOfWork : IDisposable
{
    void Commit();
}

public class EntityFrameworkUnitOfWork : IUnitOfWork
{
    private readonly DbContext context;

    public EntityFrameworkUnitOfWork()
    {
        this.context = new CustomerContext();
    }

    internal DbSet<T> GetDbSet<T>()
        where T : class
    {
        return context.Set<T>();
    }

    internal DbSet GetDbSet(Type type)
    {
        return context.Set(type);
    }

    public void Commit()
    {
        context.SaveChanges();
    }

    public void Dispose()
    {
        context.Dispose();
    }
}

And now CustomerRepository can be a proxy and refer to it.

现在CustomerRepository可以作为代理并引用它。

public interface IRepository<T> where T : class
{
    void Add(T item);

    void Remove(T item);
}

public abstract class RepositoryBase<T> : IRepository<T> where T : class
{
    protected readonly IRepository Repository;

    protected RepositoryBase(IRepository repository)
    {
        Repository = repository;
    }

    public void Add(T item)
    {
        Repository.Add(item);
    }

    public void Remove(T item)
    {
        Repository.Remove(item);
    }
}

public interface ICustomerRepository : IRepository<Customer>
{
    IList<Customer> All();

    IList<Customer> FindByCriteria(Func<Customer, bool> criteria);
}

public class CustomerRepository : RepositoryBase<Customer>, ICustomerRepository
{
    public CustomerRepository(IRepository repository)
        : base(repository)
    { }

    public IList<Customer> All()
    {
        return Repository.Query<Customer>().ToList();
    }

    public IList<Customer> FindByCriteria(Func<Customer, bool> criteria)
    {
        return Repository.Query<Customer>().Where(criteria).ToList();
    }
}

#2


2  

The only con I see is that you are highly dependent on your IOC tool, so make sure your implementation is solid. However, this is not unique to Onion designs. I have used Onion on a number of projects and have not run into any real 'gotchas".

我唯一看到的是你高度依赖你的IOC工具,所以要确保你的实现是可靠的。然而,这并非洋葱设计所独有。我在一些项目中使用了Onion,并没有碰到任何真正的“陷阱”。

#3


0  

I see couple of serious problems in code.

我在代码中看到了几个严重的问题。

The 1st problem is relashionship between repositories and UoW.

第一个问题是存储库和UoW之间的关系。

    var unitOfWork = container.Resolve<IUnitOfWork>();
    var customerRepository = container.Resolve<ICustomerRepository>();

Here is implicit dependency. Repository will not work itself without UoW! Not all repositories needs to be connected with UoW. For example what about stored procedures? You have stored procedure and you hide it behind repository. Stored procedure invokation uses separate transaction! At least not in all cases. So if I resolve the only repository and add item then it will not work. Moreover this code will not work if I set Transient life license because repository will have another UoW instance. So we have tight implicit coupling.

这是隐式依赖。没有UoW,存储库将无法正常工作!并非所有存储库都需要与UoW连接。例如存储过程怎么样?您有存储过程,并将其隐藏在存储库后面。存储过程调用使用单独的事务!至少在所有情况下都不是。因此,如果我解决了唯一的存储库并添加项目,那么它将无法正常工作。此外,如果我设置Transient生活许可证,则此代码将不起作用,因为存储库将具有另一个UoW实例。所以我们有紧密的隐式耦合。

The 2nd problem you create tight coupling between DI container engine and use it as service locator! Service locator is not good approach to implement IoC and aggregation. In some case it is anti pattern. DI container should be used

第二个问题是你在DI容器引擎之间建立紧密耦合并将其用作服务定位器!服务定位器不是实现IoC和聚合的好方法。在某些情况下,它是反模式。应使用DI容器

#1


23  

I can say that this code is good enough for the first time try but it does have some places to improve.

我可以说这个代码第一次尝试已经足够好但是确实有一些地方需要改进。

Let's go through some of them.

让我们来看看其中的一些。

1. Dependency injection (DI) and usage of IoC.

1.依赖注入(DI)和IoC的使用。

You use the simplest version of Service Locator pattern - container instance itself.

您使用最简单的Service Locator模式 - 容器实例本身。

I suggest you use 'constructor injection'. You can find more information here (ASP.NET MVC 4 Dependency Injection).

我建议你使用'构造函数注入'。您可以在此处找到更多信息(ASP.NET MVC 4依赖注入)。

public class CustomerController : Controller
{
    private readonly IUnitOfWork unitOfWork;
    private readonly ICustomerRepository customerRepository;

    public CustomerController(
        IUnitOfWork unitOfWork, 
        ICustomerRepository customerRepository)
    {
        this.unitOfWork = unitOfWork;
        this.customerRepository = customerRepository;
    }

    public ActionResult List()
    {
        return View(customerRepository.Query());
    }

    [HttpPost]
    public ActionResult Create(Customer customer)
    {
        customerRepository.Add(customer);
        unitOfWork.SaveChanges();
        return RedirectToAction("List");
    }
}

2. Unit of Work (UoW) scope.

2.工作单位(UoW)范围。

I can't find lifestyle of IUnitOfWork and ICustomerRepository. I am not familiar with Unity but msdn says that TransientLifetimeManager is used by default. It means that you'll get a new instance every time when you resolve type.

我找不到IUnitOfWork和ICustomerRepository的生活方式。我不熟悉Unity,但msdn说默认使用TransientLifetimeManager。这意味着每次解析类型时都会获得一个新实例。

So, the following test fails:

因此,以下测试失败:

[Test]
public void MyTest()
{
    var target = new UnityContainer();
    target.RegisterType<IUnitOfWork, EntityFrameworkUnitOfWork>();
    target.RegisterType<ICustomerRepository, CustomerRepository>();

    //act
    var unitOfWork1 = target.Resolve<IUnitOfWork>();
    var unitOfWork2 = target.Resolve<IUnitOfWork>();

    // assert
    // This Assert fails!
    unitOfWork1.Should().Be(unitOfWork2);
} 

And I expect that instance of UnitOfWork in your controller differs from the instance of UnitOfWork in your repository. Sometimes it may be resulted in bugs. But it is not highlighted in the ASP.NET MVC 4 Dependency Injection as an issue for Unity.

我希望您的控制器中的UnitOfWork实例与您的存储库中的UnitOfWork实例不同。有时可能会导致错误。但它并没有在ASP.NET MVC 4依赖注入中突出显示为Unity的一个问题。

In Castle Windsor PerWebRequest lifestyle is used to share the same instance of type within single http request.

在Castle Windsor中,PerWebRequest生活方式用于在单个http请求*享相同的类型实例。

It is common approach when UnitOfWork is a PerWebRequest component. Custom ActionFilter can be used in order to invoke Commit() during invocation of OnActionExecuted() method.

当UnitOfWork是PerWebRequest组件时,这是常见的方法。可以使用自定义ActionFilter,以便在调用OnActionExecuted()方法期间调用Commit()。

I would also rename the SaveChanges() method and call it simply Commit as it is called in the example and in the PoEAA.

我还将重命名SaveChanges()方法,并在示例和PoEAA中调用它,就像调用它一样。

public interface IUnitOfWork : IDisposable
{
    void Commit();
}

3.1. Dependencies on repositories.

3.1。对存储库的依赖性。

If your repositories are going to be 'empty' it is not needed to create specific interfaces for them. It is possible to resolve IRepository<Customer> and have the following code in your controller

如果您的存储库将“空”,则不需要为它们创建特定的接口。可以解析IRepository 并在控制器中包含以下代码

public CustomerController(
    IUnitOfWork unitOfWork, 
    IRepository<Customer> customerRepository)
{
    this.unitOfWork = unitOfWork;
    this.customerRepository = customerRepository;
}

There is a test that tests it.

有一个测试它的测试。

[Test]
public void MyTest()
{
    var target = new UnityContainer();
    target.RegisterType<IRepository<Customer>, CustomerRepository>();

    //act
    var repository = target.Resolve<IRepository<Customer>>();

    // assert
    repository.Should().NotBeNull();
    repository.Should().BeOfType<CustomerRepository>();
}

But if you would like to have repositories that are 'layer of abstraction over the mapping layer where query construction code is concentrated.' (PoEAA, Repository)

但是,如果您希望拥有的存储库是映射层的抽象层,而查询构造代码则集中在该层上。 (PoEAA,Repository)

A Repository mediates between the domain and data mapping layers, acting like an in-memory domain object collection. Client objects construct query specifications declaratively and submit them to Repository for satisfaction.

存储库在域和数据映射层之间进行调解,其作用类似于内存中的域对象集合。客户端对象以声明方式构造查询规范,并将其提交到存储库以获得满意。

3.2. Inheritance on EntityFrameworkRepository.

3.2。 EntityFrameworkRepository的继承。

In this case I would create a simple IRepository

在这种情况下,我会创建一个简单的IRepository

public interface IRepository
{
    void Add(object item);

    void Remove(object item);

    IQueryable<T> Query<T>() where T : class;
}

and its implementation that knows how to work with EntityFramework infrastructure and can be easily replaced by another one (e.g. AzureTableStorageRepository).

及其实现,知道如何使用EntityFramework基础结构,并可以很容易地被另一个(例如AzureTableStorageRepository)替换。

public class EntityFrameworkRepository : IRepository
{
    public readonly EntityFrameworkUnitOfWork unitOfWork;

    public EntityFrameworkRepository(IUnitOfWork unitOfWork)
    {
        var entityFrameworkUnitOfWork = unitOfWork as EntityFrameworkUnitOfWork;

        if (entityFrameworkUnitOfWork == null)
        {
            throw new ArgumentOutOfRangeException("Must be of type EntityFrameworkUnitOfWork");
        }

        this.unitOfWork = entityFrameworkUnitOfWork;
    }

    public void Add(object item)
    {
        unitOfWork.GetDbSet(item.GetType()).Add(item);
    }

    public void Remove(object item)
    {
        unitOfWork.GetDbSet(item.GetType()).Remove(item);
    }

    public IQueryable<T> Query<T>() where T : class
    {
        return unitOfWork.GetDbSet<T>();
    }
}

public interface IUnitOfWork : IDisposable
{
    void Commit();
}

public class EntityFrameworkUnitOfWork : IUnitOfWork
{
    private readonly DbContext context;

    public EntityFrameworkUnitOfWork()
    {
        this.context = new CustomerContext();
    }

    internal DbSet<T> GetDbSet<T>()
        where T : class
    {
        return context.Set<T>();
    }

    internal DbSet GetDbSet(Type type)
    {
        return context.Set(type);
    }

    public void Commit()
    {
        context.SaveChanges();
    }

    public void Dispose()
    {
        context.Dispose();
    }
}

And now CustomerRepository can be a proxy and refer to it.

现在CustomerRepository可以作为代理并引用它。

public interface IRepository<T> where T : class
{
    void Add(T item);

    void Remove(T item);
}

public abstract class RepositoryBase<T> : IRepository<T> where T : class
{
    protected readonly IRepository Repository;

    protected RepositoryBase(IRepository repository)
    {
        Repository = repository;
    }

    public void Add(T item)
    {
        Repository.Add(item);
    }

    public void Remove(T item)
    {
        Repository.Remove(item);
    }
}

public interface ICustomerRepository : IRepository<Customer>
{
    IList<Customer> All();

    IList<Customer> FindByCriteria(Func<Customer, bool> criteria);
}

public class CustomerRepository : RepositoryBase<Customer>, ICustomerRepository
{
    public CustomerRepository(IRepository repository)
        : base(repository)
    { }

    public IList<Customer> All()
    {
        return Repository.Query<Customer>().ToList();
    }

    public IList<Customer> FindByCriteria(Func<Customer, bool> criteria)
    {
        return Repository.Query<Customer>().Where(criteria).ToList();
    }
}

#2


2  

The only con I see is that you are highly dependent on your IOC tool, so make sure your implementation is solid. However, this is not unique to Onion designs. I have used Onion on a number of projects and have not run into any real 'gotchas".

我唯一看到的是你高度依赖你的IOC工具,所以要确保你的实现是可靠的。然而,这并非洋葱设计所独有。我在一些项目中使用了Onion,并没有碰到任何真正的“陷阱”。

#3


0  

I see couple of serious problems in code.

我在代码中看到了几个严重的问题。

The 1st problem is relashionship between repositories and UoW.

第一个问题是存储库和UoW之间的关系。

    var unitOfWork = container.Resolve<IUnitOfWork>();
    var customerRepository = container.Resolve<ICustomerRepository>();

Here is implicit dependency. Repository will not work itself without UoW! Not all repositories needs to be connected with UoW. For example what about stored procedures? You have stored procedure and you hide it behind repository. Stored procedure invokation uses separate transaction! At least not in all cases. So if I resolve the only repository and add item then it will not work. Moreover this code will not work if I set Transient life license because repository will have another UoW instance. So we have tight implicit coupling.

这是隐式依赖。没有UoW,存储库将无法正常工作!并非所有存储库都需要与UoW连接。例如存储过程怎么样?您有存储过程,并将其隐藏在存储库后面。存储过程调用使用单独的事务!至少在所有情况下都不是。因此,如果我解决了唯一的存储库并添加项目,那么它将无法正常工作。此外,如果我设置Transient生活许可证,则此代码将不起作用,因为存储库将具有另一个UoW实例。所以我们有紧密的隐式耦合。

The 2nd problem you create tight coupling between DI container engine and use it as service locator! Service locator is not good approach to implement IoC and aggregation. In some case it is anti pattern. DI container should be used

第二个问题是你在DI容器引擎之间建立紧密耦合并将其用作服务定位器!服务定位器不是实现IoC和聚合的好方法。在某些情况下,它是反模式。应使用DI容器