c# - 如何在不显式指定 InjectionConstructor 中的每个参数的情况下将 Decorator Pattern 与 Unity 一起使用

标签 c# unity-container decorator

这篇来自 David Haydn 的有用文章(编辑:诈骗链接已删除,可能是 this article )展示了如何使用 InjectionConstructor 类来帮助您使用装饰器模式设置链与团结。但是,如果装饰器链中的项在其构造函数中有其他参数,则 InjectionConstructor 必须显式声明它们中的每一个(否则 Unity 会提示找不到正确的构造函数)。这意味着您不能在不更新 Unity 配置代码的情况下简单地向装饰器链中的项目添加新的构造函数参数。

这里有一些示例代码来解释我的意思。 ProductRepository 类首先由 CachingProductRepository 包装,然后由 LoggingProductRepostiory 包装。 CachingProductRepository 和 LoggingProductRepository 除了在其构造函数中采用 IProductRepository 之外,还需要来自容器的其他接口(interface)。

    public class Product 
    {
        public int Id;
        public string Name;
    }

    public interface IDatabaseConnection { }

    public interface ICacheProvider 
    { 
        object GetFromCache(string key);
        void AddToCache(string key, object value);
    }

    public interface ILogger
    {
        void Log(string message, params object[] args);
    }


    public interface IProductRepository
    {
        Product GetById(int id);    
    }

    class ProductRepository : IProductRepository
    {
        public ProductRepository(IDatabaseConnection db)
        {
        }

        public Product GetById(int id)
        {
            return new Product() { Id = id, Name = "Foo " + id.ToString() };
        }
    }

    class CachingProductRepository : IProductRepository
    {
        IProductRepository repository;
        ICacheProvider cacheProvider;
        public CachingProductRepository(IProductRepository repository, ICacheProvider cp)
        {
            this.repository = repository;
            this.cacheProvider = cp;
        }

        public Product GetById(int id)
        {       
            string key = "Product " + id.ToString();
            Product p = (Product)cacheProvider.GetFromCache(key);
            if (p == null)
            {
                p = repository.GetById(id);
                cacheProvider.AddToCache(key, p);
            }
            return p;
        }
    }

    class LoggingProductRepository : IProductRepository
    {
        private IProductRepository repository;
        private ILogger logger;

        public LoggingProductRepository(IProductRepository repository, ILogger logger)
        {
            this.repository = repository;
            this.logger = logger;
        }

        public Product GetById(int id)
        {
            logger.Log("Requesting product {0}", id);
            return repository.GetById(id);
        }
    }

这是一个(通过的)单元测试。请参阅评论,了解我想删除的一些多余配置:

    [Test]
    public void ResolveWithDecorators()
    {
        UnityContainer c = new UnityContainer();            
        c.RegisterInstance<IDatabaseConnection>(new Mock<IDatabaseConnection>().Object);
        c.RegisterInstance<ILogger>(new Mock<ILogger>().Object);
        c.RegisterInstance<ICacheProvider>(new Mock<ICacheProvider>().Object);

        c.RegisterType<IProductRepository, ProductRepository>("ProductRepository");

        // don't want to have to update this line every time the CachingProductRepository constructor gets another parameter
        var dependOnProductRepository = new InjectionConstructor(new ResolvedParameter<IProductRepository>("ProductRepository"), new ResolvedParameter<ICacheProvider>());
        c.RegisterType<IProductRepository, CachingProductRepository>("CachingProductRepository", dependOnProductRepository);

        // don't want to have to update this line every time the LoggingProductRepository constructor changes
        var dependOnCachingProductRepository = new InjectionConstructor(new ResolvedParameter<IProductRepository>("CachingProductRepository"), new ResolvedParameter<ILogger>());
        c.RegisterType<IProductRepository, LoggingProductRepository>(dependOnCachingProductRepository);
        Assert.IsInstanceOf<LoggingProductRepository>(c.Resolve<IProductRepository>());
    }

最佳答案

感谢@DarkSquirrel42 的建议,另一种方法是使用 InjectionFactory。缺点是每次将新的构造函数参数添加到链中的某些内容时,代码仍然需要更新。优点是代码更容易理解,并且只需一次注册到容器中。

Func<IUnityContainer,object> createChain = container =>
    new LoggingProductRepository(
        new CachingProductRepository(
            container.Resolve<ProductRepository>(), 
            container.Resolve<ICacheProvider>()), 
        container.Resolve<ILogger>());

c.RegisterType<IProductRepository>(new InjectionFactory(createChain));
Assert.IsInstanceOf<LoggingProductRepository>(c.Resolve<IProductRepository>());

关于c# - 如何在不显式指定 InjectionConstructor 中的每个参数的情况下将 Decorator Pattern 与 Unity 一起使用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6109646/

相关文章:

c# - 如何在 ASP.Net Core 6.0 中实现 HSTS header ?

c# - WebApi.UnityDependencyResolver 没有实现 Microsoft.Practices.ServiceLocation.IServiceLocator。参数 : commonServiceLocator

python - 子类化装饰类

javascript - 为什么我们不在 app.module.ts 的装饰器数组中添加 'ngModule'

c# - 生成 CPU 缓存未命中时的性能

C# 控制台 .exe 文件打开和关闭后立即

c# - claim 类型不正确

asp.net-mvc - 使用 MVC 的 Unity 2.0 Web.config 设置

c# - UnityContainer - 如何使用依赖链注册/解析

python - 如何在装饰器中捕获异常但允许调用者也捕获它?