Эта полезная статья от David Haydn (EDIT: мошенническая ссылка удалена, возможно, это была эта статья ) показывает, как вы можете использовать класс InjectionConstructor
, чтобы помочь вам создать цепочку с использованием шаблона декораторас единством.Однако, если элементы в вашей цепочке декораторов имеют другие параметры в своем конструкторе, 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);
}
}
Вот (проходящий) модульный тест.См. Комментарии к битам излишней конфигурации, которые я хочу устранить:
[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>());
}