项目链接以及目录结构

liuzhixin405/efcore-template (github.com)

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

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

  1. using Microsoft.CodeAnalysis.CSharp.Syntax;
  2. using Microsoft.CodeAnalysis.Elfie.Model;
  3. using Microsoft.EntityFrameworkCore;
  4. using project.Context;
  5. using project.Repositories;
  6. using project.Services;
  7. using RepositoryComponent.DbFactories;
  8.  
  9. namespace project.Extensions
  10. {
  11. public static partial class TheExtensions
  12. {
  13. public static void AddDatabase(this WebApplicationBuilder builder)
  14. {
  15. ///sqlserver
  16. if (builder.Configuration["DbType"]?.ToLower() == "sqlserver")
  17. {
  18. builder.Services.AddDbContext<ReadProductDbContext>(options => options.UseSqlServer(builder.Configuration["ConnectionStrings:SqlServer:ReadConnection"]), ServiceLifetime.Scoped);
  19. builder.Services.AddDbContext<WriteProductDbContext>(options => options.UseSqlServer(builder.Configuration["ConnectionStrings:SqlServer:WriteConnection"]), ServiceLifetime.Scoped);
  20.  
  21. }
  22. ///mysql
  23. else if (builder.Configuration["DbType"]?.ToLower() == "mysql")
  24. {
  25. builder.Services.AddDbContext<ReadProductDbContext>(options => options.UseMySQL(builder.Configuration["ConnectionStrings:MySql:ReadConnection"]), ServiceLifetime.Scoped);
  26. builder.Services.AddDbContext<WriteProductDbContext>(options => options.UseMySQL(builder.Configuration["ConnectionStrings:MySql:WriteConnection"]), ServiceLifetime.Scoped);
  27.  
  28. }
  29. else
  30. {
  31. //throw new ArgumentNullException("δ����ȷ��ע�����ݿ�");
  32. builder.Services.AddDbContext<ReadProductDbContext>(options => options.UseInMemoryDatabase("test_inmemory_db"), ServiceLifetime.Scoped);
  33. builder.Services.AddDbContext<WriteProductDbContext>(options => options.UseInMemoryDatabase("test_inmemory_db"), ServiceLifetime.Scoped);
  34.  
  35. }
  36.  
  37. builder.Services.AddScoped<Func<ReadProductDbContext>>(provider => () => provider.GetService<ReadProductDbContext>() ?? throw new ArgumentNullException("ReadProductDbContext is not inject to program"));
  38. builder.Services.AddScoped<Func<WriteProductDbContext>>(provider => () => provider.GetService<WriteProductDbContext>() ?? throw new ArgumentNullException("WriteProductDbContext is not inject to program"));
  39.  
  40. builder.Services.AddScoped<DbFactory<WriteProductDbContext>>();
  41. builder.Services.AddScoped<DbFactory<ReadProductDbContext>>();
  42.  
  43. builder.Services.AddTransient<IReadProductRepository, ProductReadRepository>();
  44. builder.Services.AddTransient<IWriteProductRepository, ProductWriteRepository>();
  45. builder.Services.AddTransient<IProductService, ProductService>();
  46.  
  47. builder.Services.AddTransient<ICustomerService, CustomerService>();
  48. }
  49. }
  50. }

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

  1. using MessageMiddleware.Factory;
  2. using MessageMiddleware.RabbitMQ;
  3.  
  4. namespace project.Extensions
  5. {
  6. public static partial class TheExtensions
  7. {
  8. public static void AddMq(this WebApplicationBuilder builder)
  9. {
  10. var rabbitMqSetting = new RabbitMQSetting
  11. {
  12. ConnectionString = builder.Configuration["MqSetting:RabbitMq:ConnectionString"].Split(';'),
  13. Password = builder.Configuration["MqSetting:RabbitMq:PassWord"],
  14. Port = int.Parse(builder.Configuration["MqSetting:RabbitMq:Port"]),
  15. SslEnabled = bool.Parse(builder.Configuration["MqSetting:RabbitMq:SslEnabled"]),
  16. UserName = builder.Configuration["MqSetting:RabbitMq:UserName"],
  17. };
  18. var kafkaSetting = new MessageMiddleware.Kafka.Producers.ProducerOptions
  19. {
  20. BootstrapServers = builder.Configuration["MqSetting:Kafka:BootstrapServers"],
  21. SaslUsername = builder.Configuration["MqSetting:Kafka:SaslUserName"],
  22. SaslPassword = builder.Configuration["MqSetting:Kafka:SaslPassWord"],
  23. Key = builder.Configuration["MqSetting:Kafka:Key"]
  24. };
  25. var mqConfig = new MQConfig
  26. {
  27. ConsumerLog = bool.Parse(builder.Configuration["MqSetting:ConsumerLog"]),
  28. PublishLog = bool.Parse(builder.Configuration["MqSetting:PublishLog"]),
  29. Rabbit = rabbitMqSetting,
  30. Use = int.Parse(builder.Configuration["MqSetting:Use"]),
  31. Kafka = kafkaSetting
  32. };
  33. builder.Services.AddSingleton<MQConfig>(sp => mqConfig);
  34. builder.Services.AddMQ(mqConfig);
  35. }
  36. }
  37. }

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

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

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

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

  1. using System.Text;
  2. using System.Text.Json.Serialization;
  3. using MessagePack;
  4. using StackExchange.Redis.Extensions.Core;
  5. using StackExchange.Redis.Extensions.Core.Abstractions;
  6. using StackExchange.Redis.Extensions.Core.Configuration;
  7. using StackExchange.Redis.Extensions.Core.Implementations;
  8.  
  9. namespace project.Utility.Helper
  10. {
  11. public class CacheHelper
  12. {
  13. private static IRedisClientFactory _factory_with_msgpack;
  14. private static IRedisDatabase _redis_with_msgpack => _factory_with_msgpack.GetDefaultRedisDatabase();
  15.  
  16. private static IRedisClientFactory _factory;
  17. private static IRedisDatabase _redis => _factory.GetDefaultRedisDatabase();
  18. public static void Init(IConfiguration configuration)
  19. {
  20. var config = configuration.GetSection("Redis").Get<RedisConfiguration>();
  21. _factory = new RedisClientFactory(new[] { config }, null, new RedisSerializer());
  22. _factory_with_msgpack = new RedisClientFactory(new[] { config }, null, new RedisMessagepackSerializer());
  23. }
  24. static CacheHelper() { }
  25.  
  26. public static T Get<T>(string key)
  27. {
  28. return _redis.GetAsync<T>(key).GetAwaiter().GetResult();
  29. }
  30. public static async Task<T> GetAsync<T>(string key)
  31. {
  32. return await _redis.GetAsync<T>(key);
  33. }
  34. public static async Task<T> GetAsync_With_Msgpack<T>(string key)
  35. {
  36. return await _redis_with_msgpack.GetAsync<T>(key);
  37. }
  38.  
  39. public static string Get(string key)
  40. {
  41. return _redis.GetAsync<string>(key).GetAwaiter().GetResult();
  42. }
  43.  
  44. public static bool Set(string key, object value, TimeSpan expiresIn)
  45. {
  46. return _redis.AddAsync(key, value, expiresIn).GetAwaiter().GetResult();
  47. }
  48. public static async Task<bool> SetAsync(string key, object value, TimeSpan expiresIn)
  49. {
  50. return await _redis.AddAsync(key, value, expiresIn);
  51. }
  52.  
  53. public static async Task<bool> SetAsync_With_Msgpack(string key, object value, TimeSpan expiresIn)
  54. {
  55. return await _redis_with_msgpack.AddAsync(key, value, expiresIn);
  56. }
  57.  
  58. /// <summary>
  59. /// 以秒为单位,返回给定 key 的剩余生存时间
  60. /// </summary>
  61.  
  62. public static long GetExpirin(string key)
  63. {
  64. var timespan = _redis.Database.KeyTimeToLive(key);
  65. if (timespan == null) { return 0; }
  66. return (long)timespan.Value.TotalSeconds;
  67. }
  68. public static bool KeyExpire(string key, TimeSpan expiresIn)
  69. {
  70. return _redis.Database.KeyExpire(key, expiresIn);
  71. }
  72. public static async Task<bool> RemoveKeyAsync(string key)
  73. {
  74. return await _redis.Database.KeyDeleteAsync(key);
  75. }
  76. public static long RemoveKey(string key)
  77. {
  78. var result = _redis.Database.KeyDelete(key);
  79. return result ? 1 : 0;
  80. }
  81. }
  82.  
  83. public class RedisSerializer : ISerializer
  84. {
  85. public T? Deserialize<T>(byte[] serializedObject)
  86. {
  87. var data = Encoding.UTF8.GetString(serializedObject);
  88. return System.Text.Json.JsonSerializer.Deserialize<T>(data);
  89. }
  90.  
  91. public byte[] Serialize<T>(T? item)
  92. {
  93. var data = System.Text.Json.JsonSerializer.Serialize(item);
  94. return Encoding.UTF8.GetBytes(data);
  95. }
  96. }
  97.  
  98. public class RedisMessagepackSerializer : ISerializer
  99. {
  100. private MessagePackSerializerOptions _options;
  101. public RedisMessagepackSerializer()
  102. {
  103. _options = MessagePackSerializerOptions.Standard.WithCompression(MessagePackCompression.Lz4BlockArray);
  104. }
  105. public T? Deserialize<T>(byte[] serializedObject)
  106. {
  107. return MessagePackSerializer.Deserialize<T>(serializedObject, _options);
  108. }
  109.  
  110. public byte[] Serialize<T>(T? item)
  111. {
  112. return MessagePackSerializer.Serialize(item, _options);
  113. }
  114. }
  115. }

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

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

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

webapi开发框架实践的更多相关文章

  1. Restful WebApi开发实践

    随笔分类 - Restful WebApi开发实践   C#对WebApi数据操作 摘要: ## 目标简化并统一程序获取WebApi对应实体数据的过程,方便对实体进行扩充.原理就是数据服务使用反射发现 ...

  2. 一次asp.net core3.1打造webapi开发框架的实践

    aaarticlea/png;base64,iVBORw0KGgoAAAANSUhEUgAAAigAAAAbCAYAAABWfHSvAAAH30lEQVR4nO1dy5GsMAx80RIESRAEST ...

  3. 快读《ASP.NET Core技术内幕与项目实战》WebApi3.1:WebApi最佳实践

    本节内容,涉及到6.1-6.6(P155-182),以WebApi说明为主.主要NuGet包:无 一.创建WebApi的最佳实践,综合了RPC和Restful两种风格的特点 1 //定义Person类 ...

  4. 42岁大龄程序员的迷茫,看我最新尝鲜.net 5+Dapper搭建的WebAPI框架

    42岁大龄程序员的迷茫 我真傻,真的.我单知道雪天是野兽在深山里没有食吃,会到村里来;我不知道春天也会有-- 我真傻,真的.我单知道程序员要活到老学到老,年龄大了要失业;我不知道码农(新型农民工)也会 ...

  5. WebAPI

    WebAPI的Host OWIN IIS WebAPI 的handler和Filter有啥区别? WebAPI  常用 Filters Exception Filter Timer Filter Lo ...

  6. C#对WebApi数据操作

    目标 简化并统一程序获取WebApi对应实体数据的过程,方便对实体进行扩充.原理就是数据服务使用反射发现数据提供者,处理好泛型就行. 相关传送门:Restful WebApi开发实践 先来看下最后的请 ...

  7. 适合WebApi的简单的C#状态机实现

    目标 采用了Restful WebApi的架构,再把业务逻辑状态转移放到后端就有点违背初衷了.实际上只要后端Api的权限设置的好,把状态转移放到前端也未尝不可.我考虑的结果是,一般如果变更这个状态本身 ...

  8. 大道至简、大智若愚—GO语言最佳详解实践

      导读:2007年,受够了C++煎熬的Google首席软件工程师Rob Pike纠集Robert Griesemer和Ken Thompson两位牛人,决定创造一种新语言来取代C++, 这就是Gol ...

  9. 你应该知道Go语言的几个优势

    要说起GO语言的优势,我们就得从GO语言的历史讲起了-- 本文由腾讯技术工程官方号发表在腾讯云+社区 2007年,受够了C++煎熬的Google首席软件工程师Rob Pike纠集Robert Grie ...

  10. 基于.NetCore开发博客项目 StarBlog - (3) 模型设计

    系列文章 基于.NetCore开发博客项目 StarBlog - (1) 为什么需要自己写一个博客? 基于.NetCore开发博客项目 StarBlog - (2) 环境准备和创建项目 基于.NetC ...

随机推荐

  1. 【编程日记】搭建python开发环境

    0.相关确定 0.1确定操作系统 Python是一种跨平台的编程语言,这意味着它能够运行在所有主要的操作系统中.然而,在不同的操作系统(Windows/Mac/Linux)中,安装Python的方法存 ...

  2. Dapr在Java中的实践 之 服务调用

    服务调用 通过服务调用(Service-to-service Invocation),服务可以使用 gRPC 或 HTTP 这样的标准协议来发现并可靠地与其他服务通信. Dapr采用边车(Sideca ...

  3. 曲线艺术编程 coding curves 第八章 贝赛尔曲线(Bézier Curves)

    贝赛尔曲线(Bézier Curves) 原作:Keith Peters https://www.bit-101.com/blog/2022/11/coding-curves/ 译者:池中物王二狗(s ...

  4. R语言中的跨平台支持:如何在Windows、MacOS和Linux上使用R语言进行数据分析和可视化

    目录 当今数据科学领域,R语言已经成为了数据分析和可视化的流行工具.R语言具有强大的功能和灵活性,使得它可以在各种不同的平台上运行,包括Windows.MacOS和Linux.因此,本文将介绍R语言中 ...

  5. Java判断101-200之间有多少个素数,并输出所有素数。

    代码如下: public static void main(String[] args) { //记录个数 int count = 0; //循环遍历 for(int i = 101;i <= ...

  6. 手写RPC框架之泛化调用

    一.背景 前段时间了解了泛化调用这个玩意儿,又想到自己之前写过一个RPC框架(参考<手写一个RPC框架>),于是便想小试牛刀. 二.泛化调用简介 什么是泛化调用 泛化调用就是在不依赖服务方 ...

  7. 如何让ChatGPT高效的理解你的Prompt

    1.概述 ChatGPT是由 OpenAI 开发的一种强大的语言模型,它在许多自然语言处理任务中展现出了惊人的能力.而其中一个关键的技术概念就是 "Prompt".本文将深入探讨 ...

  8. C++面试八股文:什么是空指针/野指针/悬垂指针?

    某日二师兄参加XXX科技公司的C++工程师开发岗位第30面: 面试官:什么是空指针? 二师兄:一般我们将等于0/NULL/nullptr的指针称为空指针.空指针不能被解引用,但是可以对空指针取地址. ...

  9. Sentieon | 每周文献-Population Sequencing-第一期

    群体基因组系列文章-1 标题(英文):The Impact of ACEs on BMI: An Investigation of the Genotype-Environment Effects o ...

  10. 【SpringBoot】 集成 Ehcache

    SpringBoot ehcache 缓存 简介 EhCache 是一个纯 Java 的进程内缓存框架,具有快速.精干等特点, 是 Hibernate 中默认CacheProvider.Ehcache ...