create account

Adapter pattern in plug-and-play architecture in Asp.NET 5 by dotnetguru

View this thread on: hive.blogpeakd.comecency.com
· @dotnetguru ·
$11.22
Adapter pattern in plug-and-play architecture in Asp.NET 5
After the last [blog post](https://ecency.com/hive-169321/@dotnetguru/preparation-for-plug-and-play) we are prepared to start the implementation of architecture aspects.

First, we need to create a new project where we will place the aspects. We don't want to place them in an executable project because we will be using them in both web and console applications in the next blog post. The new project is named "PlugAndPlayExample.Infrastructure".

![image.png](https://images.ecency.com/DQmUvzwvwaMR8wdY7hyQkYBNJTtFLp6gmrgyKKoTMuh7cPZ/image.png)

For simplicity's sake, we won't implement all infrastructure aspects that are almost always required for a reliable architecture, and maybe we will cover them in another series. Right now it is enough to implement a caching mechanism and a logger. After we are done the project and file structure should look like this:

![image.png](https://images.ecency.com/DQmPSPd7wnqHqhW6ZVMzke9a7j1vuQBDGt25Pd8u597S5Ue/image.png)

Both aspect implementations will follow the adapter pattern for their respective functionalities, and that will allow us to swap them as needed, either by configuration or by convention.

The adapter pattern dictates that we create an interface that the implementations will follow:

```
using System;

namespace PlugAndPlayExample.Infrastructure.Logging
{
    public interface ILogger
    {
        void Trace(string message);

        void Info(string message);

        void Warn(string message);

        void Error(string message);

        void Fatal(string message);

        void Info(string message, Exception exception);

        void Warn(string message, Exception exception);

        void Error(string message, Exception exception);

        void Fatal(string message, Exception exception);
    }
}
```

And then create all implementations that we want or need. For examples sake we will create simple console and file loggers:

```
using System;

namespace PlugAndPlayExample.Infrastructure.Logging
{
    public class ConsoleLogger : ILogger
    {
        public void Error(string message)
        {
            Console.Error.WriteLine("ERROR: {0}", message);
        }

        public void Error(string message, Exception exception)
        {
            Console.Error.WriteLine("ERROR: {0}{1}{2}", message, Environment.NewLine, exception);
        }

        public void Fatal(string message)
        {
            Console.Error.WriteLine("FATAL: {0}", message);
        }

        public void Fatal(string message, Exception exception)
        {
            Console.Error.WriteLine("FATAL: {0}{1}{2}", message, Environment.NewLine, exception);
        }

        public void Info(string message)
        {
            Console.Error.WriteLine("INFO: {0}", message);
        }

        public void Info(string message, Exception exception)
        {
            Console.Error.WriteLine("INFO: {0}{1}{2}", message, Environment.NewLine, exception);
        }

        public void Trace(string message)
        {
            Console.Error.WriteLine("TRACE: {0}", message);
        }

        public void Warn(string message)
        {
            Console.Error.WriteLine("WARN: {0}", message);
        }

        public void Warn(string message, Exception exception)
        {
            Console.Error.WriteLine("WARN: {0}{1}{2}", message, Environment.NewLine, exception);
        }
    }
}
```

```
using System;
using System.IO;

namespace PlugAndPlayExample.Infrastructure.Logging
{
    public class FileLogger : ILogger
    {
        private readonly FileInfo file;

        public FileLogger(FileInfo file)
        {
            this.file = file;
            if (!file.Exists)
            {
                file.Create();
            }
        }
        public void Error(string message)
        {
            WriteLine("ERROR: {0}", message);
        }

        public void Error(string message, Exception exception)
        {
            WriteLine("ERROR: {0}{1}{2}", message, Environment.NewLine, exception);
        }

        public void Fatal(string message)
        {
            WriteLine("FATAL: {0}", message);
        }

        public void Fatal(string message, Exception exception)
        {
            WriteLine("FATAL: {0}{1}{2}", message, Environment.NewLine, exception);
        }

        public void Info(string message)
        {
            WriteLine("INFO: {0}", message);
        }

        public void Info(string message, Exception exception)
        {
            WriteLine("INFO: {0}{1}{2}", message, Environment.NewLine, exception);
        }

        public void Trace(string message)
        {
            WriteLine("TRACE: {0}", message);
        }

        public void Warn(string message)
        {
            WriteLine("WARN: {0}", message);
        }

        public void Warn(string message, Exception exception)
        {
            WriteLine("WARN: {0}{1}{2}", message, Environment.NewLine, exception);
        }

        private void WriteLine(string format, params object[] args)
        {
            using(var writer = new StreamWriter(file.OpenWrite()))
            {
                writer.WriteLine(format, args);
            }
        }
    }
}
```

We need to do the same thing for the caching aspect, but for now we will only implement an in-memory one and not worry about other implementations.

```
namespace PlugAndPlayExample.Infrastructure.Caching
{
    public interface ICache
    {
        T Get<T>(string key) where T : class;

        void Set<T>(string key, T value);
    }
}
```

```
using System.Collections.Generic;

namespace PlugAndPlayExample.Infrastructure.Caching
{
    public class InMemoryCache : ICache
    {
        private readonly Dictionary<string, object> _cache = new Dictionary<string, object>();

        public T Get<T>(string key) where T : class
        {
            if(!string.IsNullOrEmpty(key) && _cache.ContainsKey(key))
                return _cache[key] as T;

            return default(T);
        }

        public void Set<T>(string key, T value)
        {
            _cache.Add(key, value);
        }

        public object this[string key]
        {
            get { return Get<object>(key); }
            set { Set(key, value); }
        }
    }
}
```

Latter on if we want to use some other caching mechanism, like Redis, we can just create another implementation that will utilize the Redis caching server and swap the registered implementation in the service container.

In order to register the newly created aspects we will create another extension method:

```
using Microsoft.Extensions.DependencyInjection;
using PlugAndPlayExample.Infrastructure.Caching;
using PlugAndPlayExample.Infrastructure.Logging;
using System.IO;

namespace PlugAndPlayExample.Configuration
{
    public static class RegisterInfrastructureAspectsExtension
    {
        public static IServiceCollection RegisterInfrastructureAspects(this IServiceCollection services)
        {
            services.AddSingleton<ICache, InMemoryCache>();
            services.AddSingleton<ILogger, FileLogger>(provider => new FileLogger(new FileInfo("my_log_file.log")));

            return services;
        }
    }
}
```

And call that extension method in the startup class

```
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvc();
            services.RegisterServices();
            services.RegisterInfrastructureAspects();
        }
```

This is a clear example of how the Adapter pattern and the "Dependency Injection" "Inversion of Control" pattern play together to allow us a simple decoupling of concerns and allow us to swap real implementations. Lets see how to do that in a command:

```
using PlugAndPlayExample.Infrastructure.Caching;
using PlugAndPlayExample.Services.Infrastructure;

namespace PlugAndPlayExample.Services.Commands
{
    public class AddUser
    {
        public class Request: ICommand
        {
            public string UserName { get; set; }
            public string Password { get; set; }
        }

        public class Handler : ICommandHandler<Request, Response<int>>
        {
            private readonly ICache cache;

            public Handler(ICache cache)
            {
                this.cache = cache;
            }

            public Response<int> Handle(Request command)
            {
                var user = // User registration code...

                cache.Set<User>($"user-{user.Id}", user);

                return user.Id;
            }
        }
    }
}
```

In this example, the DI (Dependency Injection) container will inject the InMemoryCache implementation for our ICache service. If we want to use Redis we can create another implementation of the ICache interface that will use the Redis server, register it in the service container as an implementation of the ICache interface, and the DI container will inject it in our command handler.

In the next blog post we will see how we can create an execution pipeline and inject some aspects in it, like the logger, and manipulate that it to fit our needs as part of a convention or through a configuration.

*Happy coding,*
*DotNetGuru*
👍  , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , and 269 others
properties (23)
authordotnetguru
permlinkadapter-pattern-in-plug-and
categoryhive-169321
json_metadata{"links":["https://ecency.com/hive-169321/@dotnetguru/preparation-for-plug-and-play"],"image":["https://images.ecency.com/DQmUvzwvwaMR8wdY7hyQkYBNJTtFLp6gmrgyKKoTMuh7cPZ/image.png","https://images.ecency.com/DQmPSPd7wnqHqhW6ZVMzke9a7j1vuQBDGt25Pd8u597S5Ue/image.png"],"thumbnails":["https://images.ecency.com/DQmUvzwvwaMR8wdY7hyQkYBNJTtFLp6gmrgyKKoTMuh7cPZ/image.png","https://images.ecency.com/DQmPSPd7wnqHqhW6ZVMzke9a7j1vuQBDGt25Pd8u597S5Ue/image.png"],"tags":["hive-169321","development","programming","design-patterns"],"app":"ecency/3.0.20-vision","format":"markdown+html"}
created2022-01-08 10:31:06
last_update2022-01-08 10:31:06
depth0
children2
last_payout2022-01-15 10:31:06
cashout_time1969-12-31 23:59:59
total_payout_value5.717 HBD
curator_payout_value5.498 HBD
pending_payout_value0.000 HBD
promoted0.000 HBD
body_length9,163
author_reputation380,629,995,451
root_title"Adapter pattern in plug-and-play architecture in Asp.NET 5"
beneficiaries[]
max_accepted_payout1,000,000.000 HBD
percent_hbd10,000
post_id109,278,195
net_rshares6,699,966,184,196
author_curate_reward""
vote details (333)
@ecency ·
Your content has been **voted** as a part of [Encouragement program](https://ecency.com/ecency/@good-karma/encouragement-program-continues-82eafcd10a299). Keep up the good work! <br><br>Use Ecency daily to boost your growth on platform! <br><br><b>Support Ecency</b><br>[Vote for new Proposal](https://hivesigner.com/sign/update-proposal-votes?proposal_ids=%5B197%5D&approve=true)<br>[Delegate HP and earn more](https://ecency.com/hive-125125/@ecency/daily-100-curation-rewards)
properties (22)
authorecency
permlinkre-202218t16155740z
categoryhive-169321
json_metadata{"tags":["ecency"],"app":"ecency/3.0.16-welcome","format":"markdown+html"}
created2022-01-08 16:15:57
last_update2022-01-08 16:15:57
depth1
children0
last_payout2022-01-15 16:15:57
cashout_time1969-12-31 23:59:59
total_payout_value0.000 HBD
curator_payout_value0.000 HBD
pending_payout_value0.000 HBD
promoted0.000 HBD
body_length478
author_reputation618,483,065,100,960
root_title"Adapter pattern in plug-and-play architecture in Asp.NET 5"
beneficiaries[]
max_accepted_payout1,000,000.000 HBD
percent_hbd10,000
post_id109,285,943
net_rshares0
@hivebuzz ·
Congratulations @dotnetguru! You have completed the following achievement on the Hive blockchain and have been rewarded with new badge(s):

<table><tr><td><img src="https://images.hive.blog/60x70/http://hivebuzz.me/@dotnetguru/upvoted.png?202201081617"></td><td>You received more than 50 upvotes.<br>Your next target is to reach 100 upvotes.</td></tr>
</table>

<sub>_You can view your badges on [your board](https://hivebuzz.me/@dotnetguru) and compare yourself to others in the [Ranking](https://hivebuzz.me/ranking)_</sub>
<sub>_If you no longer want to receive notifications, reply to this comment with the word_ `STOP`</sub>



**Check out the last post from @hivebuzz:**
<table><tr><td><a href="/hivebuzz/@hivebuzz/pum-202201-8"><img src="https://images.hive.blog/64x128/https://i.imgur.com/4rgjfmA.png"></a></td><td><a href="/hivebuzz/@hivebuzz/pum-202201-8">Hive Power Up Month - Feedback from Day 7</a></td></tr><tr><td><a href="/hivebuzz/@hivebuzz/pud-202201-feedback"><img src="https://images.hive.blog/64x128/https://i.imgur.com/xQGM37X.png"></a></td><td><a href="/hivebuzz/@hivebuzz/pud-202201-feedback">Happy New Year - Feedback from the first Hive Power Up Day of 2022</a></td></tr></table>

###### Support the HiveBuzz project. [Vote](https://hivesigner.com/sign/update_proposal_votes?proposal_ids=%5B%22199%22%5D&approve=true) for [our proposal](https://peakd.com/me/proposals/199)!
properties (22)
authorhivebuzz
permlinknotify-dotnetguru-20220108t162928
categoryhive-169321
json_metadata{"image":["http://hivebuzz.me/notify.t6.png"]}
created2022-01-08 16:29:30
last_update2022-01-08 16:29:30
depth1
children0
last_payout2022-01-15 16:29:30
cashout_time1969-12-31 23:59:59
total_payout_value0.000 HBD
curator_payout_value0.000 HBD
pending_payout_value0.000 HBD
promoted0.000 HBD
body_length1,399
author_reputation369,624,075,807,249
root_title"Adapter pattern in plug-and-play architecture in Asp.NET 5"
beneficiaries[]
max_accepted_payout1,000,000.000 HBD
percent_hbd10,000
post_id109,286,248
net_rshares0