【问题标题】:Dependency Injection with Ninject share same objects with different instancesNinject 的依赖注入与不同的实例共享相同的对象
【发布时间】:2016-11-25 15:08:57
【问题描述】:

我不得不使用一个已经设置好的基于 Ninject DI 的应用程序,在我正在开发的应用程序的开发过程中,我已经发展并添加了相当多的应用程序。

我现在发现了一个我想纠正的问题。我已经设法使用继承来解决它,但想要一个更清洁的解决方案。

我有两个连接需要注入不同的服务和存储库。然后,我需要将存储库也正确链接到具有相同 UnitOfWork 的正确服务。

我想我可能会问一些没有继承和专业化就不可能的事情,但这就是我问的原因。

我设法通过创建主要 RepositoryUnitOfWork 类的子类来解决这个问题,但除了实现基类之外什么都不做。 我只是不喜欢完全依赖于超类功能的子类的想法,除了构造函数之外基本上是空括号,对我来说,这似乎不是真正的 OOP,只是为了解决这个问题。因此,如果可能的话,我在 DI 中寻求使用一类解决方案的更好解决方案。

因此,如果您可以忽略我所说的解决方案,因为我完全还原了更改,这就是我剩下的:

看看下面的代码你就知道目标是什么了。

...

public class UnitOfWork : IUnitOfWork
{
    private static readonly log4net.ILog log = log4net.LogManager.GetLogger("UnitOfWork");
    public DbContext DataContext { get; set; }

    public UnitOfWork(string connectionString)
    {
        DataContext = new DbContext(connectionString);
    }

    public void Commit()
    {
        ...
    }
}

...

public class Repository<T> : IRepository<T> where T : class
{
    public IUnitOfWork unitOfWork { get; set; }
    private readonly IDbSet<T> dbSet;

    //private static readonly log4net.ILog log = log4net.LogManager.GetLogger("Repository");

    public Repository(IUnitOfWork unitOfWork)
    {
        this.unitOfWork = unitOfWork;
        dbSet = this.unitOfWork.DataContext.Set<T>();
    }
    ...
}
...

public class IPOPDataModules : NinjectModule
{
    public override void Load()
    {
    Bind<IUnitOfWork>().To<UnitOfWork>().InRequestScope().WithConstructorArgument("connectionString", ConfigurationManager.ConnectionStrings["IPOP_BE_TESTEntities"].ConnectionString);
    Bind<IRepository<tOrder>>().To<Repository<tOrder>>().InRequestScope();
    }
}

...

public class DataModules : NinjectModule
{
    public override void Load()
    {
    Bind<IUnitOfWork>().To<UnitOfWork>().InRequestScope().WithConstructorArgument("connectionString", ConfigurationManager.ConnectionStrings["IPOP_BAPSEntities"].ConnectionString);
    Bind<IRepository<Data.Quote>>().To<Repository<Data.Quote>>().InRequestScope();
    }
}

...

public class QuoteService : IQuoteService
{
    private IUnitOfWork unitOfWork;
    private IRepository<Data.Quote> quoteRepository;
    public QuoteService(IUnitOfWork unitOfWork, IRepository<Data.Quote> quoteRepository)
    {
        ...
    }
}

...

public class IPOPService : IIPOPService
{
    private IUnitOfWork unitOfWork;
    private IRepository<Data.tOrder> tOrderRepository;

    public IPOPService(IUnitOfWork unitOfWork, IRepository<Data.tOrder>)
    {
        ...
    }
}

我想知道的是,是否可以通过两个不同的连接共享相同的 UnitOfWorkRepository 对象,并将它们作为不同的实例注入到各自的服务中(IPOPService 用于 IPOP_BE_TEST 连接,@987654328 @ 用于 IPOP_BAP 连接)

同样,上面的代码没有达到我想要的效果,但这是我想尝试使用的那种架构。

【问题讨论】:

    标签: c# asp.net-mvc dependency-injection ninject


    【解决方案1】:

    你的问题对我来说并不完全清楚。但是请查看以下两个范围的文档,这可能对您的场景很有趣。

    • InCallScope 将导致每个解析树只创建一个实例。我通常在桌面应用程序上将此范围用于工作单元。请参阅文档here。为此,您需要 Ninject.Extensions.NamedScope 扩展名。
    • InRequestScope 将导致在 Web 应用程序中,每个 HTTP 请求只会创建一个实例。我通常将此范围用于工作单元。请参阅文档here。为此,您需要 Ninject.Web.Common 软件包。

    【讨论】:

    • 感谢@Zoltan 的输入,我在一个MVC 网站上工作,我对这个问题做了一些修改,希望能解决引起的一些混乱。关于上面的答案,我希望能够以某种方式将正确的UnitOfWorkRepository(具有正确的数据库连接)映射到上面的两个不同服务。我想我错过了一些 Ninject 逻辑,如果可能的话。
    【解决方案2】:

    您正在寻找的是 Ninject 绑定范围。每当您声明一个绑定时,Ninject 都会为该绑定提供一个委托,激活过程使用该委托来确定它是否应该创建该服务的新实例,或者是否应该返回先前构造的实例。

    因此,如果您想在 Ninject 中实现单例,您只需声明一个如下所示的绑定:

    Bind<IRepository<Data.Quote>>().To<Repository<Data.Quote>>().InSingletonScope();
    

    InSingletonScope()InRequestScope() 只是 IBindingInSyntax&lt;T&gt; 上的 InScope(Func&lt;Ninject.Activation.IContext, object&gt; scope) 方法的糖(或在 InRequestScope 扩展方法的情况下)。任何时候你想确保 Ninject 在给定的情况下返回相同的服务实例,你需要做的就是实现一个自定义范围。

    如果我正确理解您的问题,您希望确保当请求到达您的应用程序时,Repository&lt;T&gt;IUnitOfWork 的相同实例将被注入到您应用程序中的所有服务中。在这种情况下,您只需编写如下绑定:

    Bind<IUnitOfWork>().To<UnitOfWork>().InRequestScope().WithConstructorArgument("connectionString", ConfigurationManager.ConnectionStrings["IPOP_BE_TESTEntities"].ConnectionString);
    Bind<IRepository<tOrder>>().To<Repository<tOrder>>().InRequestScope();
    

    但是,您的问题似乎是您有两个单独的模块,具有两个单独的绑定。我建议您需要使用具有上下文绑定的单个模块来确定应将哪个连接字符串提供给系统的哪个部分。所以你的一个模块可能看起来像这样:

    Bind<IUnitOfWork>()
        .To<UnitOfWork>()
        .WhenInjectedInto<IIPOPService>()
        .InRequestScope()
        .WithConstructorArgument("connectionString", ConfigurationManager.ConnectionStrings["IPOP_BE_TESTEntities"].ConnectionString);
    Bind<IUnitOfWork>()
        .To<UnitOfWork>()
        .WhenInjectedInto<IQuoteService>()
        .InRequestScope()
        .WithConstructorArgument("connectionString", ConfigurationManager.ConnectionStrings["IPOP_BAPSEntities"].ConnectionString);
    
    Bind<IRepository<tOrder>>().To<Repository<tOrder>>().InRequestScope();
    

    这样你就可以确定当 Ninject 解析 IIPOPService 时,它会创建一个 UnitOfWork 的实例,并使用 "IPOP_BE_TESTEntities" 连接字符串初始化,并且在解析 IQuoteService 时,它将使用 "IPOP_BAPSEntities" 连接字符串,但除此之外,在该请求范围内,Ninject 只会构造一个实例。

    希望这会有所帮助。

    【讨论】:

    • 感谢您的建议@s3raph86,不幸的是,有大约 40 种类似于 IIPOPService 和 IQuoteService 的服务,我想知道是否有一个更优雅的解决方案,如果可能的话,以尽可能少的代码更改来适应所有人.
    • 这不应该是一个交易破坏者@Saleh - Ninject 为上下文绑定提供了很多选项。关于决定应用一个或另一个连接字符串时需要使用的规则,您能告诉我什么?可能你可以定义一个属性来装饰你的服务,然后在你的绑定中使用它?
    • 目前该应用支持 3 个 conns 以及未来更多(上面的问题显示了 2 个示例)。我不确定是否有可能,通过最少的代码更改,我想指定一个带有 conn 的 UnitOfWork 来对多个存储库和服务说 IPOP_BAPS,同时指定另一个具有不同 conn 的 UnitOfWork 到另一组存储库和服务。此外,由于存储库被注入到服务中,因此服务中的 UnitOfwork 和它插入的存储库需要相同。不需要重用服务,但我想重用的存储库。
    • 绝对可行。一旦我有机会修补和测试,我会重新审视我的答案。
    • 请记住@Saleh,如果您想在请求范围内重用已处理的 UnitOfWork 对象,您可能会遇到问题。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-05-22
    • 1970-01-01
    • 2016-10-08
    • 1970-01-01
    • 1970-01-01
    • 2021-06-30
    • 1970-01-01
    相关资源
    最近更新 更多