经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » ASP.net » 查看文章
无业游民写的最后一个.net有关项目框架
来源:cnblogs  作者:星仔007  时间:2024/7/1 11:28:17  对本文有异议

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

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

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

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

liuzhixin405/netcore-micro (github.com)

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

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

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

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

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

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

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

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

  1. public interface IRepository<TEntity,TID> where TEntity : IEntity<TID>
  2. {
  3. Task<ApiResult> AddAsync(TEntity entity);
  4. Task<ApiResult> UpdateAsync(TEntity entity);
  5. Task<ApiResult> DeleteAsync(Expression<Func<TEntity, bool>> filter);
  6. Task<ApiResult> DeleteAsync(TID id);
  7. // 通用分页查询
  8. Task<PagedResult<TEntity>> GetPagedAsync(PagingParameters<TEntity> pagingParameters);
  9. // 其他常用操作
  10. Task<IEnumerable<TEntity>> FindAsync(Expression<Func<TEntity, bool>> filter);
  11. }

服务层也是同样的方法

  1. [Intercept("business-service log")]
  2. public interface IService
  3. {
  4. Task<ApiResult> AddAsync(IRequestDto requestDto);
  5. Task<ApiResult> UpdateAsync(IRequestDto requestDto);
  6. Task<ApiResult> DeleteAsyncc(IRequestDto requestDto);
  7. Task<ApiResult> GetPagedAsyncc(IRequestDto requestDto) ;
  8. Task<ApiResult> FindAsyncc(IRequestDto requestDto);
  9. }

 

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

  1. public interface IModule
  2. {
  3. void ConfigureService(IServiceCollection services, IConfiguration configuration = null);
  4. void Configure(IApplicationBuilder app, IWebHostEnvironment env = null);
  5. }
  6. public abstract class ModuleBase : IModule
  7. {
  8. public virtual void ConfigureService(IServiceCollection services, IConfiguration configuration = null)
  9. {
  10. }
  11. public virtual void Configure(IApplicationBuilder app, IWebHostEnvironment env = null)
  12. {
  13. }
  14. }

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

  1. using Microsoft.AspNetCore.Mvc.ApplicationParts;
  2. using Project.Base.Reflect;
  3. using System.Reflection;
  4. using Project.Base.ProjExtension;
  5. using Project.Base.Common;
  6. using Project.Base.DependencyInjection;
  7. using Project.Base.Module;
  8. namespace Project.Host
  9. {
  10. public class Program
  11. {
  12. public static void Main(string[] args)
  13. {
  14. var builder = WebApplication.CreateBuilder(args);
  15. builder.Configuration.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true);
  16. builder.Configuration.AddJsonFile($"appsettings.{builder.Environment.EnvironmentName}.json", optional: true, reloadOnChange: true);
  17. builder.Configuration.AddJsonFile("appsettings.Modules.json", optional: false, reloadOnChange: true);
  18. //IModule注入 ,然后扫描调用ConfigureService,Business注入需要的服务入口
  19. builder.Services.InitModule(builder.Configuration);
  20. var sp = builder.Services.BuildServiceProvider();
  21. var moduleInitializers = sp.GetServices<IModule>();
  22. foreach (var moduleInitializer in moduleInitializers)
  23. {
  24. moduleInitializer.ConfigureService(builder.Services, builder.Configuration);
  25. }
  26. // Add services to the container.
  27. var assemblys = GolbalConfiguration.Modules.Select(x => x.Assembly).ToList();
  28. var mvcBuilder=builder.Services.AddControllers().ConfigureApplicationPartManager(apm => {
  29. var folder = Path.Combine(Directory.GetCurrentDirectory(), "bus_lib");
  30. var serviceList = (builder.Configuration.GetSection("ServiceList").Get<string[]>()) ?? new string[] { "ADM" };//默认加载基础服务
  31. string[] serviceFiles = Directory.GetFiles(folder, "*.Api.dll").Where(x =>
  32. serviceList.Any(y => x.Contains(y))
  33. ).ToArray();
  34. foreach (var file in serviceFiles)
  35. {
  36. if (File.Exists(file))
  37. {
  38. var assembly = Assembly.LoadFrom(file);
  39. var controllerAssemblyPart = new AssemblyPart(assembly);
  40. apm.ApplicationParts.Add(controllerAssemblyPart);
  41. }
  42. }
  43. });
  44. foreach (var assembly in assemblys)
  45. {
  46. // 扫描并注册其他程序集中的控制器
  47. mvcBuilder.AddApplicationPart(assembly);
  48. // 扫描并注册其他程序集中的服务 针对特性注入
  49. builder.Services.ReisterServiceFromAssembly(assembly);
  50. }
  51. // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
  52. builder.Services.AddEndpointsApiExplorer();
  53. builder.Services.AddSwaggerGen();
  54. builder.Services.AddBusinessServices();
  55. var app = builder.Build();
  56. ServiceLocator.Instance = app.Services;
  57. //imodule 的Configure调用,business可以实现中间件等操作
  58. foreach (var moduleInitializer in moduleInitializers)
  59. {
  60. moduleInitializer.Configure(app, app.Environment);
  61. }
  62. // Configure the HTTP request pipeline.
  63. if (app.Environment.IsDevelopment())
  64. {
  65. app.UseSwagger();
  66. app.UseSwaggerUI();
  67. }
  68. app.UseHttpsRedirection();
  69. app.UseAuthorization();
  70. app.MapControllers();
  71. app.Run();
  72. }
  73. }
  74. }

 

业务需求注入代码如下:

  1. using ADM001_User.Model;
  2. using Microsoft.AspNetCore.Builder;
  3. using Microsoft.AspNetCore.Hosting;
  4. using Microsoft.EntityFrameworkCore;
  5. using Microsoft.Extensions.Configuration;
  6. using Microsoft.Extensions.DependencyInjection;
  7. using MongoDB.Bson.Serialization.Serializers;
  8. using MongoDB.Bson.Serialization;
  9. using MongoDB.Bson;
  10. using MongoDB.Driver;
  11. using Project.Base.IRepository;
  12. using Project.Base.Module;
  13. using Project.Base.Reflect;
  14. using Project.Base.Repository;
  15. using Project.Base.Services;
  16. using System;
  17. using System.Collections;
  18. using System.Collections.Generic;
  19. using System.Linq;
  20. using System.Text;
  21. using System.Threading.Tasks;
  22. using ADM001_User.Business.Settings;
  23. using Project.Base.Model;
  24. namespace ADM001_User.Business
  25. {
  26. public class UserModule : ModuleBase
  27. {
  28. public override void ConfigureService(IServiceCollection services, IConfiguration configuration = null)
  29. {
  30. services.AddDbContext<UserDbContext>(options =>
  31. options.UseInMemoryDatabase("InMemoryDb"));
  32. services.AddScoped<IRepository<User, int>, GenericRepository<User, int, UserDbContext>>();
  33. services.AddTransient<IService, UserService>();
  34. AddMongo(services);
  35. AddMongoRepository<User, int>(services, "users");
  36. }
  37. private static IServiceCollection AddMongo(IServiceCollection services)
  38. {
  39. BsonSerializer.RegisterSerializer(new GuidSerializer(BsonType.String));
  40. BsonSerializer.RegisterSerializer(new DateTimeOffsetSerializer(BsonType.String));
  41. services.AddSingleton(serviceProvider =>
  42. {
  43. var configuration = serviceProvider.GetService<IConfiguration>();
  44. var serviceSettings = configuration.GetSection(nameof(ServiceSettings)).Get<ServiceSettings>();
  45. var mongoDbSettings = configuration.GetSection(nameof(MongoDbSettings)).Get<MongoDbSettings>();
  46. var mongoClient = new MongoClient(mongoDbSettings.ConenctionString);
  47. return mongoClient.GetDatabase(serviceSettings.ServiceName);
  48. });
  49. return services;
  50. }
  51. private static IServiceCollection AddMongoRepository<T, TID>(IServiceCollection services, string collectionName) where T : IEntity<TID>
  52. {
  53. services.AddSingleton<IRepository<User, int>>(serviceProvider =>
  54. {
  55. var db = serviceProvider.GetService<IMongoDatabase>();
  56. return new MongoRepository<User, int>(db, "collectionname");
  57. });
  58. return services;
  59. }
  60. }
  61. }

 

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

  1. using Castle.DynamicProxy;
  2. using Microsoft.Extensions.DependencyInjection;
  3. using System;
  4. using System.Collections.Generic;
  5. using System.Linq;
  6. using System.Reflection;
  7. using System.Runtime.CompilerServices;
  8. using System.Text;
  9. using System.Threading.Tasks;
  10. namespace Project.Base.Reflect
  11. {
  12. public static class ServiceExtension
  13. {
  14. private static readonly ProxyGenerator _generator = new ProxyGenerator();
  15. public static IServiceCollection AddBusinessServices(this IServiceCollection services)
  16. {
  17. var folder = Path.Combine(Directory.GetCurrentDirectory(), "bus_lib");
  18. var dllFiles = Directory.GetFiles(folder, "*.Business.dll");
  19. var assemblies = dllFiles.Select(Assembly.LoadFrom).ToArray();
  20. 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();
  21. CastleInterceptor castleInterceptor = new CastleInterceptor();
  22. foreach (var type in businessTypes)
  23. {
  24. var interfaceType = type.GetInterfaces().First(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IBusiness<>));
  25. services.AddTransient(interfaceType, provider =>
  26. {
  27. var target = ActivatorUtilities.CreateInstance(provider, type);
  28. return _generator.CreateInterfaceProxyWithTarget(interfaceType, target, castleInterceptor);
  29. });
  30. }
  31. return services;
  32. }
  33. }
  34. }

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

  1. using Project.Base.Model;
  2. using Project.Base.ProjAttribute;
  3. using System;
  4. using System.Collections.Generic;
  5. using System.Linq;
  6. using System.Text;
  7. using System.Threading.Tasks;
  8. namespace ADM001_User.Business
  9. {
  10. /// <summary>
  11. /// 有需要就实现前后动作
  12. /// </summary>
  13. public class AddAop: BaseAopAttribute
  14. {
  15. public override Task After(BusinessAopContext aopContext)
  16. {
  17. return Task.CompletedTask;
  18. }
  19. public override Task Before(BusinessAopContext aopContext)
  20. {
  21. return Task.CompletedTask;
  22. }
  23. }
  24. }

 

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

  1. [Route("api/[controller]/[action]")]
  2. [ApiController]
  3. public class InitController<TModel>:ControllerBase
  4. {
  5. protected readonly ILogger<InitController<TModel>> _logger;
  6. public InitController(ILogger<InitController<TModel>> logger)
  7. {
  8. _logger = logger;
  9. }
  10. }

 

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

地址如下:

liuzhixin405/single-arch (github.com)

原文链接:https://www.cnblogs.com/morec/p/18276172

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号