tjaskula (51) [Avatar] Offline
#1
Hello,

After reading the section 2.3.3 about interception and cross cutting concerns, I would like you to clarify some ideas.
I agree with you that injecting dependencies that are cross cutting concerns (logging, authorization, etc.) is not a very good idea. I try to avoid it as much as possible because it clutter the code and it violates SRP.

Let's say in ASP.NET MVC application I would like to enable logging in controllers, domain services, repositories it would be much easier to pass just an instance of a logger to the constructor (I know it's bad). If the decorator technique is easier to implement with repositories and services it's not obvious with MVC controllers. I would like for example to log not only errors but some informations about code execution (before some methods calls from services, after receiving a result from a service, etc.) How you would do it in order to have the same technique for dependency injection of cross cutting concerns in all layars ? (mvc, domain, dal) I think that it's important to have a concistent API that is understood by all developers in a team, so they should not invent the wheel when they would like to deal with CCC like logging for example.

Thanks for your response.
mark.seemann (383) [Avatar] Offline
#2
Re: Chapter 2 and Cross Cutting Concerns
I read your question as generally relating to instrumentation, and I wrote a blog post to answer some of the issues: http://blog.ploeh.dk/2010/09/20/InstrumentationWithDecoratorsAndInterceptors.aspx

As I see it, there are still a few questions left unanswered. First and foremost: since ASP.NET MVC controllers are concrete classes, they are difficult to decorate. However, they still implement IController that should give you information about the action requested and so forth, so you could choose to decorate that.

Still, if you don't want to go in that direction, you have a few other options. You can, as you suggest, inject the IRegistrar from the blog post above into the controller and use it as a dependency. However, from a design perspective, I still consider that a violation of the SRP.

In essence you could say that an ASP.NET MVC controller works by a sort of duck typing, so you can still define an instrumenting Decorator for a controller by mimicking the 'real' controller's API. However, I don't think that dynamic interception is going to work in that case, as there's no interface to implement.

However, this easily can be solved by defining an interface for each controller.
tjaskula (51) [Avatar] Offline
#3
Re: Chapter 2 and Cross Cutting Concerns
Mark,

Thanks for your answer and a great post.
I agree with you that Decorator is very helpfull when implementing CCC. The thing I don't like with it is that I need to repeat the same template for different implementations which are quite similar.
If I have 10 repositories and 3 different CCC to deal with I would have to implement 30 decorators. Than some others for domain services (for example to enable business logging) and so on.
Interception is great in some cases but you can just intercept the begining and the end of the method. If I would like to enable business loging inside the method the interception would not be of great use.
I know there is no the perfect solution, but injecting sometimes a CCC as a dependacy is worse that dealing with all decorators and Interception classes ?
mark.seemann (383) [Avatar] Offline
#4
Re: Chapter 2 and Cross Cutting Concerns
Consider very hard why you "would like to enable business loging inside the method." (my emphasis)

If you examine my blog post and carefully compare the log dump with the method bodies (from the previous posts) you will find that (close to) every single line of code is instrumented.

This is because at a high level, the entry method orchestrates a few method calls on dependencies. However, each of those methods are instrumented themselves, and some of them even repeat the pattern. At the leafs, each method is really quite small, and we shouldn't have a need to perform detailed instrumentation inside of them (other than begin/end).

Remember that with interception, you can intercept everything smilie

If you still find this unsatisfactory, I'd suggest that it might imply that the method bodies of the leaf methods are too big. Could it imply an SRP violation?
tjaskula (51) [Avatar] Offline
#5
Re: Chapter 2 and Cross Cutting Concerns
Business loging was just a customer requirement but I agree it has no sense.

Yes, there is certainly an SRP violation in the methods. I agree with you in that.
mark.seemann (383) [Avatar] Offline
#6
Re: Chapter 2 and Cross Cutting Concerns
Well, you can always inject a logger into a service if there's no other way around it... It's not how I would design a new system, but it may be necessary (perhaps as a temporary measure) if you need to add it to existing code.

However, it violates both the SRP and the OCP...
tjaskula (51) [Avatar] Offline
#7
Re: Chapter 2 and Cross Cutting Concerns
Hello,

I agree with you on that point. However I have another question. This time more technical. It's taken from the book sample.

How do you register AuditingProductRepository with Unity for example ? The point is to use auto-wiring in order to inject SqlProductRepository into AuditingProductRepository but to use AuditingProductRepository as default implementation for ProductRepository.

I didn't see it in the code sample.

I don't think it could work with RegisterType, but if I use RegisterInstance instead, will it be possible to define a life time manager to transcient ?

Thanks,

Message was edited by:
tjaskula
mark.seemann (383) [Avatar] Offline
#8
Re: Chapter 2 and Cross Cutting Concerns
Well, AuditingProductRepository is a Decorator, and the answer to that general question can be found in chapter 14. However, since ch. 14 isn't yet in the MEAP, I'll answer here. Something like this (I haven't tested it) ought to work:

container.RegisterType<SqlProductRepository>(new ContainerControlledLifetimeManager());
container.RegisterType<ProductRepository, AuditingProductRepository>(new InjectionConstructor(new ResolvedParameter<SqlProductRepository>(), new ResolvedParameter<IAuditor>()));

This also requires you to register IAuditor (not shown).

It registers SqlProductRepository as a Singleton. I'll leave as an exercise to you how to change AuditingProductRepository to a Singleton too smilie
tjaskula (51) [Avatar] Offline
#9
Re: Chapter 2 and Cross Cutting Concerns
Mark,

It's exactely what I was looking for. I don't know that part of the API very well. Thanks again.
tjaskula (51) [Avatar] Offline
#10
Re: Chapter 2 and Cross Cutting Concerns
Hello Mark,

Tried to use the registration sample above but I encounter some problems. It seems that Unity while resolving SqlProductRepository takes into account just the default parametrless constructor even if registred with InjectionConstructor member to use another. Any thoughts ?

Message was edited by:
tjaskula
tjaskula (51) [Avatar] Offline
#11
Re: Chapter 2 and Cross Cutting Concerns
Please, misregard my previous question. I sorted it out.