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


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

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

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

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

    liuzhixin405/netcore-micro (github.com)

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

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

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

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

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

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

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

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

    复制代码
     public interface IRepository where TEntity : IEntity
     {
         Task AddAsync(TEntity entity);
         Task UpdateAsync(TEntity entity);
    
         Task DeleteAsync(Expressionbool>> filter);
         Task DeleteAsync(TID id);
         // 通用分页查询
         Task> GetPagedAsync(PagingParameters pagingParameters);
    
         // 其他常用操作
         Task> FindAsync(Expressionbool>> filter);
    
     }
    复制代码

    服务层也是同样的方法

    复制代码
     [Intercept("business-service log")]
     public interface IService
     {
        Task AddAsync(IRequestDto requestDto);
        Task UpdateAsync(IRequestDto requestDto);
        Task DeleteAsyncc(IRequestDto requestDto);
        Task GetPagedAsyncc(IRequestDto requestDto) ;
        Task 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();
                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(options =>
           options.UseInMemoryDatabase("InMemoryDb"));
    
                services.AddScopedint>, GenericRepositoryint, UserDbContext>>();
                services.AddTransient();
    
                AddMongo(services);
                AddMongoRepositoryint>(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();
                    var serviceSettings = configuration.GetSection(nameof(ServiceSettings)).Get();
                    var mongoDbSettings = configuration.GetSection(nameof(MongoDbSettings)).Get();
                    var mongoClient = new MongoClient(mongoDbSettings.ConenctionString);
                    return mongoClient.GetDatabase(serviceSettings.ServiceName);
                });
                return services;
            }
            private static IServiceCollection AddMongoRepository(IServiceCollection services, string collectionName) where T : IEntity
            {
                services.AddSingletonint>>(serviceProvider =>
                {
                    var db = serviceProvider.GetService();
                    return new MongoRepositoryint>(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
    {
        /// 
        /// 有需要就实现前后动作
        /// 
        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:ControllerBase
     {
         protected readonly ILogger> _logger;
          public InitController(ILogger> logger)
         {
             _logger = logger;
    
         }
       
     }
    复制代码

     

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

    地址如下:

    liuzhixin405/single-arch (github.com)

  • 相关阅读:
    【数据库基础】数据库介绍和三大范式
    HTML网页设计制作——初音动漫(6页) dreamweaver作业静态HTML网页设计模板
    Leetcode-每日一题1106. 解析布尔表达式(DFS模拟栈)
    ubuntu初始化安装
    单片机开发——宠物自动饮水器方案
    2021最新中高级Java面试题目,Java面试题汇总
    Docker的奇幻漂流
    解决库存超卖问题
    视频怎么消除人声?一款视频去人声软件,轻松去除视频人声
    Linux 各文件夹说明 linux文件夹说明
  • 原文地址:https://www.cnblogs.com/morec/p/18276172