AutoFac拦截,如何区分内在和外在

时间:2016-06-22 17:37:49

标签: c# autofac interception method-interception

所以我正在尝试实现拦截的概念,同时使用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”。

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

谢谢! 迈克尔

3 个答案:

答案 0 :(得分:1)

您可以使用named dependencies执行此操作 而不是说IProductService的实现是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,您可以在向容器注册依赖项时告知它使用哪个命名依赖项。

但这应该有用。

答案 1 :(得分:1)

您要做的似乎不是拦截概念,而是装饰者概念。

  

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

Decorator pattern

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

有关详细信息,请参阅adapters and decorators from Autofac documentation

答案 2 :(得分:0)

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

string cmdText = @"INSERT INTO [Sales] 
                ([Printers], [Ink], [Paper])
       VALUES (@Printers,@Ink,@Paper);
       SELECT @EmpID FROM (Emplopyee)";