无业游民写的最后一个.net有关项目框架

news/2024/7/7 18:09:25/文章来源:https://www.cnblogs.com/morec/p/18276172

理想很丰满,现实往往很残酷。

一种按照ddd的方式,根据业务来把自己需要的模块一个一个写出来,再按照模块把需要的接口一个一个的写出来,堆砌一些中间件,以及解耦的command,handler等等

,一个项目就这么成型了。上面的项目有一个非常清晰的特点,就是按需开发,不需要去可以定义业务相关的公共的模块,有就有没就没。这项目看起来没有什么公共框架,就是一个项目。当然这样效率性能也是最高的,不需要过多的包装一层又一层的公共代码。

有关示例如下,不做过多的赘述:

liuzhixin405/netcore-micro (github.com)

一种业务非常大,开发人员只需要写业务实现,这就需要一个公共框架,提供公共的可复制模块让业务人员写业务代码。

下面以为简洁的方式呈现这种开发模式,项目层级如下:

三个模块分别是业务模块,主机,基础模块。业务模块Business通过dll形式提供给host来完成注册和发布。

主机host可以存放公共的基础模块,例如注册、登录、认证等,这里省略。

业务模块存放业务代码,包括提供接口。

流程如下:request => 业务模块controller => business => service=> repository

整个项目接口不变,实现可各异。

 在仓储层定义几个公共的方法,

 public interface IRepository<TEntity,TID> where TEntity : IEntity<TID>{Task<ApiResult> AddAsync(TEntity entity);Task<ApiResult> UpdateAsync(TEntity entity);Task<ApiResult> DeleteAsync(Expression<Func<TEntity, bool>> filter);Task<ApiResult> DeleteAsync(TID id);// 通用分页查询Task<PagedResult<TEntity>> GetPagedAsync(PagingParameters<TEntity> pagingParameters);// 其他常用操作Task<IEnumerable<TEntity>> FindAsync(Expression<Func<TEntity, bool>> filter);}

服务层也是同样的方法

 [Intercept("business-service log")]public interface IService{Task<ApiResult> AddAsync(IRequestDto requestDto);Task<ApiResult> UpdateAsync(IRequestDto requestDto);Task<ApiResult> DeleteAsyncc(IRequestDto requestDto);Task<ApiResult> GetPagedAsyncc(IRequestDto requestDto) ;Task<ApiResult> FindAsyncc(IRequestDto requestDto);}

 

依赖注入还是老一套,实现它就行。

 public interface IModule{void ConfigureService(IServiceCollection services, IConfiguration configuration = null);void Configure(IApplicationBuilder app, IWebHostEnvironment env = null);}public abstract class ModuleBase : IModule{public virtual void ConfigureService(IServiceCollection services, IConfiguration configuration = null){}public virtual void Configure(IApplicationBuilder app, IWebHostEnvironment env = null){}}

在主机通过扫描assembly来注册服务

using Microsoft.AspNetCore.Mvc.ApplicationParts;
using Project.Base.Reflect;
using System.Reflection;
using Project.Base.ProjExtension;
using Project.Base.Common;
using Project.Base.DependencyInjection;
using Project.Base.Module;
namespace Project.Host
{public class Program{public static void Main(string[] args){var builder = WebApplication.CreateBuilder(args);builder.Configuration.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true);builder.Configuration.AddJsonFile($"appsettings.{builder.Environment.EnvironmentName}.json", optional: true, reloadOnChange: true);builder.Configuration.AddJsonFile("appsettings.Modules.json", optional: false, reloadOnChange: true);//IModule注入 ,然后扫描调用ConfigureService,Business注入需要的服务入口
            builder.Services.InitModule(builder.Configuration);var sp = builder.Services.BuildServiceProvider();var moduleInitializers = sp.GetServices<IModule>();foreach (var moduleInitializer in moduleInitializers){moduleInitializer.ConfigureService(builder.Services, builder.Configuration);}// Add services to the container.var assemblys = GolbalConfiguration.Modules.Select(x => x.Assembly).ToList();var mvcBuilder=builder.Services.AddControllers().ConfigureApplicationPartManager(apm => {var folder = Path.Combine(Directory.GetCurrentDirectory(), "bus_lib");var serviceList = (builder.Configuration.GetSection("ServiceList").Get<string[]>()) ?? new string[] { "ADM" };//默认加载基础服务string[] serviceFiles = Directory.GetFiles(folder, "*.Api.dll").Where(x =>serviceList.Any(y => x.Contains(y))).ToArray();foreach (var file in serviceFiles){if (File.Exists(file)){var assembly = Assembly.LoadFrom(file);var controllerAssemblyPart = new AssemblyPart(assembly);apm.ApplicationParts.Add(controllerAssemblyPart);}}});foreach (var assembly in assemblys){// 扫描并注册其他程序集中的控制器
                mvcBuilder.AddApplicationPart(assembly);// 扫描并注册其他程序集中的服务   针对特性注入
                builder.Services.ReisterServiceFromAssembly(assembly);} // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
            builder.Services.AddEndpointsApiExplorer();builder.Services.AddSwaggerGen();builder.Services.AddBusinessServices();var app = builder.Build();ServiceLocator.Instance = app.Services;//imodule 的Configure调用,business可以实现中间件等操作foreach (var moduleInitializer in moduleInitializers){moduleInitializer.Configure(app, app.Environment);}// Configure the HTTP request pipeline.if (app.Environment.IsDevelopment()){app.UseSwagger();app.UseSwaggerUI();}app.UseHttpsRedirection();app.UseAuthorization();app.MapControllers();app.Run();}}
}

 

业务需求注入代码如下:

using ADM001_User.Model;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using MongoDB.Bson.Serialization.Serializers;
using MongoDB.Bson.Serialization;
using MongoDB.Bson;
using MongoDB.Driver;
using Project.Base.IRepository;
using Project.Base.Module;
using Project.Base.Reflect;
using Project.Base.Repository;
using Project.Base.Services;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ADM001_User.Business.Settings;
using Project.Base.Model;namespace ADM001_User.Business
{public class UserModule : ModuleBase{public override void ConfigureService(IServiceCollection services, IConfiguration configuration = null){services.AddDbContext<UserDbContext>(options =>options.UseInMemoryDatabase("InMemoryDb"));services.AddScoped<IRepository<User, int>, GenericRepository<User, int, UserDbContext>>();services.AddTransient<IService, UserService>();AddMongo(services);AddMongoRepository<User, int>(services, "users");}private static IServiceCollection AddMongo(IServiceCollection services){BsonSerializer.RegisterSerializer(new GuidSerializer(BsonType.String));BsonSerializer.RegisterSerializer(new DateTimeOffsetSerializer(BsonType.String));services.AddSingleton(serviceProvider =>{var configuration = serviceProvider.GetService<IConfiguration>();var serviceSettings = configuration.GetSection(nameof(ServiceSettings)).Get<ServiceSettings>();var mongoDbSettings = configuration.GetSection(nameof(MongoDbSettings)).Get<MongoDbSettings>();var mongoClient = new MongoClient(mongoDbSettings.ConenctionString);return mongoClient.GetDatabase(serviceSettings.ServiceName);});return services;}private static IServiceCollection AddMongoRepository<T, TID>(IServiceCollection services, string collectionName) where T : IEntity<TID>{services.AddSingleton<IRepository<User, int>>(serviceProvider =>{var db = serviceProvider.GetService<IMongoDatabase>();return new MongoRepository<User, int>(db, "collectionname");});return services;}}
}

 

在business层加了aop,通过proxy的方式

using Castle.DynamicProxy;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;namespace Project.Base.Reflect
{public static class ServiceExtension{private static readonly ProxyGenerator _generator = new ProxyGenerator();public static IServiceCollection AddBusinessServices(this IServiceCollection services){var folder = Path.Combine(Directory.GetCurrentDirectory(), "bus_lib");var dllFiles = Directory.GetFiles(folder, "*.Business.dll");var assemblies = dllFiles.Select(Assembly.LoadFrom).ToArray();var businessTypes = assemblies.SelectMany(a => a.GetTypes().Where(t => t.IsClass&&!t.IsAbstract)).Where(type => type.GetInterfaces().Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IBusiness<>))).ToList();CastleInterceptor castleInterceptor = new CastleInterceptor();foreach (var type in businessTypes){var interfaceType = type.GetInterfaces().First(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IBusiness<>));services.AddTransient(interfaceType, provider =>{var target = ActivatorUtilities.CreateInstance(provider, type);return _generator.CreateInterfaceProxyWithTarget(interfaceType, target, castleInterceptor);});}return services;}}
}

在你需要的每个方法前加上特性就可以了

using Project.Base.Model;
using Project.Base.ProjAttribute;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace ADM001_User.Business
{/// <summary>/// 有需要就实现前后动作/// </summary>public class AddAop: BaseAopAttribute{public override Task After(BusinessAopContext aopContext){return Task.CompletedTask;}public override Task Before(BusinessAopContext aopContext){return Task.CompletedTask;}}
}

 

再控制器层加了个公共的,不管是controller拦截还是公共的部分都可以写到这里

 [Route("api/[controller]/[action]")][ApiController]public class InitController<TModel>:ControllerBase{protected readonly ILogger<InitController<TModel>> _logger;public InitController(ILogger<InitController<TModel>> logger){_logger = logger;}}

 

该框架主打就是一个简陋,像日志,缓存 ,消息中间件都可以提前约定好公共接口,service层接口调用,business层注入需要的实现。按照接口和实现分离的方式该项目还需要调整下目录

地址如下:

liuzhixin405/single-arch (github.com)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/734105.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

OOP第三次Blog

前言: (1) 第七次题集只有一道题目——家居强电电路模拟程序-3,这是第三次迭代,这次迭代主要的点有四个。 ​ 首先本次迭代添加了线路中存在多个串联起来的并联电路。不同于上次的单并联,本次更复杂。 ​ 然后本次还新迭代了一种控制器——互斥开关,互斥…

23201829OO第三次blog作业

OO第三次blog作业 前言 这次blog是本学期最后一次回顾总结,本次blog的范围是“家居强电电路模拟程序”的第三和第四次迭代,分别加入了互斥开关、窗帘的概念、多个串联起来的并联电路、串联电路中包含其他串联电路的情况和串联电路中包含串联电路的情况和二极管。因此这两次PT…

企业做账流程详解

都说会计月底很忙,那究竟都在忙什么呢,一起来看看吧

快速提取视频字幕!适用B站、AI字幕等等。好用

快速提取视频字幕!适用B站、AI字幕等等。好用 以B站为例: 视频教程:快速提取视频字幕!适用B站、AI字幕!非常好用_哔哩哔哩_bilibili 无论是视频的字幕,还是AI字幕,都可以提取。比较简单。 首先好像需要确定 视频的字幕还是AI字幕。如果是视频字幕,下面F12之后,输入的是…

Ymodem协议说明

Ymodem流程介绍1.Ymodem帧格式 Ymodem两种帧格式: 1024数据格式帧:名称 帧头 包号 包号反码 信息块 校验简写 SOH PN XPN DATA CRC字节数 1 1 1 128 2128数据格式帧:名称 帧头 包号 包号反码 信息块 校验简写 STX PN XPN DATA CRC字节数 1 1 1 1024 21.1 帧头 帧头一个有两…

CentOS7.9部署Nginx

简介 本章节主要讲的是在Linux系统CentOS7.9上去完成Nginx Web服务安装部署 步骤 1.配置 Nginx 源 2.yum 安装 Nginx 3.启动 Nginx 4.浏览默认站点 实施 1.配置 Nginx 源// 执行如下命令 rpm -ivh http://nginx.org/packages/centos/7/noarch/RPMS/nginx-release-centos-7-0.el…

CF132E Bits of merry old England

传送门构图好题。郑哥的题解 每一个输出的位置看作先分配变量(赋值或者用前面的),再输出。 给每个位置拆三个点 \(v_{i,1/2/3}\)。\(v_{i,1}\) 的流入表示这个位置的 "空变量" 数量(包括有值但是我们选择让它不重复使用的变量);\(v_{i,2}\) 有流入表示分配好了…

opp7~8题目集的反思与总结

opp7~8题目集的反思与总结 1、前言: 这两个题目集都是对于电路题目的迭代,都是对于电路题目的应用,对于电路的题目集,在上次的opp的题目集中就已经简述过了,没有看过的可以点击链接查看(opp第二次blog) 在这个里面就是之前的opp的题目集,讲述了大致的内容,这个是基础,…

CentOS7.9部署.NET Core 8.0

简介 本章节主要讲的是在Linux系统CentOS7.9上去完成.NET Core 8.0软件的安装,确定Linux的版本是x64还是arm64的,然后到.NET Core的官网下载8.0的SDK,并进行安装 步骤 1.查看系统版本 2.打开.NET Core 8.0下载网址 3.下载与Linux系统对应版本的.NET Core SDK 4.上传.NET Cor…

使用世界变换的逆转置矩阵对法线进行变换

最近在做法向量变换的时候,踩了两个坑,记录一下相关的知识点法向量变换细节记录 最近在做法向量变换的时候,踩了两个坑,记录一下相关的知识点 法向量做变换,最后一位是补0 我们知道,顶点在做变换的时候最后一位是 1.0,法线最后一位是补0.0 vec3 normCurrent = (getMatri…

JS逆向——动态页面逆向

JS逆向——动态页面逆向 一. 步骤关键点分析 逆向分析信息提取逻辑 模拟执行二. 实现 1. 关键点分析 找到实现kml导出的按键,发现是通过JS函数实现的,需要针对该函数进行JS代码逆向分析,找到实现的函数逻辑。 ​​ 2. 逆向分析信息提取逻辑 定位找到exportKML函数 ​​ 对该…

开源一款基于 Typecho 开发的导航主题

主题介绍 BeaconNav是基于typecho开发的一款导航主题,Beacon是灯塔的意思,希望使用者在知识的海洋里能够如同有灯塔指引一样目标明确,永远不会迷失方向。 演示站点:https://nav.ilaozhu.com主题特点响应式设计,适配手机、平板、电脑等设备; 支持自定义 LOGO、背景图片; …

一款利用人工智能将自然语言查询转换为 SQL 代码的互译工具 - SQL Translator

前言 对于后端程序员来说,编写SQL代码是日常工作中不可或缺的一部分。然而,随着数据复杂性的增加,如何高效、准确地编写SQL查询成为了新的挑战。幸运的是,SQL Translator的出现为后端程序员提供了一个强大的工具,将自然语言查询转换为精确的SQL代码,极大地提高了工作效率…

LINUX查看文件总行数和指定行,wc命令的使用

LINUX查看文件总行数和指定行明训关注IP属地: 浙江 0.2972019.01.26 19:20:22字数 92阅读 9,764问题说明 在Linux服务器运维或操作过程中时常需要统计文件行数和查看指定行 相关命令 查看文件指定行描述命令查看文件的前5行 head -5 test.log查看文件的后2行 tail -2 test.log …

正义使者-隐藏关

最正义的一集\(\Huge{放假!!!}\)

Openvx Tiovx技术杂谈

Openvx & Tiovx技术杂谈 Openvx & Tiovx (六) Host & Target https://zhuanlan.zhihu.com/p/474701695 https://dev.ti.com/tirex/explore/node?node=ANd.gAKGXC97FboluTIMhw Openvx 保留了代码移植到多核平台的可能性。可能是因为多核平台的种类繁多,Openvx 在定…

7~8次题目集总结Blog

一、前言 关于7,8两次家具强电电路模拟程序,我认为是比较困难且综合的程序题,我们需要处理复杂的电路结构,如包含多个并联电路的串联电路,以及并联电路之间的包含关系还要精确地处理输入和输出格式,确保所有的计算和输出都符合题目要求,这些设计程序整体的方面都属于较为…

CentOS7.9部署.NET Core 6.0

简介 本章节主要讲的是在Linux系统CentOS7.9上去完成.NET Core 6.0软件的安装,确定Linux的版本是x64还是arm64的,然后到.NET Core的官网下载6.0的SDK,并进行安装 步骤 1.查看系统版本 2.打开.NET Core 6.0下载网址 3.下载与Linux系统对应版本的.NET Core SDK 4.上传.NET Cor…

全网最适合入门的面向对象编程教程:05 类和对象的Python实现-PyCharm代码标签(一个帮你提升coding效率的小技巧)

本文介绍了PyCharm IDE中代码标签的定义、类型和使用方法。摘要: 本文介绍了PyCharm IDE中代码标签的定义、类型和使用方法。 往期推荐: 学嵌入式的你,还不会面向对象??! 全网最适合入门的面向对象编程教程:00 面向对象设计方法导论 全网最适合入门的面向对象编程教程:…