This is my method, I am trying to validate componentToSave
(or access method parameter values) and throw an exception before method body even runs.
public Component SaveComponent(Component componentToSave) {
...
}
I tried using PostSharp but it is not free and also there were other libraries that rely on AutoFac as IoC but in my current setup I am using dotnet core's built-in dependency injection.
I tried NConcern
and it relies on CNeptune
and CNeptune
itself relies on a .exe
file for post-compile binding and I currently use Linux for both development and production so I cannot use it, even I tried testing with it on windows but could not get it to work with dotnet core.
I tried this approach (i.e. ActionFilter
and ServiceFilter
) but I only got it working if [ServiceFilter(typeof(LoggingActionFilter))]
is over controller not any other method (i.e. SaveComponent
method).
I tried using RealProxy
but apparently, it is not supported in dotnet core.
I am just lost, maybe I am over complicating the problem but there should be a way. Any help would be greatly appreciated.
Look into CQS, decorators and SimpleInjector. If you promote methods to classes, you can have the class dedicated to one thing (SOLID). Then you can add cross cutting concerns on decorators that will have the same interface as the implementation, but they essential chain the calls. if validation decorator fails, then your main logic won't ever be called. You can even add all exception handling here and any logging or retry logic or caching.
Edit
Sorry, was on mobile before! :)
For an example, I'll use your method here. Normally with CQS, you'd have a generic interface for all your queries (read-only) and commands (change state). That way, all your logic ends up going through a IQueryHandler or ICommandHandler so you can add cross cutting concerns to ALL your logic all at once. However, I'll make an example specific to your question.
public interface ISaveComponent
{
Component SaveComponent(Component componentToSave);
}
public class SaveComponent : ISaveComponent
{
public Component SaveComponent(Component componentToSave)
{
// Do your work here
}
}
public class SaveComponentValidation : ISaveComponent
{
private readonly ISaveComponent _saveComponent;
public SaveComponentValidation(ISaveComponent saveCompnent)
{
_saveComponent = saveCompnent;
}
public Component SaveComponent(Component componentToSave)
{
// Do Validation here
return _saveComponent.SaveComponent(componentToSave);
}
}
If you let SimpleInjector (IoC/DI) handle the decorations for you, then you just have to register them in one line of code like this:
container.RegisterDecorator(typeof(ISaveComponent), typeof(SaveComponentValidation));
Otherwise, you would have to manually create them like this:
public class Program
{
public static void Main()
{
ISaveComponent handler = new SaveComponentValidation(new SaveComponent());
handler.SaveComponent(new Component());
}
}
Validating method arguments before the method body even runs can be achieved by creating a dynamic proxy, which intercepts method calls and executes your validation logic.
One dynamic proxy implementation that is supported in .NET Core is provided by Castle.Core
package.
However, in my personal experience, implementing these dynamic proxies takes some getting used to and often consists of quite some boiler plate code.
To make the process of dynamically decorating your methods simpler, I created a wrapper package Decor.NET and released it under MIT licence. Following are the instructions on how to achieve the behaviour you are asking.
- Install packages.
Install-Package Decor
Install-Package Decor.Extensions.Microsoft.DependencyInjection
- Create a decorator, which contains validation logic.
public class ComponentValidator : IDecorator
{
public async Task OnInvoke(Call call)
{
var componentToSave = (Component)call.Arguments[0];
if (/* Your validation logic */)
throw new Exception("Something's not right.");
await call.Next();
}
}
- Add
[Decorate(typeof(ComponentValidator))]
attribute to the method(s) you want to validate.
[Decorate(typeof(ComponentValidator))]
public virtual Component SaveComponent(Component componentToSave) {
...
}
- Register Decor.NET, validation decorator and decorated class using the extension methods provided by
Decor.Extensions.Microsoft.DependencyInjection
.
services.AddDecor()
.AddTransient<ComponentValidator>()
.AddScoped<SomeService>().Decorated();
Notice that the decorated method has to be overridable (marked as virtual
or implemented from interface). This is needed for dynamic proxy to override method's implementation.