37
loading...
This website collects cookies to deliver better user experience
In computing, aspect-oriented programming (AOP) is a programming paradigm that aims to increase modularity by allowing the separation of cross-cutting concerns. It does so by adding additional behavior to existing code (an advice) without modifying the code itself, instead separately specifying which code is modified via a "pointcut" specification, such as "log all function calls when the function's name begins with 'set'". This allows behaviors that are not central to the business logic (such as logging) to be added to a program without cluttering the code core to the functionality.
– Aspect-oriented programming, Wikipedia (accessed 17 July 2021)
LoggingInterceptor
. This LoggingInterceptor
then takes every call to a service method and logs it along with whether it was successful or if exceptions were thrown.Autofac
The base library for using Autofac. At the time of writing this, I am installing version 6.2.0
Autofac.Extras.DynamicProxy
The library that allows us to use interceptors with Autofac. This package will also install Castle.Core which contains the dynamic proxy functionality required for the interception. At the time of writing this, I am installing version 6.0.0
namespace AutofacInterceptorDemonstration.Services
{
public interface ILabelMaker
{
/// <summary>
/// Prints the specified string contents on a label.
/// </summary>
/// <param name="contents">The contents to be printed.</param>
void Print(string contents);
/// <summary>
/// Calculates the estimated label dimensions.
/// </summary>
/// <param name="contents">The contents that will be printed on the label.</param>
/// <returns>The estimated dimensions of the label.</returns>
(int width, int height) CalculateLabelSize(string contents);
}
}
ILabelMaker
interface that we created previously.using System;
using System.Linq;
using System.Text;
namespace AutofacInterceptorDemonstration.Services
{
public class ConsoleLabelMaker : ILabelMaker
{
/// <summary>
/// Prints the specified text onto a label on the Console.
/// </summary>
/// <param name="contents">The contents to be printed.</param>
public void Print(string contents)
{
var dimensions = CalculateLabelSize(contents);
var contentLines = contents.Split(Environment.NewLine);
var sb = new StringBuilder();
sb.AppendLine($"+{new string('-', dimensions.width - 2)}+");
foreach (var line in contentLines)
sb.AppendLine($"| {line.PadRight(dimensions.width - 4)} |");
sb.AppendLine($"+{new string('-', dimensions.width - 2)}+");
Console.WriteLine(sb.ToString());
}
/// <summary>
/// Calculates the estimated width and height of the label in the console.
/// </summary>
/// <param name="contents">The contents that would be printed.</param>
/// <returns>The width and height estimation for the label.</returns>
public (int width, int height) CalculateLabelSize(string contents)
{
var contentLines = contents.Split(Environment.NewLine);
var width = contentLines.Max(x => x.Length) + 4;
var height = contentLines.Length + 2;
return (width, height);
}
}
}
ILabelMaker
interface and ConsoleLabelMaker
implementation. Then, once our container is running and everything works, we will then create an interceptor and configure it with our Autofac container.using System;
using Autofac;
using AutofacInterceptorDemonstration.Services;
namespace AutofacInterceptorDemonstration
{
class Program
{
static void Main(string[] args)
{
var container = BuildContainer();
// Create a lifetime scope which we can use to resolve services
// https://autofac.readthedocs.io/en/latest/resolve/index.html#resolving-services
using var scope = container.BeginLifetimeScope();
var labelMaker = scope.Resolve<ILabelMaker>();
// Prompt the user for label contents
Console.Write("Please enter label contents: ");
var contents = Console.ReadLine();
// Output dimensions
var dimensions = labelMaker.CalculateLabelSize(contents);
Console.WriteLine($"Label size will be: {dimensions.width} x {dimensions.height}");
// Print the label
labelMaker.Print(contents);
}
/// <summary>
/// Builds an Autofac container with the necessary services.
/// </summary>
/// <returns>
/// Autofac container that can be used to resolve services.
/// </returns>
private static IContainer BuildContainer()
{
var builder = new ContainerBuilder();
// Register our label maker service as a singleton
// (so we only create a single instance)
builder.RegisterType<ConsoleLabelMaker>()
.As<ILabelMaker>()
.SingleInstance();
return builder.Build();
}
}
}
Please enter label contents: Introduction to Autofac Interceptors
Label size will be: 40 x 3
+--------------------------------------+
| Introduction to Autofac Interceptors |
+--------------------------------------+
IInterceptor
interface. We then implement our custom logic that we want to run before and after invoking a method.using System;
using System.Linq;
using Castle.DynamicProxy;
namespace AutofacInterceptorDemonstration.Interceptors
{
public class LoggingInterceptor : IInterceptor
{
/// <inheritdoc />
public void Intercept(IInvocation invocation)
{
Console.WriteLine($"Executing {invocation.Method.Name} with parameters: " +
string.Join(", ", invocation.Arguments.Select(a => a?.ToString()).ToArray()));
// Invoke the method
invocation.Proceed();
Console.WriteLine($"Finished executing {invocation.Method}");
}
}
}
LoggingInterceptor
, we simply need to wire it up to our Autofac container while we are building it.using System;
using Autofac;
using Autofac.Extras.DynamicProxy;
using AutofacInterceptorDemonstration.Interceptors;
using AutofacInterceptorDemonstration.Services;
namespace AutofacInterceptorDemonstration
{
class Program
{
// ...
private static IContainer BuildContainer()
{
var builder = new ContainerBuilder();
// First register our interceptors
builder.RegisterType<LoggingInterceptor>();
// Register our label maker service as a singleton
// (so we only create a single instance)
builder.RegisterType<ConsoleLabelMaker>()
.As<ILabelMaker>()
.SingleInstance()
.EnableInterfaceInterceptors()
.InterceptedBy(typeof(LoggingInterceptor));
return builder.Build();
}
}
}
Please enter label contents: Introduction to Autofac Interceptors
Executing AutofacInterceptorDemonstration.Services.ConsoleLabelMaker.CalculateLabelSize with parameters: Introduction to Autofac Interceptors
Finised executing AutofacInterceptorDemonstration.Services.ConsoleLabelMaker.CalculateLabelSize
Label size will be: 40 x 3
Executing AutofacInterceptorDemonstration.Services.ConsoleLabelMaker.Print with parameters: Introduction to Autofac Interceptors
+--------------------------------------+
| Introduction to Autofac Interceptors |
+--------------------------------------+
Finised executing AutofacInterceptorDemonstration.Services.ConsoleLabelMaker.Print
virtual
so that it can intercept the calls.ILabelMaker
interface and registered the ConsoleLabelMaker
service directly (and marked all of its methods as virtual
), then we can register the interceptor as follows:using System;
using Autofac;
using Autofac.Extras.DynamicProxy;
using AutofacInterceptorDemonstration.Interceptors;
using AutofacInterceptorDemonstration.Services;
namespace AutofacInterceptorDemonstration
{
class Program
{
// ...
private static IContainer BuildContainer()
{
var builder = new ContainerBuilder();
// First register our interceptors
builder.RegisterType<LoggingInterceptor>();
// Register our label maker service as a singleton
// (so we only create a single instance)
builder.RegisterType<ConsoleLabelMaker>()
.SingleInstance()
.EnableClassInterceptors()
.InterceptedBy(typeof(LoggingInterceptor));
return builder.Build();
}
}
}