简体   繁体   中英

How to configure nested dependency in ASP.NET 5 DI?

I would like to wrap a repository inside another repository that will handle caching while internally using the passed in repository. This way my caching logic can be completely separate from the repository implementation. This pattern would also allow me to easily change from memory cache to distributed cache, that is I could have different caching repositories that use different cache types so I can plug them in depending on the environment. On Azure I could use distributed cache but on a single server I could use memory cache for example.

public sealed class CachingFooRepository : IFooRepository
{
    private IFooRepository repo;
    private IMemoryCache  cache;

    public CachingFooRepository(IFooRepository implementation, IMemoryCache  cache, IFooCachingRules)
    {
        if ((implementation == null)||(implementation is CachingFooRepository))
        {
            throw new ArgumentException("you must pass in an implementation of IFooRpository");
        }
        repo = implementation;
        this.cache = cache;
    }

    public async Task<bool> Save(IFooItem foo)
    {
        // TODO throw if foo is null
        bool result = await repo.Save(user);
        string key = "Key-" + foo.Id.ToString();
        cache.Remove(key);
        return result;
    }

    public async Task<IFooItem> Fetch(int fooId)
    {
        string key = "Key-" + fooId.ToString();
        object result = cache.Get(key);
        if(result != null) { return (foo)result; }
        foo = await repo.Fetch(fooId);
        if(foo != null)
        cache.Set(key, foo);

        return foo;
    }

}

Obviously while CachingFooRepository implements IFooRepository, I must make sure that a different implementation of IFooRepository is passed into the constructor for CachingFooRepository since it isn't a real implementation of IFooRepository but depends on a real implementation.

This example is simplified, pseudo-ish code, whether to cache and how long to cache can be passed in as IFooCachingRules or IOptions or something like that.

So my question is how can I reigster the services in such a way that things that depend on IFooRepository will get an instance of CachingFooRepository, but CachingFooRepository will get some other implementation of IFooRepository such as SqlFooRepository? I would like to keep other classes depending only on IFooRepository, I don't want to make anything depend specifically on CachingFooRepository.

Is this possible, feasible, a good idea, a bad idea?

I would like to wrap a repository inside another repository that will handle caching while internally using the passed in repository.

There's a name for the pattern you are using. It's called: The Decorator pattern .

a good idea, a bad idea?

Using the decorator pattern is an excellent idea, because it allows you to make add functionality, without having to make changes to any existing part of the system. In other words, you are able to adhere to the Open/closed principle .

Is this possible

No, there's no easy way do this with ASP.NET Core's built-in DI container. You should use one of the mature existing DI libraries for .NET to do this. The three libraries with the best support for applying the decorator pattern are Autofac , StructureMap and Simple Injector .

Contrary to popular belief, the decorator pattern is fairly easy to implement using the built-in container.

By using the extension methods in the linked answer, registering decorators becomes as simple as this:

public void ConfigureServices(IServiceCollection services)
{
    // First add the regular implementation
    services.AddSingleton<IDependency, OriginalImplementation>();

    // Wouldn't it be nice if we could do this...
    services.AddDecorator<IDependency>(
        (serviceProvider, decorated) => new DecoratorImplementation(decorated));
            
    // ...or even this?
    services.AddDecorator<IDependency, DecoratorImplementation>();
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM