Adnc 源码解析
先看 Adnc.Demo.Usr.Api 解决方案
var startAssembly = System.Reflection.Assembly.GetExecutingAssembly();
var startAssemblyName = startAssembly.GetName().Name ?? string.Empty;
var lastName = startAssemblyName.Split('.').Last();
var migrationsAssemblyName = startAssemblyName.Replace($".{lastName}", ".Repository");
var serviceInfo = ServiceInfo.CreateInstance(startAssembly, migrationsAssemblyName);
上面代码 是构建 Adnc.Demo.Usr.Api 服务信息
namespace Adnc.Shared.WebApi;
public class ServiceInfo : IServiceInfo
{
private static ServiceInfo? _instance = null;
private static readonly object _lockObj = new();
public string Id { get; private set; } = string.Empty;
public string ServiceName { get; private set; } = string.Empty;
public string CorsPolicy { get; set; } = string.Empty;
public string ShortName { get; private set; } = string.Empty;
public string RelativeRootPath { get; private set; } = string.Empty;
public string Version { get; private set; } = string.Empty;
public string Description { get; private set; } = string.Empty;
public Assembly StartAssembly { get; private set; } = default!;
public string MigrationsAssemblyName { get; private set; } = string.Empty;
private ServiceInfo()
{
}
public static ServiceInfo CreateInstance(Assembly startAssembly, string? migrationsAssemblyName = null)
{
if (_instance is not null)
return _instance;
lock (_lockObj)
{
if (_instance is not null)
return _instance;
if (startAssembly is null)
startAssembly = Assembly.GetEntryAssembly() ?? throw new NullReferenceException(nameof(startAssembly));
var attribute = startAssembly.GetCustomAttribute<AssemblyDescriptionAttribute>();
var description = attribute is null ? string.Empty : attribute.Description;
var version = startAssembly.GetName().Version ?? throw new NullReferenceException("startAssembly.GetName().Version");
var startAssemblyName = startAssembly.GetName().Name ?? string.Empty;
var serviceName = startAssemblyName.Replace(".", "-").ToLower();
var ticks = DateTime.Now.GetTotalMilliseconds().ToLong();
var ticksHex = Convert.ToString(ticks, 16);
var envName = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT")?.ToLower();
var serviceId = envName switch
{
"development" => $"{serviceName}-dev-{ticksHex}",
"test" => $"{serviceName}-test-{ticksHex}",
"staging" => $"{serviceName}-stag-{ticksHex}",
"production" => $"{serviceName}-{ticksHex}",
_ => throw new NullReferenceException("ASPNETCORE_ENVIRONMENT")
};
var names = startAssemblyName.Split(".");
migrationsAssemblyName ??= startAssemblyName.Replace($".{names.Last()}", ".Migrations");
_instance = new ServiceInfo
{
Id = serviceId,
ServiceName = serviceName,
ShortName = $"{names[^2]}-{names[^1]}".ToLower(),
RelativeRootPath = $"{names[^2]}/{names[^1]}".ToLower(),
CorsPolicy = "default",
StartAssembly = startAssembly,
Description = description,
Version = $"{version.Major}.{version.Minor}.{version.Build}.{version.Revision}",
MigrationsAssemblyName = migrationsAssemblyName
};
}
return _instance;
}
public static ServiceInfo GetInstance()
{
if (_instance is null)
throw new NullReferenceException(nameof(ServiceInfo));
return _instance;
}
}
上面使用得是单例 模式
注意 这里的 代码 public Assembly StartAssembly { get; private set; } = default!;
StartAssembly 后面需要 用它反射
接下来 这行代码
var app = WebApplication.CreateBuilder(args).ConfigureAdncDefault(serviceInfo).Build();
调用 ConfigureAdncDefault(serviceInfo)
这是一个扩展方法 在 Adnc.Shared.WebApi 解决方案中
public static WebApplicationBuilder ConfigureAdncDefault(this WebApplicationBuilder builder, IServiceInfo serviceInfo)
{
builder.Services.AddAdnc(serviceInfo);
}
调用了 AddAdnc(serviceInfo) 扩展方法
public static class ServiceCollectionExtension
{
/// <summary>
/// 统一注册Adnc.WebApi通用服务
/// </summary>
/// <param name="services"></param>
/// <param name="startupAssembly"></param>
/// <returns></returns>
/// <exception cref="ArgumentNullException"></exception>
/// <exception cref="NullReferenceException"></exception>
public static IServiceCollection AddAdnc(this IServiceCollection services, IServiceInfo serviceInfo)
{
if (serviceInfo?.StartAssembly is null)
throw new ArgumentNullException(nameof(serviceInfo));
var webApiRegistarType = serviceInfo.StartAssembly.ExportedTypes.FirstOrDefault(m => m.IsAssignableTo(typeof(IDependencyRegistrar)) && m.IsAssignableTo(typeof(AbstractWebApiDependencyRegistrar)) && m.IsNotAbstractClass(true));
if (webApiRegistarType is null)
throw new NullReferenceException(nameof(IDependencyRegistrar));
if (Activator.CreateInstance(webApiRegistarType, services) is not IDependencyRegistrar webapiRegistar)
throw new NullReferenceException(nameof(webapiRegistar));
webapiRegistar.AddAdnc();
return services;
}
}
上面 红色代码 serviceInfo.StartAssembly 通过他反射
创建了 子类 Activator.CreateInstance(webApiRegistarType, services) is not IDependencyRegistrar webapiRegistar
子类也就是 Adnc.Demo.Usr.Api 解决方法中得 UsrWebApiDependencyRegistrar
using Adnc.Demo.Usr.Api.Authentication;
using Adnc.Demo.Usr.Api.Authorization;
using Adnc.Shared.WebApi.Registrar;
namespace Adnc.Demo.Usr.Api;
public sealed class UsrWebApiDependencyRegistrar : AbstractWebApiDependencyRegistrar
{
public UsrWebApiDependencyRegistrar(IServiceCollection services)
: base(services)
{
}
public UsrWebApiDependencyRegistrar(IApplicationBuilder app)
: base(app)
{
}
public override void AddAdnc()
{
AddWebApiDefault<BearerAuthenticationLocalProcessor, PermissionLocalHandler>();
AddHealthChecks(true, true, true, false);
Services.AddGrpc();
}
public override void UseAdnc()
{
UseWebApiDefault(endpointRoute: endpoint =>
{
endpoint.MapGrpcService<Grpc.AuthGrpcServer>();
endpoint.MapGrpcService<Grpc.UsrGrpcServer>();
});
}
}
子类 重写了父类得 AddAdnc() 方法 调用了 父类AbstractWebApiDependencyRegistrar 中得 AddWebApiDefault<BearerAuthenticationLocalProcessor, PermissionLocalHandler>(); 方法 也就是
/// <summary>
/// 注册Webapi通用的服务
/// </summary>
/// <typeparam name="TAuthenticationProcessor"><see cref="AbstractAuthenticationProcessor"/></typeparam>
/// <typeparam name="TAuthorizationHandler"><see cref="AbstractPermissionHandler"/></typeparam>
protected virtual void AddWebApiDefault<TAuthenticationProcessor, TAuthorizationHandler>()
where TAuthenticationProcessor : AbstractAuthenticationProcessor
where TAuthorizationHandler : AbstractPermissionHandler
{
Services.AddControllers();
Services
.AddHttpContextAccessor()
.AddMemoryCache();
Configure();
AddControllers();
AddAuthentication<TAuthenticationProcessor>();
AddAuthorization<TAuthorizationHandler>();
AddCors();
var enableSwaggerUI = Configuration.GetValue(NodeConsts.SwaggerUI_Enable, true);
if(enableSwaggerUI)
{
AddSwaggerGen();
AddMiniProfiler();
}
AddApplicationServices();
}
父类中的 方法 调用了 AddApplicationServices(); 方法
这个方法
namespace Adnc.Shared.WebApi.Registrar;
public abstract partial class AbstractWebApiDependencyRegistrar
{
/// <summary>
/// 注册Application层服务
/// </summary>
protected virtual void AddApplicationServices()
{
var appAssembly = ServiceInfo.GetApplicationAssembly();
if (appAssembly is not null)
{
var applicationRegistrarType = appAssembly.ExportedTypes.FirstOrDefault(m => m.IsAssignableTo(typeof(IDependencyRegistrar)) && !m.IsAssignableTo(typeof(AbstractWebApiDependencyRegistrar)) && m.IsNotAbstractClass(true));
if (applicationRegistrarType is not null)
{
var applicationRegistrar = Activator.CreateInstance(applicationRegistrarType, Services) as IDependencyRegistrar;
applicationRegistrar?.AddAdnc();
}
}
}
}
这段代码 ServiceInfo.GetApplicationAssembly(); 获取了 Adnc.Shared.Application 程序集
using Adnc.Infra.Consul.Configuration;
namespace Adnc.Shared.WebApi
{
public static class ServiceInfoExtension
{
private static readonly object lockObj = new();
private static Assembly? appAssembly;
/// <summary>
/// 获取WebApiAssembly程序集
/// </summary>
/// <returns></returns>
public static Assembly GetWebApiAssembly(this IServiceInfo serviceInfo) => serviceInfo.StartAssembly;
/// <summary>
/// 获取Application程序集
/// </summary>
/// <returns></returns>
public static Assembly GetApplicationAssembly(this IServiceInfo serviceInfo)
{
if (appAssembly is null)
{
lock (lockObj)
{
if (appAssembly is null)
{
Assembly startAssembly = serviceInfo.StartAssembly ?? throw new NullReferenceException(nameof(serviceInfo.StartAssembly));
string startAssemblyFullName = startAssembly.FullName ?? throw new NullReferenceException(nameof(startAssembly.FullName));
string startAssemblyName = startAssembly.GetName().Name ?? string.Empty;
string lastName = startAssemblyName.Split(".").Last();
string appAssemblyFullName = startAssemblyFullName.Replace($".{lastName}", ".Application");
appAssembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(x => x.FullName == appAssemblyFullName);
if (appAssembly is null)
{
appAssembly = startAssembly;
//appAssembly = Assembly.Load(appAssemblyName);
//var appAssemblyPath = serviceInfo.AssemblyLocation.Replace(".WebApi.dll", ".Application.dll");
///appAssembly = Assembly.LoadFrom(appAssemblyPath);
}
}
}
}
return appAssembly;
}
/// <summary>
/// 获取导航首页内容
/// </summary>
/// <param name="serviceInfo"></param>
/// <param name="app"></param>
/// <returns></returns>
public static string GetDefaultPageContent(this IServiceInfo serviceInfo, IServiceProvider serviceProvider)
{
var swaggerUrl = $"/{serviceInfo.RelativeRootPath}/index.html";
var consulOptions = serviceProvider.GetRequiredService<IOptions<ConsulOptions>>();
var healthCheckUrl = consulOptions?.Value?.HealthCheckUrl ?? $"/{serviceInfo.RelativeRootPath}/health-24b01005-a76a-4b3b-8fb1-5e0f2e9564fb";
var envName = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");
var content = $"<div align='center'><a href='https://github.com/alphayu/adnc' target='_blank'><img src='https://aspdotnetcore.net/wp-content/uploads/2023/04/adnc-topics.png'/></a><br>" +
$"ASPNETCORE_ENVIRONMENT = {envName} <br> " +
$"Version = {serviceInfo.Version} <br> " +
$"ServiceName = {serviceInfo.ServiceName} <br> " +
$"ShortName = {serviceInfo.ShortName} <br> " +
$"RelativeRootPath = {serviceInfo.RelativeRootPath} <br> " +
$"<br><a href='{swaggerUrl}'>swagger UI</a> | <a href='{healthCheckUrl}'>healthy checking</a><br>" +
$"<br>{DateTime.Now}</div>";
return content;
}
}
}
这段代码
string appAssemblyFullName = startAssemblyFullName.Replace($".{lastName}", ".Application");
appAssembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(x => x.FullName == appAssemblyFullName);
是获取 Adnc.Shared.Application 程序集
这个方法通过反射 构造了 DependencyRegistrar 对象 强调 一下 这是是对 Adnc.Demo.Usr.Application 中的 DependencyRegistrar 构造 对象
namespace Adnc.Demo.Usr.Application;
public sealed class DependencyRegistrar : AbstractApplicationDependencyRegistrar
{
public override Assembly ApplicationLayerAssembly => Assembly.GetExecutingAssembly();
public override Assembly ContractsLayerAssembly => typeof(IUserAppService).Assembly;
public override Assembly RepositoryOrDomainLayerAssembly => typeof(EntityInfo).Assembly;
public DependencyRegistrar(IServiceCollection services) : base(services)
{
}
public override void AddAdnc() => AddApplicaitonDefault();
}
Adnc.Demo.Usr.Application 解决方案 DependencyRegistrar 类 重写了 父类方法
public override void AddAdnc() => AddApplicaitonDefault();
该方法 AddApplicaitonDefault() 在 Adnc.Shared.Application 解决方案
/// <summary>
/// 注册adnc.application通用服务
/// </summary>
protected virtual void AddApplicaitonDefault()
{
Services
.AddValidatorsFromAssembly(ContractsLayerAssembly, ServiceLifetime.Scoped)
.AddAdncInfraAutoMapper(ApplicationLayerAssembly)
.AddAdncInfraYitterIdGenerater(RedisSection)
.AddAdncInfraConsul(ConsulSection)
.AddAdncInfraDapper();
AddApplicationSharedServices();
AddAppliactionSerivcesWithInterceptors();
AddApplicaitonHostedServices();
AddEfCoreContextWithRepositories();
AddMongoContextWithRepositries();
AddRedisCaching();
AddBloomFilters();
}
这里 说明 AddEfCoreContextWithRepositories(); 这个方法 是注册 EF
using Adnc.Infra.Repository.EfCore.MySql.Configurations;
using Adnc.Infra.Repository.Mongo;
using Adnc.Infra.Repository.Mongo.Configuration;
using Adnc.Infra.Repository.Mongo.Extensions;
using Microsoft.EntityFrameworkCore;
namespace Adnc.Shared.Application.Registrar;
public abstract partial class AbstractApplicationDependencyRegistrar
{
/// <summary>
/// 注册EFCoreContext与仓储
/// </summary>
protected virtual void AddEfCoreContextWithRepositories()
{
var serviceType = typeof(IEntityInfo);
var implType = RepositoryOrDomainLayerAssembly.ExportedTypes.FirstOrDefault(type => type.IsAssignableTo(serviceType) && type.IsNotAbstractClass(true));
if (implType is null)
throw new NotImplementedException(nameof(IEntityInfo));
else
Services.AddScoped(serviceType, implType);
AddEfCoreContext();
}
/// <summary>
/// 注册EFCoreContext
/// </summary>
protected virtual void AddEfCoreContext()
{
var serviceInfo = Services.GetServiceInfo();
var mysqlConfig = MysqlSection.Get<MysqlOptions>();
var serverVersion = new MariaDbServerVersion(new Version(10, 5, 4));
Services.AddAdncInfraEfCoreMySql(options =>
{
options.UseLowerCaseNamingConvention();
options.UseMySql(mysqlConfig.ConnectionString, serverVersion, optionsBuilder =>
{
optionsBuilder.MinBatchSize(4)
.MigrationsAssembly(serviceInfo.MigrationsAssemblyName)
.UseQuerySplittingBehavior(QuerySplittingBehavior.SplitQuery);
});
//if (this.IsDevelopment())
//{
// //options.AddInterceptors(new DefaultDbCommandInterceptor())
// options.LogTo(Console.WriteLine, LogLevel.Information)
// .EnableSensitiveDataLogging()
// .EnableDetailedErrors();
//}
//替换默认查询sql生成器,如果通过mycat中间件实现读写分离需要替换默认SQL工厂。
//options.ReplaceService<IQuerySqlGeneratorFactory, AdncMySqlQuerySqlGeneratorFactory>();
});
}
/// <summary>
/// 注册MongoContext与仓储
/// </summary>
protected virtual void AddMongoContextWithRepositries(Action<IServiceCollection>? action = null)
{
action?.Invoke(Services);
var mongoConfig = MongoDbSection.Get<MongoOptions>();
Services.AddAdncInfraMongo<MongoContext>(options =>
{
options.ConnectionString = mongoConfig.ConnectionString;
options.PluralizeCollectionNames = mongoConfig.PluralizeCollectionNames;
options.CollectionNamingConvention = (NamingConvention)mongoConfig.CollectionNamingConvention;
});
}
}
其中 Services.AddAdncInfraEfCoreMySql扩展方法 调用得 是 Adnc.Infra.Repository.EfCore.MySql 解决方案 注册了 仓储 这里
using Adnc.Infra.Repository.EfCore.MySql;
using Adnc.Infra.Repository.EfCore.MySql.Transaction;
namespace Microsoft.Extensions.DependencyInjection;
public static class ServiceCollectionExtension
{
public static IServiceCollection AddAdncInfraEfCoreMySql(this IServiceCollection services, Action<DbContextOptionsBuilder> optionsBuilder)
{
if (services.HasRegistered(nameof(AddAdncInfraEfCoreMySql)))
return services;
services.TryAddScoped<IUnitOfWork, MySqlUnitOfWork<MySqlDbContext>>();
services.TryAddScoped(typeof(IEfRepository<>), typeof(EfRepository<>));
services.TryAddScoped(typeof(IEfBasicRepository<>), typeof(EfBasicRepository<>));
services.AddDbContext<DbContext, MySqlDbContext>(optionsBuilder);
return services;
}
}
这篇 分析了
Adnc.Demo.Usr.Api -----(ConfigureAdncDefault(serviceInfo))---------> Adnc.Shared.WebApi -------- AddAdnc 反射获取子类 子类重写 AddAdnc 调用了父类
AddWebApiDefault.AddApplicationServices 反射出 Adnc.Shared.Application------->
Adnc.Shared.Application ----- (AbstractApplicationDependencyRegistrar.AddApplicaitonDefault.AddEfCoreContextWithRepositories) ----->
Adnc.Infra.Repository.EfCore.MySql
就是 依次 往上调用
浙公网安备 33010602011771号