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

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

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

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

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

liuzhixin405/netcore-micro (github.com)

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

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

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

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

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

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

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

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

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

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

 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)

发表评论

评论已关闭。

相关文章