无业游民写的最后一个.net有关项目框架
理想很丰满,现实往往很残酷。
一种按照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)
无业游民写的最后一个.net有关项目框架的更多相关文章
- Flutter学习三之搭建一个简单的项目框架
上一篇文章介绍了Dart的语法的基本使用,从这篇文章开始,开发一个基于玩Android网站的app.使用的他们开放的api来获取网站数据. 根据网站的结构,我们app最外层框架需要添加一个底部导航栏, ...
- 写自己的一个pdo数据库操作框架
http://stackoverflow.com/questions/20669850/pdo-database-abstraction-layer-with-multiple-queries-in- ...
- asp.net mvc 简单项目框架的搭建过程(一)对Bll层和Dal层进行充分解耦
学习asp.net 已经有近三个月的时间了,在asp.net mvc上花的时间最多,但个人真是有些菜,不得不说,asp.net mvc的水真的还是蛮深的.目前在公司实习,也见过公司几个项目的代码了.对 ...
- 自己动手写一个简单的MVC框架(第二版)
一.ASP.NET MVC核心机制回顾 在ASP.NET MVC中,最核心的当属“路由系统”,而路由系统的核心则源于一个强大的System.Web.Routing.dll组件. 在这个System.W ...
- 如何参与一个 GitHub 开源项目?
最近一年开源项目特别的热,很多技术大会或论坛都以开源项目作为主题进行探讨,可见这是一种趋势.而Github作为开源项目的著名托管地,可谓无 人不知,越来越多的个人和公司纷纷加入到Github的大家族里 ...
- 如何用Maven创建一个普通Java项目
一下内容包括:用Maven创建一个普通Java项目,并把该项目转成IDEA项目,导入到IDEA,最后把这个项目打包成一个jar文件. 有时候运行mvn命令失败,重复运行几次就OK了,无解(可能因为网络 ...
- 一个word合并项目的分布式架构设计
一个word合并项目的分布式架构设计 项目背景与问题起源 我们要给一个客户做word生成报告以及报告合并的工作,要合并的报告非常多,而且每个报告也比较大,一个多的报告大概有200页以上.我们用c#操作 ...
- 如何参与一个GitHub开源项目
Github作为开源项目的著名托管地,可谓无人不知,越来越多的个人和公司纷纷加入到Github的大家族里来,为开源尽一份绵薄之力.对于个人来讲,你把自己的项目托管到Github上并不表示你参与了Git ...
- 一个完整的项目中,需要的基本gulp
一个完整的项目需要使用gulp的多种功能,包括—— (1)加载各种需要的插件 var concat=require('gulp'); var clean=require(''gulp); 等等.需要的 ...
- SuperSocket基础(二)-----一个完成SocketServer项目
SuperSocket基础(二)-----一个完成SocketServer项目 由于时间关系未能及时更新,关于SuperSocket,对于初学者而言,一个SuperSock的Server真的不好写.官 ...
随机推荐
- Nginx 简单应用(Windows os)
实际问题如下: 我的电脑上有vm虚拟机,我有两个网络,一个叫137,一个叫102 ,我现在vm的网络是102的网络(137不允许被vm使用),但是别人都是137的网络,如何让137的局域网访问我的vm ...
- C#中的对象深拷贝和浅拷贝
目录 C#中的对象深拷贝和浅拷贝 概述 1. 浅拷贝 2. 深拷贝 总结 引用 C#中的对象深拷贝和浅拷贝 概述 在C#中,对象拷贝是指将一个对象的副本创建到另一个对象中.对象拷贝通常用于数据传输或创 ...
- jeecg-boot中分页接口用自定义sql和实体实现
1.controller实现 @RequestMapping(value = "/list", method = RequestMethod.GET) public Result& ...
- ansible自定义模板部署apache服务
使用Ansible来部署Apache服务是一个很好的选择,因为它可以自动化部署过程,确保所有的服务器上都有相同的配置.以下是一个简单的步骤指南,展示如何使用Ansible来部署Apache服务: 1 ...
- 降本提效 | AIRIOT设备运维管理解决方案
传统运维多是使用在本地化系统,以人工运维和独立系统执行运维工作,重点关注的是设施运行,存在以下几个问题: 1.信息孤岛:本地化系统的接口不同,功能单一独立,各个系统之间的数据无法对接.交互,形 ...
- 【漏洞复现】Apache RocketMQ 代码注入漏洞(CVE-2023-37582)
产品介绍 Apache RocketMQ是美国阿帕奇(Apache)基金会的一款轻量级的数据处理平台和消息传递引擎. 漏洞概述 Apache RocketMQ 存在代码注入漏洞,该漏洞源于当 Name ...
- vue-router单页面应用的多标签页使用问题
正常的思维 做多vue页面应用,我们的第一反应是配置多个入口点,多个vue应用,编译成多个HTML文件,由服务器来决定路由.这是正常的思维. 但谁知道单页面应用也能做到类似的效果呢.单页面不过是服务器 ...
- 启动Django项目的方式
方式一: python manage.py runserver 方式二: # 加上监听地址和端口 python manage.py runserver 0.0.0.0:8080 方式三: 使用 Pyc ...
- C#简易商城收银系统v1.1简单工厂实现(2-2)
C#简易商城收银系统v1.1简单工厂实现(2-2) 当初: C#简易商城收银系统v1.0 现在: 用之前的工厂模式对商城收银系统v1.0进行升级 可以参考之前的 C#简易商城收银系统v1.0 随笔 ...
- itestwork(爱测试) 一站式接口测试&敏捷测试工作站 9.0.1 发布,ui 及Bug fix
(一)itest 简介 itest work (爱测试) 一站式工作站让测试变得简单.敏捷.itest work 包含极简的任务管理,测试管理,缺陷管理,测试环境管理,接口测试,接口Mock 6合1 ...