如何使用使用Unity修饰模式没有明确指定在InjectionConstructor每个参数 [英] How do I use the Decorator Pattern with Unity without explicitly specifying every parameter in the InjectionConstructor

查看:1172
本文介绍了如何使用使用Unity修饰模式没有明确指定在InjectionConstructor每个参数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

从大卫·海顿(EDIT这是很有帮助的文章:诈骗链接删除,它可能是的这篇文章)展示了如何使用 InjectionConstructor 类来帮助你使用设置了一个链装饰图案与统一。但是,如果在你的装饰链的项目在其构造有其他参数, InjectionConstructor 必须明确宣布他们每个人(或Unity将抱怨找不到正确的构造函数)。这意味着,你不能简单地添加新的构造函数的参数在装饰链项目,而不同时更新您的统一配置code。

下面是一些例子code来解释我的意思。在 ProductRepository 类首先由 CachingProductRepository 包裹,然后通过 LoggingProductRepostiory 。既CachingProductRepository和LoggingProductRepository,除了在其构造服用IProductRepository,还需要其他的接口从容器

 公共类产品
    {
        公众诠释标识;
        公共字符串名称;
    }    公共接口IDatabaseConnection类型{}    公共接口ICacheProvider
    {
        对象GetFromCache(字符串键);
        无效AddToCache(字符串键,对象的值);
    }    公共接口ILogger
    {
        日志无效(字符串消息,params对象[]参数);
    }
    公共接口IProductRepository
    {
        产物GetById(中间体ID);
    }    类ProductRepository:IProductRepository
    {
        公共ProductRepository(IDatabaseConnection类型DB)
        {
        }        公共产品GetById(INT ID)
        {
            返回新产品(){ID = ID,名字=富+ id.ToString()};
        }
    }    类CachingProductRepository:IProductRepository
    {
        IProductRepository库;
        ICacheProvider的cacheProvider;
        公共CachingProductRepository(IProductRepository库,ICacheProvider CP)
        {
            this.repository =库;
            this.cacheProvider = CP;
        }        公共产品GetById(INT ID)
        {
            字符串键=产品+ id.ToString();
            产品p =(产品)cacheProvider.GetFromCache(密钥);
            如果(P == NULL)
            {
                p值= repository.GetById(ID);
                cacheProvider.AddToCache(键,P);
            }
            回磷;
        }
    }    类LoggingProductRepository:IProductRepository
    {
        私人IProductRepository库;
        私人ILogger记录;        公共LoggingProductRepository(IProductRepository库,ILogger记录仪)
        {
            this.repository =库;
            this.logger =记录仪;
        }        公共产品GetById(INT ID)
        {
            logger.Log(请求产品{0},身份证);
            返回repository.GetById(ID);
        }
    }

下面是一个(合格)的单元测试。见盈余配置的位评论我要删除的需要:

  [测试]
    公共无效ResolveWithDecorators()
    {
        UnityContainer C =新UnityContainer();
        c.RegisterInstance&所述; IDatabaseConnection类型>(新莫克&所述; IDatabaseConnection类型方式>()对象);
        c.RegisterInstance&所述; ILogger>(新莫克&所述; ILogger方式>()对象);
        c.RegisterInstance&所述; ICacheProvider>(新莫克&所述; ICacheProvider方式>()对象);        c.RegisterType&所述; IProductRepository,ProductRepository&GT(ProductRepository);        //不想有每次CachingProductRepository构造得到另一个参数的时间来更新该行
        变种dependOnProductRepository =新InjectionConstructor(新ResolvedParameter&所述; IProductRepository&GT(ProductRepository),新ResolvedParameter&所述; ICacheProvider>());
        c.RegisterType&所述; IProductRepository,CachingProductRepository&GT(CachingProductRepository,dependOnProductRepository);        //不想每次都更新此行的LoggingProductRepository构造变化
        变种dependOnCachingProductRepository =新InjectionConstructor(新ResolvedParameter&所述; IProductRepository&GT(CachingProductRepository),新ResolvedParameter&所述; ILogger>());
        c.RegisterType< IProductRepository,LoggingProductRepository>(dependOnCachingProductRepository);
        Assert.IsInstanceOf&所述; LoggingProductRepository>(c.Resolve&所述; IProductRepository>());
    }


解决方案

另一种方法,这要归功于来自@ DarkSquirrel42的建议,是使用 InjectionFactory 。不足之处是,code仍然需要更新每一个新的构造函数参数添加到链中的一些时间。其优点是更容易理解code和只有一个注册到容器

  Func键< IUnityContainer,对象> createChain =容器=>
    新LoggingProductRepository(
        新CachingProductRepository(
            container.Resolve< ProductRepository>()
            container.Resolve&所述; ICacheProvider>()),
        container.Resolve&所述; ILogger>());c.RegisterType&所述; IProductRepository>(新InjectionFactory(createChain));
Assert.IsInstanceOf&所述; LoggingProductRepository>(c.Resolve&所述; IProductRepository>());

This helpful article from David Haydn (EDIT: scam link removed, it could have been this article) shows how you can use the InjectionConstructor class to help you set up a chain using the decorator pattern with Unity. However, if the items in your decorator chain have other parameters in their constructor, the InjectionConstructor must explicitly declare each one of them (or Unity will complain that it can't find the right constructor). This means that you can't simply add new constructor parameters to items in the decorator chain without also updating your Unity configuration code.

Here's some example code to explain what I mean. The ProductRepository class is wrapped first by CachingProductRepository and then by LoggingProductRepostiory. Both CachingProductRepository and LoggingProductRepository, in addition to taking a IProductRepository in their constructor, also need other interfaces from the container.

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

Here's a (passing) unit test. See the comments for the bits of surplus configuration I want to remove the need for:

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

解决方案

Another approach, thanks to a suggestion from @DarkSquirrel42, is to use an InjectionFactory. The downside is that the code still needs updating every time a new constructor parameter is added to something in the chain. The advantages are much easier to understand code, and only a single registration into the container.

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

这篇关于如何使用使用Unity修饰模式没有明确指定在InjectionConstructor每个参数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆