[英]C# .Net Core 2 DI, how can I register an interface with a class that also take the same interface as constructor a parameter (Decorator)
I have a Repository that implement a IRepository interface.我有一个实现 IRepository 接口的存储库。 I have created a CachedRepository that also implement IRepository but it take a IRepository as constructor argument.
我创建了一个 CachedRepository ,它也实现了 IRepository 但它采用 IRepository 作为构造函数参数。 Also, I know that I will eventually create an Oracle11Repository that inherit Repository to override some Repository method.
另外,我知道我最终会创建一个继承 Repository 的 Oracle11Repository 来覆盖一些 Repository 方法。
My objectif is to be able to swap these 3 Repositorys in the startup.cs without modifying the rest of the app.我的目标是能够在 startup.cs 中交换这 3 个存储库,而无需修改应用程序的 rest。
How can I register the IRepository to use CachedRepository, specifying that the CachedRepository must use the Repository as is own IRepository?如何注册 IRepository 以使用 CachedRepository,指定 CachedRepository 必须像自己的 IRepository 一样使用 Repository?
Obviously, i got the error "A circular dependency was detected for the service of type 'IRepository'"显然,我收到错误“检测到'IRepository'类型的服务的循环依赖”
Or maybe it's my approch that is not correct?或者也许是我的方法不正确?
//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>();
}
It looks to me like you need to set up Repository
as a base class for your functionality, and a base interface that will make bases consistent for future injection, so a rename and a new interface might be in order.在我看来,您需要将
Repository
设置为基础 class 以实现您的功能,以及使基础与未来注入保持一致的基础接口,因此可能需要重命名和新接口。 So lets say your database is SQL, and your Repository
class declaration would become因此,假设您的数据库是 SQL,您的
Repository
class 声明将变为
public class SqlRepository : IRepositoryBase
{
// SQL Specific Search Method
}
and the base repository interface is now并且基础存储库接口现在是
public interface IRepositoryBase
{
public Task<PagedEnvelope<SearchResult>> Search(string term);
}
You now have an implementation specific to your SQL access and an interface for injection, and then you inject it into your CachedRepository
class like below.您现在有一个特定于您的 SQL 访问的实现和一个用于注入的接口,然后您将其注入到您的
CachedRepository
class 中,如下所示。
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
}
}
Now in your Startup
class, you can use 2 injections to configure it how you want it:现在在您的
Startup
class 中,您可以使用 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>();
}
Now you have some flexibility built in, because to change to an Oracle DB, you just create the Oracle repository base:现在您有了一些内置的灵活性,因为要更改为 Oracle DB,您只需创建 Oracle 存储库库:
public class OracleRepository : IRepositoryBase
{
// Oracle Specific Search Method
}
and then in your DI container just change the line然后在您的 DI 容器中更改行
services.AddTransient<IRepositoryBase, SqlRepository>();
to至
services.AddTransient<IRepositoryBase, OracleRepository>();
and you've switched database implementations by adding 1 new DB class, and then changing 1 line in startup, and this leaves your caching behavior alone.并且您已经通过添加 1 个新 DB class 来切换数据库实现,然后在启动时更改 1 行,这样就可以单独处理缓存行为。
Above discusses switching your base database, but you could of course change the level up as well, so CachedRepository
can be switched out for anything that implements the IRepository
interface, so you get the flexibility built into that layer as well.上面讨论了切换您的基础数据库,但您当然也可以更改级别,因此可以将
CachedRepository
切换为实现IRepository
接口的任何内容,因此您也可以在该层中获得内置的灵活性。
From what I read of your question I think I understand and this appears to solve your problem.从我读到的你的问题,我想我明白了,这似乎可以解决你的问题。 No more circular dependency.
不再有循环依赖。
Please comment if not, hope this helps!如果没有请评论,希望这有帮助!
I finaly find a way to acheive that.我终于找到了实现这一目标的方法。
//Startup.cs
public void ConfigureServices(IServiceCollection services)
{
//...
// Register a concrete implementation so DI know how to construct it with dependency
services.AddTransient<Oracle11Repository>();
// Register a call back to tell specific IRepository to inject
services.AddTransient<IRepository>(sp =>
new CachedRepository(
sp.GetService<Oracle11Repository>(),
sp.GetService<ILogger<CachedRepository>>(),
sp.GetService<IMemoryCache>(),
sp.GetService<IConfiguration>())
);
Now it would be nice to not have to specify all others constructors arguments since they are all registred.现在不必指定所有其他构造函数 arguments 会很好,因为它们都已注册。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.