0

所以我试图在使用autofac的同时实现拦截的概念。我没有做任何花哨的事情,比如实现动态拦截,每个类我都有具体的代码。

我的代码(不是我的真实代码,我在网上找到了这个,但它说明了我的问题

public class DefaultProductService : IProductService
{       
    public Product GetProduct(int productId)
    {
        return new Product();
    }
}

public class CachedProductService : IProductService
{
    private readonly IProductService _innerProductService;
    private readonly ICacheStorage _cacheStorage;

    public CachedProductService(IProductService innerProductService, ICacheStorage cacheStorage)
    {
        if (innerProductService == null) throw new ArgumentNullException("ProductService");
        if (cacheStorage == null) throw new ArgumentNullException("CacheStorage");
        _cacheStorage = cacheStorage;
        _innerProductService = innerProductService;
    }

    public Product GetProduct(int productId)
    {
        string key = "Product|" + productId;
        Product p = _cacheStorage.Retrieve<Product>(key);
        if (p == null)
        {
            p = _innerProductService.GetProduct(productId);
            _cacheStorage.Store(key, p);
        }

        return p;
    }
}

public class ProductManager : IProductManager
{
    private readonly IProductService _productService;

    public ProductManager(IProductService productService)
    {
        _productService = productService;
    }
}

我的问题是,我希望我的 ProductManager 接收 IProductService 的“CachedProductService”,我希望我的 CachedProductService 接收 IProductService 的“DefaultProductService”。

我知道一些解决方案,但它们似乎都不完全正确。这样做的正确方法是什么?

谢谢!迈克尔

4

3 回答 3

1

您可以使用命名的依赖项来做到这一点。
而不是说IProductServiceis的实现DefaultProductService,您将有两个按名称区分的实现,如下所示:

builder.Register<DefaultProductService>().Named<IProductService>("DefaultProductService");
builder.Register<CachedProductService>().Named<IProductService>("CachedProductService");

文档中描述了几种不同的方法来指示您希望将哪个实现注入给定的类。这看起来像最简单和最明确的:

public class ProductManager : IProductManager
{
    private readonly IProductService _productService;

    public ProductManager([WithKey("CachedProductService")]IProductService productService)
    {
        _productService = productService;
    }
}

老实说,我不喜欢它,因为它会导致类对容器产生依赖。它不允许您完全独立于类本身进行配置。使用 Windsor,您可以在向容器注册依赖项时执行此操作,告诉它要使用哪个命名的依赖项。

但这应该有效。

于 2016-06-22T18:01:39.543 回答
1

您尝试做的似乎不是拦截概念,而是装饰器概念。

在面向对象的编程中,装饰器模式是一种设计模式,它允许将行为静态或动态地添加到单个对象,而不会影响同一类中其他对象的行为。
来自装饰者模式(维基百科)

装饰图案

Autofac内置了对使用命名注册的装饰器的支持。

首先,您必须将基本组件和装饰器声明为命名注册:

builder.RegisterType<DefaultProductService>().Named<IProductService>("base"); 
builder.RegisterType<CacheProductServiceAdapter>().Named<IProductService>("cache"); 

然后你可以注册你的装饰器

builder.RegisterDecorator((c, inner) => c.ResolveNamed<IProductService>("cache", TypedParameter.From(inner)), fromKey : "base")
       .As<IProductService>(); 

您还可以拥有多个适配器:

builder.RegisterType<DefaultProductService>().Named<IProductService>("base"); 
builder.RegisterType<CacheProductServiceAdapter>().Named<IProductService>("cache"); 
builder.RegisterType<LoggingProductServiceAdapter>().Named<IProductService>("logging"); 

builder.RegisterDecorator((c, inner) => c.ResolveNamed<IProductService>("cache", TypedParameter.From(inner)), fromKey : "base", toKey:"cached"); 
builder.RegisterDecorator((c, inner) => c.ResolveNamed<IProductService>("logging", TypedParameter.From(inner)), fromKey : "cached")
       .As<IProductService>(); 

有关更多信息,请参阅Autofac 文档中的适配器和装饰器。

于 2016-06-23T07:12:03.710 回答
0

根据 Scott 的建议,我想我找到了一种方法,而不必在我的 ProductManager 类中引入对容器的依赖(我真的不想这样做!)。我可以简单地实例化 CachedProductService 并告诉它使用哪个 ProductService

builder.RegisterType(typeof(ProductService)).Named<IProductService>("DefaultProductService");
builder.Register(c => new CachedProductService(c.ResolveKeyed<IProductService>("DefaultProductService"))).As<IProductService>();
于 2016-06-22T18:41:04.127 回答