webapi开发框架实践

项目链接以及目录结构

liuzhixin405/efcore-template (github.com)

这是一个纯webapi的开发框架。

1、支持的orm有efcore6、dapper,可以灵活切换数据库。

using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Elfie.Model;
using Microsoft.EntityFrameworkCore;
using project.Context;
using project.Repositories;
using project.Services;
using RepositoryComponent.DbFactories;

namespace project.Extensions
{
    public static partial class TheExtensions
    {
        public static void AddDatabase(this WebApplicationBuilder builder)
        {
            ///sqlserver   
            if (builder.Configuration["DbType"]?.ToLower() == "sqlserver")
            {
                builder.Services.AddDbContext<ReadProductDbContext>(options => options.UseSqlServer(builder.Configuration["ConnectionStrings:SqlServer:ReadConnection"]), ServiceLifetime.Scoped);
                builder.Services.AddDbContext<WriteProductDbContext>(options => options.UseSqlServer(builder.Configuration["ConnectionStrings:SqlServer:WriteConnection"]), ServiceLifetime.Scoped);

            }
            ///mysql
            else if (builder.Configuration["DbType"]?.ToLower() == "mysql")
            {
                builder.Services.AddDbContext<ReadProductDbContext>(options => options.UseMySQL(builder.Configuration["ConnectionStrings:MySql:ReadConnection"]), ServiceLifetime.Scoped);
                builder.Services.AddDbContext<WriteProductDbContext>(options => options.UseMySQL(builder.Configuration["ConnectionStrings:MySql:WriteConnection"]), ServiceLifetime.Scoped);

            }
            else
            {
                //throw new ArgumentNullException("δ����ȷ��ע�����ݿ�");
                builder.Services.AddDbContext<ReadProductDbContext>(options => options.UseInMemoryDatabase("test_inmemory_db"), ServiceLifetime.Scoped);
                builder.Services.AddDbContext<WriteProductDbContext>(options => options.UseInMemoryDatabase("test_inmemory_db"), ServiceLifetime.Scoped);

            }

            builder.Services.AddScoped<Func<ReadProductDbContext>>(provider => () => provider.GetService<ReadProductDbContext>() ?? throw new ArgumentNullException("ReadProductDbContext is not inject to program"));
            builder.Services.AddScoped<Func<WriteProductDbContext>>(provider => () => provider.GetService<WriteProductDbContext>() ?? throw new ArgumentNullException("WriteProductDbContext is not inject to program"));

            builder.Services.AddScoped<DbFactory<WriteProductDbContext>>();
            builder.Services.AddScoped<DbFactory<ReadProductDbContext>>();

            builder.Services.AddTransient<IReadProductRepository, ProductReadRepository>();
            builder.Services.AddTransient<IWriteProductRepository, ProductWriteRepository>();
            builder.Services.AddTransient<IProductService, ProductService>();

            builder.Services.AddTransient<ICustomerService, CustomerService>();
        }
    }
}

2、至于消息中间件有rabbitmq、kafka,也是通过配置文件来指定哪一个实现。

using MessageMiddleware.Factory;
using MessageMiddleware.RabbitMQ;

namespace project.Extensions
{
    public static partial class TheExtensions
    {
        public static void AddMq(this WebApplicationBuilder builder)
        {
            var rabbitMqSetting = new RabbitMQSetting
            {
                ConnectionString = builder.Configuration["MqSetting:RabbitMq:ConnectionString"].Split(';'),
                Password = builder.Configuration["MqSetting:RabbitMq:PassWord"],
                Port = int.Parse(builder.Configuration["MqSetting:RabbitMq:Port"]),
                SslEnabled = bool.Parse(builder.Configuration["MqSetting:RabbitMq:SslEnabled"]),
                UserName = builder.Configuration["MqSetting:RabbitMq:UserName"],
            };
            var kafkaSetting = new MessageMiddleware.Kafka.Producers.ProducerOptions
            {
                BootstrapServers = builder.Configuration["MqSetting:Kafka:BootstrapServers"],
                SaslUsername = builder.Configuration["MqSetting:Kafka:SaslUserName"],
                SaslPassword = builder.Configuration["MqSetting:Kafka:SaslPassWord"],
                Key = builder.Configuration["MqSetting:Kafka:Key"]
            };
            var mqConfig = new MQConfig
            {
                ConsumerLog = bool.Parse(builder.Configuration["MqSetting:ConsumerLog"]),
                PublishLog = bool.Parse(builder.Configuration["MqSetting:PublishLog"]),
                Rabbit = rabbitMqSetting,
                Use = int.Parse(builder.Configuration["MqSetting:Use"]),
                Kafka = kafkaSetting
            };
            builder.Services.AddSingleton<MQConfig>(sp => mqConfig);
            builder.Services.AddMQ(mqConfig);
        }
    }
}

3、该项目还集成了mongodb和elasticsearch,在project项目中没有写实现案例,实现起来也很简单。

4、下面是分布式雪花id的实现,先注入代码,使用的时候直接使用distributedid即可。

 builder.Services.AddDistributedLock(x =>
 {
     x.LockType = LockType.InMemory;
     x.RedisEndPoints = new string[] { builder.Configuration["DistributedRedis:ConnectionString"] ?? throw new Exception("$未能获取distributedredis连接字符串")};
 }).AddCache(new CacheOptions
 {
     CacheType = CacheTypes.Redis,
     RedisConnectionString = builder.Configuration["DistributedRedis:ConnectionString"] ?? throw new Exception("$未能获取distributedredis连接字符串")
 }).AddDistributedId(new DistributedIdOptions
 {
     Distributed = true
 });
 newProduct.Id = _distributedId.NewLongId().ToString();

5、缓存使用的是分布式缓存和内存缓存,其中分布式缓存有一般实现和指定序列化格式的实现。

using System.Text;
using System.Text.Json.Serialization;
using MessagePack;
using StackExchange.Redis.Extensions.Core;
using StackExchange.Redis.Extensions.Core.Abstractions;
using StackExchange.Redis.Extensions.Core.Configuration;
using StackExchange.Redis.Extensions.Core.Implementations;

namespace project.Utility.Helper
{
    public class CacheHelper
    {
        private static IRedisClientFactory _factory_with_msgpack;
        private static IRedisDatabase _redis_with_msgpack => _factory_with_msgpack.GetDefaultRedisDatabase();

        private static IRedisClientFactory _factory;
        private static IRedisDatabase _redis => _factory.GetDefaultRedisDatabase();
        public static void Init(IConfiguration configuration)
        {
            var config = configuration.GetSection("Redis").Get<RedisConfiguration>();
            _factory = new RedisClientFactory(new[] { config }, null, new RedisSerializer());
            _factory_with_msgpack = new RedisClientFactory(new[] { config }, null, new RedisMessagepackSerializer());
        }
        static CacheHelper() { }

        public static T Get<T>(string key)
        {
            return _redis.GetAsync<T>(key).GetAwaiter().GetResult();
        }
        public static async Task<T> GetAsync<T>(string key)
        {
            return await _redis.GetAsync<T>(key);
        }
        public static async Task<T> GetAsync_With_Msgpack<T>(string key)
        {
            return await _redis_with_msgpack.GetAsync<T>(key);
        }

        public static string Get(string key)
        {
            return _redis.GetAsync<string>(key).GetAwaiter().GetResult();
        }

        public static bool Set(string key, object value, TimeSpan expiresIn)
        {
            return _redis.AddAsync(key, value, expiresIn).GetAwaiter().GetResult();
        }
        public static async Task<bool> SetAsync(string key, object value, TimeSpan expiresIn)
        {
            return await _redis.AddAsync(key, value, expiresIn);
        }

        public static async Task<bool> SetAsync_With_Msgpack(string key, object value, TimeSpan expiresIn)
        {
            return await _redis_with_msgpack.AddAsync(key, value, expiresIn);
        }

        /// <summary>
        /// 以秒为单位,返回给定 key 的剩余生存时间
        /// </summary>

        public static long GetExpirin(string key)
        {
            var timespan = _redis.Database.KeyTimeToLive(key);
            if (timespan == null) { return 0; }
            return (long)timespan.Value.TotalSeconds;
        }
        public static bool KeyExpire(string key, TimeSpan expiresIn)
        {
            return _redis.Database.KeyExpire(key, expiresIn);
        }
        public static async Task<bool> RemoveKeyAsync(string key)
        {
            return await _redis.Database.KeyDeleteAsync(key);
        }
        public static long RemoveKey(string key)
        {
            var result = _redis.Database.KeyDelete(key);
            return result ? 1 : 0;
        }
    }


    public class RedisSerializer : ISerializer
    {
        public T? Deserialize<T>(byte[] serializedObject)
        {
            var data = Encoding.UTF8.GetString(serializedObject);
            return System.Text.Json.JsonSerializer.Deserialize<T>(data);
        }

        public byte[] Serialize<T>(T? item)
        {
            var data = System.Text.Json.JsonSerializer.Serialize(item);
            return Encoding.UTF8.GetBytes(data);
        }
    }

    public class RedisMessagepackSerializer : ISerializer
    {
        private MessagePackSerializerOptions _options;
        public RedisMessagepackSerializer()
        {
            _options = MessagePackSerializerOptions.Standard.WithCompression(MessagePackCompression.Lz4BlockArray);
        }
        public T? Deserialize<T>(byte[] serializedObject)
        {
            return MessagePackSerializer.Deserialize<T>(serializedObject, _options);
        }

        public byte[] Serialize<T>(T? item)
        {
            return MessagePackSerializer.Serialize(item, _options);
        }
    }
}

6、单元测试、集成测试没有写。

更细节的需要自己看代码,这应该是一个基本的开发具备的功能。

该项目下载下来可以直接运行。

posted @ 2023-09-12 14:44  星仔007  阅读(713)  评论(0编辑  收藏  举报