【问题标题】:How to configure nested dependency in ASP.NET 5 DI?如何在 ASP.NET 5 DI 中配置嵌套依赖项?
【发布时间】:2015-10-08 04:29:45
【问题描述】:

我想将一个存储库包装在另一个存储库中,该存储库将在内部使用传入的存储库时处理缓存。 这样我的缓存逻辑可以与存储库实现完全分离。这种模式还允许我轻松地从内存缓存更改为分布式缓存,也就是说,我可以拥有使用不同缓存类型的不同缓存存储库,因此我可以根据环境插入它们。在 Azure 上我可以使用分布式缓存,但在单个服务器上我可以使用内存缓存。

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;
    }

}

显然,虽然 CachingFooRepository 实现了 IFooRepository,但我必须确保将 IFooRepository 的不同实现传递到 CachingFooRepository 的构造函数中,因为它不是 IFooRepository 的真正实现,而是依赖于实际实现。

这个例子是简化的伪代码,是否缓存以及缓存多长时间可以作为 IFooCachingRules 或 IOptions 或类似的东西传入。

所以我的问题是,我怎样才能重新注册服务,使依赖 IFooRepository 的事物将获得 CachingFooRepository 的实例,但 CachingFooRepository 将获得 IFooRepository 的其他一些实现,例如 SqlFooRepository? 我想保留仅依赖于 IFooRepository 的其他类,我不想让任何东西专门依赖于 CachingFooRepository。

这是可能的、可行的、好主意还是坏主意?

【问题讨论】:

    标签: c# caching dependency-injection asp.net-core


    【解决方案1】:

    我想将一个存储库包装在另一个存储库中 在内部使用传入的存储库时处理缓存。

    您正在使用的模式有一个名称。它被称为:Decorator pattern

    一个好主意,一个坏主意?

    使用装饰器模式是一个绝妙的主意,因为它允许您添加功能,而无需对系统的任何现有部分进行更改。换句话说,您可以遵守Open/closed principle

    这可能吗

    不,no easy way do this 带有 ASP.NET Core 的内置 DI 容器。您应该使用成熟的现有 .NET DI 库之一来执行此操作。最支持应用装饰器模式的三个库是AutofacStructureMapSimple Injector

    【讨论】:

    • 如果我只定义公共接口 IFooRepositoryWrapper 怎么办:IFooRepository,与 IFooRepository 相比没有其他方法或属性。然后我创建了一个 DefaultFooRepositoryWrapper,它接受 IFooRepository 并将所有方法委托给它。我可以让大多数类依赖于 IFooRepositoryWrapper 而不是 IFooRepository,然后我可以实现 CachingFooRepositoryWrapper。我认为这应该与默认 DI 一起使用,而不依赖于特定的实现。
    • @JoeAudette:当然可以。然而问题是您现在正在使用自定义抽象来污染您的应用程序,并且您需要在整个应用程序中进行彻底的更改以使事情正常运行。请不要那样做。请切换到一个像样的 DI 库;它们都是免费的,而且比内置库要好得多。
    • 我正在构建将成为开源的组件,因此我不想将任何特定的 DI 强加给我的组件的使用者。我希望人们能够使用默认的 DI 或任何他们选择的。我的应用程序处于早期阶段,我可以进行更改。使用 2 个相同的接口来嵌套包装器真的会造成污染吗?
    • @JoeAudette:如果您正在构建一个开源工具,我什至会更进一步,并防止对任何诸如 DI 容器之类的东西产生任何依赖。所以甚至没有来自 ASP.NET 的内置 DI 容器。而且你当然不应该依赖内置容器的注册API,因为这迟早会在你和你的项目上引起hell to break loose
    • 我的产品将处于人们可以消费的 nugets 中,并且不依赖于任何 DI。但它也将使用启动集成文件中的默认 DI 打包为工作应用程序。那些想要以不同方式连接它的人可以使用任何 DI 编写自己的集成文件,但我的工作示例将使用默认 DI
    【解决方案2】:

    与流行的看法相反,装饰器模式fairly easy,使用内置容器来实现。

    通过使用链接答案中的扩展方法,注册装饰器变得如此简单:

    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>();
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-01-27
      • 2016-04-09
      • 1970-01-01
      • 2020-11-26
      • 2022-01-24
      • 2018-07-18
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多