Proxy Pattern

Hello everyone, and welcome to .NET Core Central. In this blog post, I will walk through the proxy design pattern. The proxy design pattern is one of the design patterns from the Gang of Four design patterns.

And the proxy design pattern is probably one of the simplest and easiest to understand and implement design patterns in the gang of four design patterns. this pattern is also a structural design pattern.

The intent of the Proxy Pattern

The main intent of the proxy design pattern is to provide a placeholder for another object to control access to it. Now, this may sound a little bit complex, but when we walk through an example, it will become very simple.

The main idea behind this pattern is you proxy an object with another placeholder so that you can control how the final object is accessed by the end user. So let’s take an example and show how it really works in a real-life scenario.

Demonstration of Proxy Pattern

I am going to use an ASP.NET Core Web API project to demonstrate how to implement proxy design patterns in .NET. I am going to use the default template provided by Visual Studio for creating the ASP.NET Core Web API project.

The default ASP.NET Core Web API project template comes with the default weather forecast controller. And the weather forecast implementation.

The name of the project is ProxyPattern.Demo, and I will keep the same name for the solution as well.

New class library

Next what I am going to do is to move the weather forecast implementation into a new class library. So I will create a new class library project in the same solution, which is ProxyPattern.Demo.

In name this class library as Weather.Forecast. And inside the Weather.Forecast project.

Firstly, I will move the data model, WeatherForecast. And there will be four properties inside this model, Date, TemparatureC, TemparatureF, and Summary.

namespace Weather.Forecast;

public class WeatherForecast
{
    public DateTime Date { get; set; }

    public int TemperatureC { get; set; }

    public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);

    public string? Summary { get; set; }
}

Secondly, I will create a new interface IWeatherProvider. And this new interface will have a single method Get. The Get method has a single integer parameter totalCount and returns an IEnumerable of WeatherForecast.

namespace Weather.Forecast;

public interface IWeatherProvider
{
    IEnumerable<WeatherForecast> Get(int totalCount);
}

Thirdly, I will create a new class WeatherProvider, which will implement the interface IWeatherProvider. And the code inside the WeatherProvider will be very similar to the default code which shows up with the template when you create a new ASP.NET Core Web API application.

The only change I will do is to use the totalCount parameter to select the number of items to return.

namespace Weather.Forecast;

public class WeatherProvider : IWeatherProvider
{
    private static readonly string[] Summaries = new[]
    {
        "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
    };

    public IEnumerable<WeatherForecast> Get(int totalCount)
    {
        return Enumerable.Range(1, totalCount).Select(index => new WeatherForecast
        {
            Date = DateTime.Now.AddDays(index),
            TemperatureC = Random.Shared.Next(-20, 55),
            Summary = Summaries[Random.Shared.Next(Summaries.Length)]
        }).ToArray();
    }
}

The use case for Proxy Pattern

In our everyday applications, we use third-party libraries. And we usually get these packages into our project either through project reference or through NuGet. The projects created above are no different.

Now let’s say here the requirement is for us to call the WeatherProvider to get weather information. But let’s say we want to maintain the totalCount only between two and five.

So anything which is less than two or greater than five we will reject. If we directly use this library and consider that this is an external NuGet package, then it is very hard to control this behavior. Because the logic exists inside of this class.

And that is precisely where the proxy pattern helps us. So to solve this problem, we essentially create a proxy of the WeatherProvider class in our Web API project, the ProxyPattern.Demo project.

Changes to the Web API

Inside the Web API project, I will create a new class. And I’m going to name the class as WeatherProviderProxy. The WeatherProviderProxy is going to implement the same interface as WeatherProvider which is the IWeatherProvider.

We will create a constructor for the WeatherProviderProxy class. And we will inject the IWeatherProvider interface into the constructor. This IWeatherProvider will contain the instance of WeatherProvider from the class library in our case.

And once we implement the IWeatherProvider interface, we will provide an implementation for the Get method. Inside the Get method, first, we will check if totalCount is less than two or greater than fine, then just throw a new ArgumentOutOfRangeException. And the message for the exception will be “Total count should be between two and five”.

After implementing the if condition, which is a logic specific to the Web API, we will just call the Get method of the IWeatherProxy instance injected through the constructor.

using Weather.Forecast;

namespace ProxyPattern.Demo;

public class WeatherProviderProxy : IWeatherProvider
{
    private readonly IWeatherProvider weatherProvider;

    public WeatherProviderProxy(IWeatherProvider weatherProvider)
    {
        this.weatherProvider = weatherProvider;
    }

    public IEnumerable<WeatherForecast> Get(int totalCount)
    {
        if (totalCount < 2 || totalCount > 5)
            throw new ArgumentOutOfRangeException("totalCount", "Total count should be between two and five");
        return weatherProvider.Get(totalCount);
    }
}

How this relates to the Proxy Pattern

As you can see here, we created this proxy which is implementing the same interface as the library on the NuGet package that we intend to use. In the constructor of the proxy class, we injected the object which is from the library or the NuGet package.

Inside the implementation, we are going to implement our logic which we need to control. And as you remember, the main intent of the proxy pattern is to provide a placeholder for another object to control access to it. So here we are controlling the access to this object through this condition.

And access control can be many things, it can be an access control due to authorization role, or it can be for checking some constraints like we are doing, or for any other reason. But this gives you the power to restructure an object and augment it. And that is why it is also a structural design pattern.

Updating the controller class

Next, we will update the WeatherForecastController to use the new proxy implementation. Hence for that, we will inject the IWeatherProvide interface into the constructor of the controller class.

Finally, we will update the HTTP Get method of the controller, and inside this method we will call the Get method of the IWeatherProvider instance, passing 7 as a parameter to demonstrate the argument exception scenario.

using Microsoft.AspNetCore.Mvc;
using Weather.Forecast;

namespace ProxyPattern.Demo.Controllers;

[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{


    private readonly ILogger<WeatherForecastController> _logger;
    private readonly IWeatherProvider _weatherProvider;

    public WeatherForecastController(ILogger<WeatherForecastController> logger,
        IWeatherProvider weatherProvider)
    {
        _logger = logger;
        _weatherProvider = weatherProvider;
    }

    [HttpGet(Name = "GetWeatherForecast")]
    public IEnumerable<WeatherForecast> Get()
    {
        return _weatherProvider.Get(7);
    }
}

Before we can test this, we will need to add all the dependencies to the dependency injection container in the Program class. During the dependency injection, we need to keep in mind that both WeatherProviderProxy and WeatherProvider implement the same interface IWeatherProvider.

The dependency injection

But for the controller, we will need to make sure the IWeatherProvider point to the instance of the WeatherProviderProxy. So to achieve we will use the following registration in the Program class.

builder.Services.AddSingleton<IWeatherProvider>(new WeatherProviderProxy(new WeatherProvider()));

The code for the entire Program class is below:

using ProxyPattern.Demo;
using Weather.Forecast;

var builder = WebApplication.CreateBuilder(args);


builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

builder.Services.AddSingleton<IWeatherProvider>(new WeatherProviderProxy(new WeatherProvider()));

var app = builder.Build();

if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();

Running the application with Proxy Pattern

Next, we will run the application. Once the application is running, we will use the Swagger page to try out the Get call.

proxy pattern

We can see in the output we are getting the argument out-of-range exception as expected. Next, I will change the code to pass 4 as the totalCount parameter. And in this case, the application will return the appropriate weather forecast.

proxy pattern success

Conclusion

So as you can see the implementation of the proxy pattern is extremely simple. But at the same time, it is extremely powerful. Another place where we use proxy a lot, but we might not implement it exactly this way, is when we call a Web API.

Calling out to a Web API is also usually a proxy pattern. So if we are making a Get, Post, and Put call, we might be creating three methods for Get, Post and Put inside of the class and they will be responsible for proxying the call out to an external service.

It is a little bit different from the traditional proxy pattern, but it works very similarly to that. And as I mentioned earlier in the blog post, the proxy pattern is one of the simplest patterns to understand as well as implement.

A Youtube video for this implementation is available here.