1

Can someone please provide a simple example of how to implement the decorator pattern with TinyIoC?

A previous question shows how to do this in Ninject with the following:

Bind<IRepository>().To<MoreAdvancedRespository>
               .WhenInjectedInto<TrickyRepository>();
Bind<IRepository>().To<SomeSimpleRepository>
               .WhenInjectedInto<MoreAdvancedRespository>();
Community
  • 1
  • 1
Ken Burkhardt
  • 3,528
  • 6
  • 33
  • 45
  • 1
    I think you just outgrown TinyIoC. Time to move on to on of the "big boys". – Steven Jul 03 '13 at 18:40
  • It's not always that easy - TinyIoC has the distinctive quality of being a single moderately sized code file that you just drop into a project instead of one or more additional assemblies and that compiles to a bit over 40k of IL code (Release configuration on .NET 4.0), which can be important in many projects; I currently use it exclusively for that very reason. That said, the number of serious shortcomings I'm discovering is growing, but that can't always be solved by simply moving to a different tool. If the OP can, though, he certainly should in this case, yes. – TeaDrivenDev Jul 12 '13 at 22:47

2 Answers2

2

After trying to set up this example for a while, the short answer is: TinyIoC can't properly do that, at least not if we're talking about "repositories" in a traditional sense and want them to be treated as singletons by the container.

That being said, this kind of works:

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

Now one might be tempted to try and trick the container (I was) by doing this:

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

which could then be wrapped in an extension method to have a single method call again on the container. Unfortunately, this doesn't work - while trying to resolve the IRepository registration named "advanced", the container apparently doesn't find the previous explicit registration for MoreAdvancedRepository anymore and throws a TinyIoCResolutionException.

So, the above will work if
- each of the different repository types is only resolved once or
- it is no problem that a new instance is created each time a specific repository type is resolved.

Otherwise you'd probably need to fall back to poor man's DI for those classes or use a different IoC container.

TeaDrivenDev
  • 6,591
  • 33
  • 50
1

You can't use auto-wiring, so you will have to register a lambda for each decorator:

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

You'll have to repeat this for each repository in the system.

Steven
  • 166,672
  • 24
  • 332
  • 435
  • 1
    Well, using named registrations and/or `NamedParameterOverloads`, it gets a bit easier in practice, but that's generally the effort it takes, yes. – TeaDrivenDev Jul 07 '13 at 22:04
  • 1
    @TeaDrivenDev: please add an answer that shows this. I think that would help the OP. – Steven Jul 08 '13 at 04:14