TinyIoC и шаблон декоратора
Может кто-нибудь привести простой пример реализации шаблона декоратора с TinyIoC?
Предыдущий вопрос показывает, как это сделать в Ninject, с помощью следующего:
Bind<IRepository>().To<MoreAdvancedRespository>
.WhenInjectedInto<TrickyRepository>();
Bind<IRepository>().To<SomeSimpleRepository>
.WhenInjectedInto<MoreAdvancedRespository>();
2 ответа
После попытки настроить этот пример на некоторое время, короткий ответ: TinyIoC не может сделать это должным образом, по крайней мере, если мы говорим о "хранилищах" в традиционном смысле и хотим, чтобы они рассматривались как синглтоны контейнер.
Это, как говорится, этот вид работ:
public interface IRepository { }
public class MoreAdvancedRepository : IRepository
{
public MoreAdvancedRepository(IRepository innerRepository, ISomeOtherDependency otherDependency) { }
}
public class TrickyRepository : IRepository
{
public TrickyRepository(IRepository innerRepository, ISomeOtherDependency otherDependency) { }
}
public class SimpleRepository : IRepository { }
public interface ISomeOtherDependency { }
public class SomeOtherDependencyWeasel : ISomeOtherDependency { }
// Register the other dependency
container.Register<ISomeOtherDependency, SomeOtherDependencyWeasel>();
// Register the "innermost" repository with a name
container.Register<IRepository, SimpleRepository>("simple");
// Register the inner decorator implementation, also with a name, and telling the container what to do for the dependency called "innerRepository"
container.Register<IRepository>((c, p) => c.Resolve<MoreAdvancedRepository>(new NamedParameterOverloads() { { "innerRepository", c.Resolve<IRepository>("simple") } }), "advanced");
// Register the outer decorator the same way, only without a name for the registration, so this will be what's resolved whenever IRepository is requested without specifying a name
container.Register<IRepository>((c, p) => c.Resolve<TrickyRepository>(new NamedParameterOverloads() { { "innerRepository", c.Resolve<IRepository>("advanced") } }));
// Resolve stuff to check how the registration worked out
var simple1 = container.Resolve<IRepository>("simple");
var simple2 = container.Resolve<IRepository>("simple");
var advanced1 = container.Resolve<IRepository>("advanced");
var advanced2 = container.Resolve<IRepository>("advanced");
var tricky1 = container.Resolve<IRepository>();
var tricky2 = container.Resolve<IRepository>();
Assert.IsType<SimpleRepository>(simple1); // this passes, unsurprisingly
Assert.Same(simple1, simple2); // this passes, too, as simple Register<TResolve, TImpl>() calls are implicitly .AsSingleton()
Assert.IsType<MoreAdvancedRepository>(advanced1); // passes
Assert.IsType<TrickyRepository>(tricky1); // passes
Assert.Same(advanced1, advanced2); // this fails, as Register<TResolve>(Func<TResolve, TinyIoCContainer, NamedParameterOverloads>) calls are implicitly .AsMultiInstance() and can not be changed to .AsSingleton()
Assert.Same(tricky1, tricky2); // fails for the same reason
Теперь можно попытаться обмануть контейнер (я был), выполнив это:
container.Register<MoreAdvancedRepository>((c, p) => c.Resolve<MoreAdvancedRepository>(new NamedParameterOverloads() { { "innerRepository", c.Resolve<IRepository>("simple") } })); // always .AsMultiInstance()
container.Register<IRepository, MoreAdvancedRepository>("advanced"); // implicitly .AsSingleton(), so only one instance should be created and then returned for subsequent calls
который затем может быть обернут в метод расширения для повторного вызова одного метода в контейнере. К сожалению, это не работает - при попытке решить IRepository
регистрация с именем "продвинутый", контейнер явно не находит предыдущую явную регистрацию для MoreAdvancedRepository
больше и бросает TinyIoCResolutionException
,
Итак, вышесказанное будет работать, если
- каждый из различных типов репозитория разрешается только один раз или
- не проблема, что новый экземпляр создается каждый раз, когда решается определенный тип репозитория.
В противном случае вам, вероятно, придется обратиться к DI бедного человека для этих классов или использовать другой контейнер IoC.
Вы не можете использовать автоматическую разводку, поэтому вам придется зарегистрировать лямбду для каждого декоратора:
// Register the repository
container.Register<SomeSimpleRepository>();
// Register the inner decorator
container.Register<MoreAdvancedRespository>(() => new MoreAdvancedRespository(
container.Resolve<SomeSimpleRepository>(),
container.Resolve<ISomeOtherDependency>()));
// Register the outer decorator
container.Register<IRepository>(() => new TrickyRepository(
container.Resolve<MoreAdvancedRespository>(),
container.Resolve<ISomeOtherDependency>()));
Вам придется повторить это для каждого хранилища в системе.