Location>code7788 >text

Getting Started with Redis - C#|.NET Core Wrapper Nuget Package

Popularity:727 ℃/2024-09-19 11:48:47

After the previous chapters, it is safe to say that you can already count on getting started with Redis development. It's time to get to work on projects.

But today I also want to share a chapter with you: packaging your own Redis C# library and then packaging it into a Nuget package.

First of all, it should be noted that: not to develop a Redis client library, but based on the six libraries introduced in the previous article, do a simple package, really simple kind, is to wrap a shell.

Again, why do this.

01Reasons

1. Testable

I want the code to be injected into the program as a service, not as a static method to be called. Using dependency injection to provide services makes the program more testable. If you do unit testing, dependency injected services are easy to test by mock, while static methods are often hard to mock and inflexible to test;

2. Decoupling

For a system there will be a variety of technologies used to achieve a certain capability, there may be many ways to realize a function, we care about realizing the function, not what method you used. Back to the topic, what we need is to use Redis to realize the business function, and the specific use of that client library is not important.

Another example such as today we chose to take over the library encountered problems we can not solve how to do? Do we stop doing business? No way! And it so happens that this time CSRedisCore library can be solved, how would you choose?

At this point one might wonder how much it would cost to switch? The two libraries have different method names and different functionality, so if there are Redis method calls scattered all over the system, how can this be switched?

Imagine if we encapsulate a layer that provides a set of interfaces, packaged as a Nuget package, this time people use this Nuget package, and we only need to use the Nuget package with the implementation of the method replaced with the implementation of the CSRedisCore to achieve a little bit, we directly update the Nuget package, you may not have to change a line of code on the completion of the replacement.

Because we are relying on our own wrapped interfaces rather than specific Redis client libraries, we can decouple and easily replace them.

3. Expansion

The native functionality of Redis can be understood as the base functionality, showing that Redis has this capability. But how we use it and how to better utilize its value is a reflection of our own ability.

Have you ever wondered why some libraries are commercialized so well and are so simple and easy to use? Good commercialization means that the service provided is good, that is to say, it can help you do a lot of things, it adds a lot of their own creations to the native features.

Project to do more, we ourselves will encounter some similar features, if this time we want to encapsulate these similar features, where to put it? How to give others to use? If we encounter a function of existing libraries do not have the corresponding ability, we need to be based on the native development of their own realization and where to do it?

These features have accumulated more always have to have a place to put it, and this time if we encapsulate a layer of their own, where to put the problem is not solved all of a sudden, maybe do it to become a product.

I don't know if you feel that your mind has been opened at once by this point. Maybe we did not write a few lines of code, but the pattern opened up at once, and these lines of code may also produce unexpected gains, may be infinite possibilities, may also be the beginning of your realization of their own Redis client library.

Of course this is my own personal opinion on coding and packaging. I hope to give you some help.

02Realization

Let's cut to the chase and get to the point, what if it comes encapsulated?

Let's sort out the general idea:

1. We need an interface that contains: the original library native capabilities Client, other our custom features.

2. An entrance, someone else has to use it, there has to be an entrance.

The requirements are actually that simple.

Let's take the example of encapsulating CSRedisClient, first define the IRedisService interface, which contains the Client field and two custom methods for the demo

using CSRedis;
namespace
{
    public interface IRedisService
    {
        /// <summary>
        /// RedisClient
        /// </summary>
        /// <returns></returns>
        CSRedisClient Client { get; }
        #region Custom Methods
        /// <summary>
        /// Get the specified key value of
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <returns></returns>
        T Get<T>(string key);
        /// <summary>
        /// Get the specified key value of
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <returns></returns>
        Task<T> GetAsync<T>(string key);
        #endregion
    }
}

Then you need to implement IRedisService, and at the same time take CSRedisClient as the constructor entry parameter, the specific code is as follows:

using CSRedis;
namespace
{
    public class RedisService : IRedisService
    {
        /// <summary>
        /// constructor
        /// </summary>
        /// <param name="redisClient"></param>
        public RedisService(CSRedisClient redisClient)
        {
            Client = redisClient;
        }
        /// <summary>
        /// CSRedis
        /// </summary>
        /// <returns></returns>
        public CSRedisClient Client { get; }
        #region Custom Methods
        /// <summary>
        /// Get the specified key value of
        /// </summary>
        /// <returns></returns>
        public T Get<T>(string key)
        {
            return <T>(key);
        }
        /// <summary>
        /// Get the specified key value of
        /// </summary>
        /// <returns></returns>
        public Task<T> GetAsync<T>(string key)
        {
            return <T>(key);
        }
        #endregion
    }
}

Here the first problem is solved, for the second problem we, we can extend the IServiceCollection to add the start-up extension method AddRedisClientSetup, the specific code is as follows:

using CSRedis;
using ;
using ;
namespace
{
    public static class RedisSetupExtensions
    {
        /// <summary>
        /// RedisClient startup items
        /// </summary>
        /// <param name="services"></param>
        /// <returns></returns>
        public static IServiceCollection AddRedisClientSetup(this IServiceCollection services)
        {
            <CSRedisClient>(serviceProvider =>
            {
                var configuration = <IConfiguration>();
                var setting = configuration["RedisConnectionString"];
                return new CSRedisClient(setting);
            });
            <IRedisService, RedisService>();
            return services;
        }
        /// <summary>
        /// RedisClient startup items
        /// </summary>
        /// <param name="services"></param>
        /// <param name="connectionString"></param>
        /// <returns></returns>
        public static IServiceCollection AddRedisClientSetup(this IServiceCollection services, string connectionString)
        {
            <CSRedisClient>(serviceProvider =>
            {
                return new CSRedisClient(connectionString);
            });
            <IRedisService, RedisService>();
            return services;
        }
    }
}

The reason why we need to provide two overloaded methods is that if the user names the Redis connection string with "RedisConnectionString" in the configuration file based on our convention, the user can directly call the AddRedisClientSetup() method to complete the Redis startup, but the user may not be able to comply with the convention for various reasons, so we also need to provide an entry point where the user can specify the Redis connection string method. Therefore, we also need to provide an entry point where users can specify the Redis connection string method.

Below we add the following configuration to the configuration file using the convention-based approach:

{
  "RedisConnectionString": "127.0.0.1:6379"
}

Then use the method to set key1, and then use the custom method Getmethod reads, the code is as follows:

public static void Run()
{
    var configuration = new ConfigurationBuilder()
        .SetBasePath()
        .AddJsonFile("", optional: false, reloadOnChange: true)
        .Build();
    var services = new ServiceCollection();
    <IConfiguration>(configuration);
    ();
    var redisService = ().GetService<IRedisService>();
    var setResult = ("key1", "value1");
    ($"(\"key1\",\"value1\")Implementation results:{setResult}");
    var value = <string>("key1");
    ($"<string>(\"key1\")Implementation results:{value}");
    ("key1");
}

The results of the implementation are as follows:

Is not very simple, and then we just need to put the above three files IRedisService, RedisService, RedisSetupExtensions into a separate class library, and then packaged and released into the Nuget package, you can give everyone together with the use of it, today I will not release the Nuget package, the back of the plan will be related to it, and then we will talk about it in detail. We will talk about it later.

classifier for sums of money: The test method code as well as the sample source code have been uploaded to the code repository for those who are interested./hugogoos/Planner