首页 > 解决方案 > C# .Net Core 2 DI,我如何注册一个接口,该接口也采用与构造函数相同的接口作为参数(装饰器)

问题描述

我有一个实现 IRepository 接口的存储库。我创建了一个 CachedRepository ,它也实现了 IRepository 但它采用 IRepository 作为构造函数参数。另外,我知道我最终会创建一个继承 Repository 的 Oracle11Repository 来覆盖一些 Repository 方法。

我的目标是能够在 startup.cs 中交换这 3 个存储库,而无需修改应用程序的其余部分。

如何注册 IRepository 以使用 CachedRepository,指定 CachedRepository 必须像自己的 IRepository 一样使用 Repository?

显然,我收到错误“检测到'IRepository'类型的服务的循环依赖”

或者也许是我的方法不正确?

//Repository.cs
public class Repository : IRepository
{
        private readonly IDbConnectionFactory dbFactory;
        private readonly ILogger<Repository> logger;

        public AssessmentRepository(IDbConnectionFactory dbFactory, ILogger<Repository> logger, IConfiguration configuration)
        {
            this.dbFactory = dbFactory;
            this.logger = logger;
        }

        public async Task<PagedEnvelope<SearchResult>> Search(string term)
        {}
}
//CachedRepository.cs
public class Repository : IRepository
{
        private IRepository repos;
        private readonly ILogger<Repository> logger;
        private IMemoryCache cache;

        public CachedREpository(IRepository repos, ILogger<Repository> logger,  IMemoryCache cache, IConfiguration configuration)
        {
            this.repos = repos;
            this.logger = logger;
            ConfigureCache(cache, configuration);
        }

        public async Task<PagedEnvelope<SearchResult>> Search(string term)
        {
            // If not in cache, call repos.Search(term) then cache and return 
        }
}
//Startup.cs
public void ConfigureServices(IServiceCollection services)
{
    //...
    services.AddTransient<IRepository, CachedRepository>();
}

标签: c#.netdependency-injection

解决方案


在我看来,您需要Repository为您的功能设置一个基类,以及一个基础接口,该接口将使基础与未来注入保持一致,因此可能需要重命名和新接口。所以假设你的数据库是 SQL,你的Repository类声明会变成

public class SqlRepository : IRepositoryBase
{
    // SQL Specific Search Method
}

并且基础存储库接口现在是

public interface IRepositoryBase
{
    public Task<PagedEnvelope<SearchResult>> Search(string term);
}

您现在有了一个特定于您的 SQL 访问的实现和一个用于注入的接口,然后您将其注入到您的CachedRepository类中,如下所示。

public class CachedRepository : IRepository
{
    private IRepositoryBase _reposBase;
    private IMemoryCache _cache;

    public CachedREpository(IRepositoryBase reposBase,   
                        IMemoryCache cache, 
                        IConfiguration configuration)
    {
        this._reposBase = reposBase;
        this._cache = cache;
        // Note I took the logger out, you have it in the _reposBase object now. 
        ConfigureCache(cache, configuration);
    }

    public async Task<PagedEnvelope<SearchResult>> Search(string term)
        {
            // If not in cache, call _reposBase.Search(term) then cache and return 
        }

}  

现在在您的Startup班级中,您可以使用 2 次注入来配置它:

//Startup.cs
public void ConfigureServices(IServiceCollection services)
{
    //...
    // Set up injection for Base first
    services.AddTransient<IRepositoryBase, SqlRepository>();

    // Then set up injection for your Cached repo
    services.AddTransient<IRepository, CachedRepository>();
}

现在您有了一些内置的灵活性,因为要更改为 Oracle DB,您只需创建 Oracle 存储库库:

public class OracleRepository : IRepositoryBase
{
    // Oracle Specific Search Method
}

然后在您的 DI 容器中更改行

services.AddTransient<IRepositoryBase, SqlRepository>();

 services.AddTransient<IRepositoryBase, OracleRepository>();

并且您已经通过添加 1 个新的 DB 类,然后在启动时更改 1 行来切换数据库实现,这让您的缓存行为单独存在。

上面讨论了切换基础数据库,但您当然也可以更改级别,因此CachedRepository可以切换任何实现IRepository接口的东西,因此您也可以在该层中获得灵活性。

从我读到的你的问题,我想我明白了,这似乎可以解决你的问题。不再有循环依赖。

如果没有请评论,希望这有帮助!


推荐阅读