ASP.NET Tutorial: Aspect-Oriented Paradigm Example Using DispatchProxy Class

We will learn how to use a dynamic proxy class to implement cross-cutting concerns (CCC) in this post. AOP implementations come in a variety of forms within the.NET environment. In our situation, we must concentrate on using the proxy object to dispatch any classes. AOP is a paradigm that offers improved modularity with SoC (separation of concerns), as you are aware. By introducing reusable code without changing already-existing code, it contributes to an increase in loose coupling.

The DispatchProxy class, which was developed in the System, will be used in our example.Reflection namespace as a library in the.NET Standard.

Cross-Cutting Concerns Example In .NET Application

DispatchProxy class works based on Interfaces. It provides dynamic wrapping of any type with additional code implementations.

That part is understandable as to what DispatchProxy is, but we need to be careful because one of the old alternatives is the RealProxy class, which provides the same thing but has more than it. RealProxy has cross-process remoting, which DispatchProxy is not.

Let’s start with an example.

Assume that we have two different application services. EmailMessageSender and ServerInfoRetriever. As you can see, the names represent what they are able to do.

public interface IEmailMessageSender
{
    public bool TrySendMessage(string to, string subject, string message);
}
public class EmailMessageSender : IEmailMessageSender
{
    // Simulation
    public bool TrySendMessage(string to, string subject, string message)
    {
        Console.WriteLine($"Message sent to the {to}");
        return true; // fake info
    }
}
public record ServerInfo(string State, DateTime ActivationDate, string Version);
 public interface IServerInfoRetriever
 {
     public ServerInfo GetInfo(IPAddress address);
 }
 public class ServerInfoRetriever : IServerInfoRetriever
 {
     // Simulation
     public ServerInfo GetInfo(IPAddress address)
     {
         Console.WriteLine($"{address.ToString()} Server Info Retrieved!");
         return new ServerInfo("In process", DateTime.Now, "1.0.0"); // fake info
     }
 }

To implement our DispatchProxy, we should start deriving from it and then implement/write our logic inside it.

public class LoggingDecoratorProxy<T> : DispatchProxy
{
    private readonly Logger _logger;
    public T Target { get; internal set; }
    public LoggingDecoratorProxy() : base()
    {
        _logger = new LoggerConfiguration()
            .WriteTo.Console().CreateLogger(); // used serilog
    }
    protected override object? Invoke(MethodInfo? targetMethod, object?[]? args)
    {
        try
        {
            _logger.Information("{TypeName}.{MethodName}({Arguments})", targetMethod.DeclaringType.Name, targetMethod.Name, args);
            var result = targetMethod.Invoke(Target, args);
            _logger.Information("{TypeName}.{MethodName} returned -> {ReturnValue}", targetMethod.DeclaringType.Name, targetMethod.Name, result);

            return result;
        }
        catch (TargetInvocationException exc)
        {

            _logger.Warning(exc.InnerException, "{TypeName}.{MethodName} threw exception: {Exception}", targetMethod.DeclaringType.Name, targetMethod.Name, exc.InnerException);
            throw exc.InnerException;
        }
    }
}

So, the code above shows that we have to inherit from DispatchProxy and override the Invoke method (which provides invocation dynamically through the reflection) implement additional logic then call the main object method through the reflection.

Everything is clear. We are almost ready to finish.

public static class LoggingDecoratorProxyFactory
{
    public static TInterface Create<TInterface, TConcrete>(TConcrete instance) where TConcrete : class, TInterface where TInterface : class
    {
        if (!typeof(TInterface).IsInterface)
            throw new Exception($"{typeof(TInterface).Name} must be interface!");
        LoggingDecoratorProxy<TInterface> proxy = LoggingDecoratorProxy<TInterface>.Create<TInterface, LoggingDecoratorProxy<TInterface>>()
                    as LoggingDecoratorProxy<TInterface>;
        proxy.Target = instance;
        return proxy as TInterface;
    }
}

The last code provides creating logging cross-cutting concerns through the proxy object. The input param is the instance of the interface that will decorate our proxy logging decorator. Inside the factory, decorate dynamically with DispatchProxy.Create method, then wrapped interface returned to the client.

The last part is the app, which is running and getting results.

IEmailMessageSender emailSender = LoggingDecoratorProxyFactory.Create<IEmailMessageSender, EmailMessageSender>(new EmailMessageSender());
emailSender.TrySendMessage("peter@hostforlife.eu", "Test", "Hi Peter");

Console.WriteLine("------------------------------------------------------------------------------------------");

IServerInfoRetriever retriever = LoggingDecoratorProxyFactory.Create<IServerInfoRetriever, ServerInfoRetriever>(new ServerInfoRetriever());
retriever.GetInfo(IPAddress.Parse("127.0.0.1"));

As you can see in our sample, we created a cross-cutting concern (logging invocation) and decorated dynamically through the DispatchProxy in runtime with any objects. This way, DispatchProxy allowed us to implement AOP in any desired type.

Conclusion

The DispatchProxy class is a powerful tool in the .NET ecosystem for creating dynamic proxies, allowing developers to intercept and modify method invocations at runtime. This capability enables flexible and clean implementations of cross-cutting concerns like logging, caching, and performance monitoring without cluttering the core business logic. By leveraging DispatchProxy, developers can maintain a modular and decoupled codebase, enhancing both maintainability and extensibility.

Stay tuned!

Best and Most Recommended ASP.NET Core 8.0.8 Hosting

Fortunately, there are a number of dependable and recommended web hosts available that can help you gain control of your website’s performance and improve your ASP.NET Core 9.0 web ranking. HostForLIFEASP.NET is highly recommended. In Europe, HostForLIFEASP.NET is the most popular option for first-time web hosts searching for an affordable plan.

Their standard price begins at only €3.49 per month. Customers are permitted to choose quarterly and annual plans based on their preferences. HostForLIFEASP.NET guarantees “No Hidden Fees” and an industry-leading ’30 Days Cash Back’ policy. Customers who terminate their service within the first thirty days are eligible for a full refund.

By providing reseller hosting accounts, HostForLIFEASP.NET also gives its consumers the chance to generate income. You can purchase their reseller hosting account, host an unlimited number of websites on it, and even sell some of your hosting space to others. This is one of the most effective methods for making money online. They will take care of all your customers’ hosting needs, so you do not need to fret about hosting-related matters.