Structuremap3 DecorateAllWith
Asked Answered
M

1

2

I've been struggling getting DecorateAllWith working on generic interfaces. I've read some posts here where they solved it using interceptors but they seem to be using an older structure map version and it doesn't seem like a "clean" solution.

I would really need some help to get it working with structure map 3

I have a generic repository which i would like to decorate with both logging and caching

public interface IEntityRepository<T> where T : Entities.IEntity
{
}

I have about 20 interfaces that inherit IEntityRepository. Example mu UserRepository

public interface IUserEntityRepository : IEntityRepository<User>
{
}

And then I have the logging decorator concrete type which I would like all instances of IEntityRepository to be decorated with

public class LoggingEntityRepository<T> : IEntityRepository<T> where T : Entities.IEntity
{
    private readonly IEntityRepository<T> _repositoryDecorated;

    public LoggingEntityRepository(IEntityRepository<T> repositoryDecorated)
    {
        _repositoryDecorated = repositoryDecorated;
    }
}

Or are there other IoC containers better suited for what I am trying to accomplish?

Edit: Is there a way to decorate all interfaces that inherit from IEntityRepository

Matherne answered 5/8, 2014 at 8:16 Comment(0)
A
0

Here's a working example that answers your first question

[Fact]
public void DecorateAllWith_AppliedToGenericType_IsReturned()
{
    var container = new Container(registry =>
    {
        registry.Scan(x =>
        {
            x.TheCallingAssembly();
            x.ConnectImplementationsToTypesClosing(typeof(IEntityRepository<>));

        });

        registry.For(typeof(IEntityRepository<>))
            .DecorateAllWith(typeof(LoggingEntityRepository<>));
    });

    var result = container.GetInstance<IEntityRepository<Entity1>>();

    Assert.IsType<LoggingEntityRepository<Entity1>>(result);
}

To answer your second question, I personally use (and contribute to) Simple Injector - it is one of the fastest containers available, has comprehensive support for generics and offers some powerful diagnostic services.

The registrations in Simple Injector would look like this:

[Fact]
public void RegisterDecorator_AppliedToGenericType_IsReturned()
{
    var container = new SimpleInjector.Container();

    container.RegisterManyForOpenGeneric(
        typeof(IEntityRepository<>), 
        typeof(IEntityRepository<>).Assembly);

    container.RegisterDecorator(
        typeof(IEntityRepository<>), 
        typeof(LoggingEntityRepository<>));

    var result = container.GetInstance<IEntityRepository<Entity1>>();

    Assert.IsType<LoggingEntityRepository<Entity1>>(result);
}
Arthrospore answered 5/8, 2014 at 10:14 Comment(10)
Thanks for your answer. I managed to get that to work. However if I try to get an instance of IUserEntityRepository that wont work. Is there a way to accomplish that? Se my updated postMatherne
LoggingEntityRepository does not implement IUserEntityRepository so cannot decorate it. You can only get it to work as-is if you refer to the service as IEntityRepository<User> in your code.Arthrospore
Not even if IUserRepository inherits IEntityRepository?Matherne
Nope. IUserEntityRepository is a IEntityRepository<User> but IEntityRepository<User> is not a IUserEntityRepositoryArthrospore
OK, thanks. I was afraid that would be the case. Do you have any suggestions on another way to accomplish what I am trying to do?Matherne
That's hard without knowing the details of each of the entity specific interfaces. Personally I would do away with them all and revert to using the generic interface. If you have anything specific you want to expose from a particular interface, say something like a GetAllUsers() method then you can do so with extensions methods public static IEnumerable<User> GetAllUsers(this IEntityRepository<User> repository) etc.Arthrospore
I understand. The entity specific interfaces has about 5 extra methods in them and the generic interface only has CRUD methods. I probably wouldn't like to use extension methods mainly because of the problems testing them and I also use mock implementations of the interfaces during development before moving to real database implementation in the end.Matherne
Do you they all implement the same 5 methods? If so you could add them as abstract methods of an abstract base class. This sort of design is what makes the dynamic proxy interception seem attractive. My own preference is to use the Query pattern as described here. One interface for all repositories and one interface for all queries makes decorating your objects a lot simpler :-)Arthrospore
I've had a look at the Query pattern and it works great with my decorators. Now I have both logging, caching and validation/permission decorators for my repositories. One last thing - in the current design there is a concrete Service layer fronting the repositories. I am pondering if the Query pattern should be applied to the respositores making the fronting service resposible for running the query and thus making the Query pattern "invisible" for the consumer. Or keep my old repositories as is and implement the Query pattern on the Service layer forcing consumers to construct queries.Matherne
Again this is a presonal decision - I like to keep my repositories private to the data layer and only expose queries. You can make the code cleaner by having an extension method (normally with the same name Handle) to construct the query object and call the Handle method of the QueryHandlerArthrospore

© 2022 - 2024 — McMap. All rights reserved.