Эта полезная статья Дэвида Гайдна (EDIT: удаленная ссылка удалена, возможно, она была в этой статье) показывает, как вы можете использовать InjectionConstructor
, чтобы помочь вам создать цепочку, используя шаблон декоратора с Unity. Однако, если элементы в вашей цепочке декораторов имеют другие параметры в своем конструкторе, InjectionConstructor
должен явно объявить каждый из них (или Unity будет жаловаться, что он не может найти правильный конструктор). Это означает, что вы не можете просто добавлять новые параметры конструктора к элементам в цепочке декораторов, не обновляя также свой код конфигурации Unity.
Вот пример кода, чтобы объяснить, что я имею в виду. Класс ProductRepository
сначала завернут CachingProductRepository
, а затем LoggingProductRepostiory
. И CachingProductRepository, и LoggingProductRepository, в дополнение к использованию IProductRepository в своем конструкторе, также нуждаются в других интерфейсах из контейнера.
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);
}
}
Здесь (прохождение) unit test. См. Комментарии к битам избыточной конфигурации. Я хочу удалить необходимость:
[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>());
}