Interceptor with Microsoft.Extensions.DependencyInjection and asp.net web api 2 for cross cutting concerns like logging
Asked Answered
G

3

16

We are using Microsoft.Extensions.DependencyInjection in our asp.net web api2 for dependency injection.

For cross cutting concerns like logging, we are of thought that aspect oriented programming should be considered and unable to find any support in the above di.

Other DI like castle, Unity and ninject are opted out post looking at their benchmarks. Is there any way to use injectors of other di frameworks like castle dynamic proxy and use it with Microsoft.Extensions.DependencyInjection?

Any suggestions related to IL-weaving frameworks are also welcomed. Consideration of PostSharp is ruled out as it isn't free.

Gramnegative answered 7/7, 2018 at 18:58 Comment(1)
MS.DI is very limited. Although it's not completely impossible to apply interceptors using dynamic interception libraries, such as Castle Dynamic Proxy, you should consider using a more mature and feature rich DI Container instead.Trinidadtrinitarian
C
6

Decor.NET is a wrapper around Castle.Core dynamic proxy, which aims to simplify method decoration.

It is DI container agnostic, but has an integration with Microsoft's DI, which is available in Decor.Extensions.Microsoft.DependencyInjection NuGet package.

  1. You create a decorator:

    public class YourDecorator : IDecorator
    {    
        public SomeDependency SomeDependency { get; }    
    
        public YourDecorator(SomeDependency someDependency) // Supports DI.
        {
            SomeDependency = someDependency;
        }
    
        public async Task OnInvoke(Call call)
        {
            ...
            await call.Next();
            ...
        }
    }
    
  2. Decorate your methods:

    [Decorate(typeof(YourDecorator))]
    virtual void SomeMethod()  // << has to be overridable (can come from an interface)
    {
        ...
    }
    
  3. Register everything in Microsofts' DI:

    services.AddDecor()
        .AddTransient<YourDecorator>()
        .AddScoped<YourService>().Decorated();
    

You can play around in this .Net Fiddle.


Disclosure: I'm the author of this package.

Cageling answered 4/3, 2020 at 21:38 Comment(1)
Isn't the point to not have to add attributes to the existing classes or remember add to all existing or new classes? In addition, if the library is used in other projects (i.e. web vs service) I don't want to modify every class. I have many large project libraries that are used in different contexts. I would like to wrap all the calls in a timer and log calls over a certain threshold. I don't want to go and modify all the classes in every library. I want to use the DI framework to instrument every class with my "aspect" - the timer and log for the runtime of each method. Thanks.Immunoreaction
T
3

I'd argue you don't need any AOP frameworks, .NET provides just enough tooling to be able to do the majority of the aspect oriented stuff yourself.

The short version is this:

  1. Inherit from MarshalByRefObject on your business class.

  2. Create an "Aspect" for your desired functionality. As in, create a class that inherits from RealProxy. e.g. public class LoggingAspect<T> : RealProxy.

  3. override the Invoke method, and implement your aspect code. Make sure to pass-though the method call to the original instance.

  4. Create a builder/factory class that will create instances of the desired classes, e.g. Entity e = EntityBuilder.WithLogging();

  5. Replace all instance of new Entity(...) with EntityBuilder.Get(...).

The long version is much better explained by Microsoft employees themselves:

Aspect-Oriented Programming : Aspect-Oriented Programming with the RealProxy Class

Tessy answered 20/7, 2018 at 14:57 Comment(0)
B
0

I am not convinced you need a tool like PostSharp or any other AOP tool for this when developing web api's. There are numerous extension points where you can plug in your logging/auditing/whatever actions. A good overview would be this poster

You could create an Action Filter. You can register this globally across all controllers (if needed) or a MessageHandler for example.

Now, adressing your comment:

[..] What about logging in classes of business layer/data access layer like any exceptions/ custom messages without making direct calls to logger. [..]

I am not sure what you want to achieve by this. If you have proper exception handling you have to deal with the exception or let it bubble up. When writing the exception handling, why not write one extra line that logs whatever you need? It gives you the opportunity to add meaningful context as well to your log message, something that is hard to do using an automated AOP tool!

Lastly, let me address this comment:

Consideration of PostSharp is ruled out as it isn't free.

Tools like PostSharp are very, very much worth their money if it can address the issue at hand. Sure you can spend day researching free alternatives or write your own implementation but it will probably much more limited, needs refactoring of your existing codebase, difficult to maintain for the team as a whole and it will swallow lots of time. The alternative might be more expensive than the tool.

Bandage answered 8/7, 2018 at 7:51 Comment(5)
I agree with you.But, the logging is then limited to the controller. What about logging in classes of business layer/data access layer like any exceptions/ custom messages without making direct calls to logger. I couldn&amp;#39;t find anything for such logging/ cross cutting concerns. I thought Aop can help me out by injecting the logging code.Gramnegative
@azharuddinirfani, I know it's been a long time since you posted this, this can be accomplished using a DI container like Lightinject, see the documentation on the "Interception" topic. You had a valid point that this reply sort of dismissed, and the fact the author never followed up on your comment is disappointing.Macedonia
@Macedonia it has been a while since I posted the answer, no idea of why I did not react but I've updated my answer. Feel free to add your opinion :-)Bandage
@azharuddinirfani kind of curious, how did it end for you? Did you use an alternative?Bandage
@PeterBons, cheers for the update, and my comment is a little uncharitable in hindsight...so apologies for that. I'll add my own 2p to your update: it's handy to have a middleware/service log any errors it encounters (or at least provide that option) and still bubble them up, where the consumer can do whatever it sees fit, add context, make the error more user-friendly, etc. But it's not safe to assume that the end user of a service will log everything you need to track down a problem in production (in my experience at least)Macedonia

© 2022 - 2024 — McMap. All rights reserved.