You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
579 lines
25 KiB
579 lines
25 KiB
using AutoMapper;
|
|
using Hangfire;
|
|
using Hangfire.Dashboard.BasicAuthorization;
|
|
using Hangfire.Redis;
|
|
using log4net;
|
|
using log4net.Repository;
|
|
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
|
using Microsoft.AspNetCore.Builder;
|
|
using Microsoft.AspNetCore.Hosting;
|
|
using Microsoft.AspNetCore.Http;
|
|
using Microsoft.AspNetCore.Mvc;
|
|
using Microsoft.AspNetCore.Mvc.ApiExplorer;
|
|
using Microsoft.AspNetCore.Mvc.Versioning;
|
|
using Microsoft.AspNetCore.Server.Kestrel.Core;
|
|
using Microsoft.Extensions.Caching.Distributed;
|
|
using Microsoft.Extensions.Caching.Memory;
|
|
using Microsoft.Extensions.Caching.StackExchangeRedis;
|
|
using Microsoft.Extensions.Configuration;
|
|
using Microsoft.Extensions.DependencyInjection;
|
|
using Microsoft.Extensions.DependencyInjection.Extensions;
|
|
using Microsoft.Extensions.Hosting;
|
|
using Microsoft.Extensions.Options;
|
|
using Microsoft.IdentityModel.Tokens;
|
|
using Microsoft.OpenApi.Models;
|
|
using Pursue.Extension.MongoDB;
|
|
using Senparc.CO2NET;
|
|
using Senparc.CO2NET.RegisterServices;
|
|
using Senparc.Weixin.MP.Containers;
|
|
using Senparc.Weixin.RegisterServices;
|
|
using Swashbuckle.AspNetCore.Filters;
|
|
using System;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Runtime.Loader;
|
|
using System.Text;
|
|
using System.Text.Encodings.Web;
|
|
using System.Text.Json;
|
|
using System.Text.Unicode;
|
|
using Znyc.Cloudcar.Admin.AspNetCore.Common;
|
|
using Znyc.Cloudcar.Admin.AspNetCore.Mvc;
|
|
using Znyc.Cloudcar.Admin.AspNetCore.Mvc.Filter;
|
|
using Znyc.Cloudcar.Admin.Commons.Cache;
|
|
using Znyc.Cloudcar.Admin.Commons.Core.App;
|
|
using Znyc.Cloudcar.Admin.Commons.Cos;
|
|
using Znyc.Cloudcar.Admin.Commons.DbContextCore;
|
|
using Znyc.Cloudcar.Admin.Commons.Extensions;
|
|
using Znyc.Cloudcar.Admin.Commons.Helpers;
|
|
using Znyc.Cloudcar.Admin.Commons.IDbContext;
|
|
using Znyc.Cloudcar.Admin.Commons.Linq;
|
|
using Znyc.Cloudcar.Admin.Commons.Log;
|
|
using Znyc.Cloudcar.Admin.Commons.Module;
|
|
using Znyc.Cloudcar.Admin.Commons.Options;
|
|
using Znyc.Cloudcar.Admin.Hangfire;
|
|
using Znyc.Cloudcar.Admin.MongoDb;
|
|
using Znyc.Cloudcar.Admin.MongoDb.Core.IRepositorys;
|
|
using Znyc.Cloudcar.Admin.MongoDb.Core.Repositorys;
|
|
using Znyc.Cloudcar.Admin.WebApi.Auth;
|
|
using Znyc.Cloudcar.Admin.WebApi.Config;
|
|
|
|
namespace Znyc.Cloudcar.Admin.WebApi
|
|
{
|
|
/// <summary>
|
|
/// </summary>
|
|
public class Startup
|
|
{
|
|
private IApiVersionDescriptionProvider apiVersionProvider; //api接口版本控制
|
|
private IMvcBuilder mvcBuilder;
|
|
|
|
private string targetPath = string.Empty;
|
|
|
|
/// <summary>
|
|
/// </summary>
|
|
/// <param name="configuration"></param>
|
|
public Startup(IConfiguration configuration, IWebHostEnvironment env)
|
|
{
|
|
Configuration = configuration;
|
|
//初始化log4net
|
|
LoggerRepository = LogManager.CreateRepository("NETCoreRepository");
|
|
Log4NetHelper.SetConfig(LoggerRepository, "log4net.config");
|
|
if (env.IsProduction())
|
|
{
|
|
Log4NetHelper.SetConfig(LoggerRepository, "log4net.Production.config");
|
|
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// </summary>
|
|
public static ILoggerRepository LoggerRepository { get; set; }
|
|
|
|
/// <summary>
|
|
/// </summary>
|
|
public IConfiguration Configuration { get; }
|
|
|
|
/// <summary>
|
|
/// This method gets called by the runtime. Use this method to add services to the container.
|
|
/// </summary>
|
|
/// <param name="services"></param>
|
|
/// <returns></returns>
|
|
public void ConfigureServices(IServiceCollection services)
|
|
{
|
|
services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
|
|
services.AddHttpContextAccessor();
|
|
//如果部署在linux系统上,需要加上下面的配置:
|
|
services.Configure<KestrelServerOptions>(options => options.AllowSynchronousIO = true);
|
|
services.AddOptions();
|
|
services.Configure<IISServerOptions>(options => options.AllowSynchronousIO = true);
|
|
|
|
#region Swagger Api文档
|
|
|
|
// Api多版本版本配置
|
|
services.AddApiVersioning(o =>
|
|
{
|
|
o.ReportApiVersions = true; //是否在请求头中返回受支持的版本信息。
|
|
o.ApiVersionReader =
|
|
new HeaderApiVersionReader("api-version"); ////版本信息放到header ,不写在不配置路由的情况下,版本信息放到response url 中
|
|
o.AssumeDefaultVersionWhenUnspecified = true; //请求没有指明版本的情况下是否使用默认的版本。
|
|
o.DefaultApiVersion = new ApiVersion(1, 0); //默认的版本号。
|
|
}).AddVersionedApiExplorer(option =>
|
|
{
|
|
// 版本名的格式:v+版本号
|
|
option.GroupNameFormat = "'v'V";
|
|
option.AssumeDefaultVersionWhenUnspecified = true;
|
|
});
|
|
//获取webapi版本信息,用于swagger多版本支持
|
|
apiVersionProvider = services.BuildServiceProvider().GetRequiredService<IApiVersionDescriptionProvider>();
|
|
services.AddSwaggerGen(options =>
|
|
{
|
|
foreach (ApiVersionDescription description in apiVersionProvider.ApiVersionDescriptions)
|
|
{
|
|
options.SwaggerDoc(description.GroupName,
|
|
new OpenApiInfo
|
|
{
|
|
Title = $"{Configuration.GetSection("SwaggerDoc:Title").Value}v{description.ApiVersion}",
|
|
Version = description.ApiVersion.ToString(),
|
|
Description = Configuration.GetSection("SwaggerDoc:Description").Value
|
|
}
|
|
);
|
|
}
|
|
|
|
Directory.GetFiles(AppDomain.CurrentDomain.BaseDirectory, "*.xml").ToList().ForEach(file =>
|
|
{
|
|
options.IncludeXmlComments(file, true);
|
|
});
|
|
options.DocumentFilter<HiddenApiFilter>(); // 在接口类、方法标记属性 [HiddenApi],可以阻止【Swagger文档】生成
|
|
//给api添加token令牌证书
|
|
options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
|
|
{
|
|
Description = "JWT授权(数据将在请求头中进行传输) 直接在下框中输入Bearer {token}(注意两者之间是一个空格)\"",
|
|
Name = "Authorization", //jwt默认的参数名称
|
|
In = ParameterLocation.Header, //jwt默认存放Authorization信息的位置(请求头中)
|
|
Type = SecuritySchemeType.ApiKey,
|
|
BearerFormat = "JWT",
|
|
Scheme = "Bearer"
|
|
});
|
|
//添加安全请求
|
|
options.AddSecurityRequirement(
|
|
new OpenApiSecurityRequirement
|
|
{
|
|
{
|
|
new OpenApiSecurityScheme
|
|
{
|
|
Reference = new OpenApiReference
|
|
{
|
|
Type = ReferenceType.SecurityScheme,
|
|
Id = "Bearer"
|
|
}
|
|
},
|
|
new string[] { }
|
|
}
|
|
}
|
|
);
|
|
//开启加权锁
|
|
options.OperationFilter<AddResponseHeadersFilter>();
|
|
options.OperationFilter<AppendAuthorizeToSummaryOperationFilter>();
|
|
options.OperationFilter<SecurityRequirementsOperationFilter>();
|
|
});
|
|
|
|
#endregion Swagger Api文档
|
|
|
|
#region 全局设置跨域访问
|
|
|
|
//允许所有跨域访问,测试用
|
|
services.AddCors(options => options.AddPolicy("Cors",
|
|
policy => policy.WithOrigins("*").AllowAnyHeader().AllowAnyMethod()));
|
|
// 跨域设置 建议正式环境
|
|
//services.AddCors(options => options.AddPolicy("ZnycCors",
|
|
// policy => policy.WithOrigins(Configuration.GetSection("AppSetting:AllowOrigins").Value.Split(',', StringSplitOptions.RemoveEmptyEntries)).AllowAnyHeader().AllowAnyMethod()));
|
|
|
|
#endregion 全局设置跨域访问
|
|
|
|
#region MiniProfiler
|
|
|
|
services.AddMiniProfiler(options => { options.RouteBasePath = "/profiler"; }).AddEntityFramework();
|
|
|
|
#endregion MiniProfiler
|
|
|
|
#region 控制器
|
|
|
|
services.AddControllers().AddJsonOptions(options =>
|
|
{
|
|
options.JsonSerializerOptions.WriteIndented = true;
|
|
options.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
|
|
//设置时间格式
|
|
options.JsonSerializerOptions.Converters.Add(new DateTimeJsonConverter());
|
|
options.JsonSerializerOptions.Converters.Add(new DateTimeNullableConverter());
|
|
//设置bool获取格式
|
|
options.JsonSerializerOptions.Converters.Add(new BooleanJsonConverter());
|
|
//设置Decimal获取格式
|
|
options.JsonSerializerOptions.Converters.Add(new DecimalJsonConverter());
|
|
//设置数字
|
|
options.JsonSerializerOptions.Converters.Add(new IntJsonConverter());
|
|
options.JsonSerializerOptions.PropertyNamingPolicy = new UpperFirstCaseNamingPolicy();
|
|
options.JsonSerializerOptions.Encoder = JavaScriptEncoder.Create(UnicodeRanges.All);
|
|
});
|
|
|
|
mvcBuilder = services.AddMvc(option =>
|
|
{
|
|
//option.Filters.Add<FunctionAuthorizationFilter>();
|
|
option.Filters.Add(new ExceptionHandlingAttribute());
|
|
// option.Filters.Add<ActionFilter>();
|
|
}).SetCompatibilityVersion(CompatibilityVersion.Latest).AddRazorRuntimeCompilation();
|
|
|
|
services.AddMvcCore()
|
|
.AddAuthorization().AddApiExplorer();
|
|
|
|
#endregion 控制器
|
|
|
|
services.AddSignalR(); //使用 SignalR
|
|
InitIoC(services);
|
|
services.AddCors(options => options.AddPolicy("ZnycCors",
|
|
policy => policy.WithOrigins("*").AllowAnyHeader().AllowAnyMethod()));
|
|
|
|
services.AddSenparcWeixinServices(Configuration);
|
|
services.TryAddSingleton<IApiHandler, ApiHandler>();
|
|
|
|
#region Handfire
|
|
services.AddHangfire(x => x.UseRedisStorage(Configuration.GetSection("CacheProvider:Redis_ConnectionString").Value));
|
|
services.AddHangfireServer(options =>
|
|
{
|
|
options.Queues = new[] { HangFireQueuesConfig.@default.ToString(), HangFireQueuesConfig.apis.ToString(), HangFireQueuesConfig.web.ToString(), HangFireQueuesConfig.recurring.ToString() };
|
|
options.ServerTimeout = TimeSpan.FromMinutes(4);
|
|
options.SchedulePollingInterval = TimeSpan.FromSeconds(15);//秒级任务需要配置短点,一般任务可以配置默认时间,默认15秒
|
|
options.ShutdownTimeout = TimeSpan.FromMinutes(30); //超时时间
|
|
options.WorkerCount = Math.Max(Environment.ProcessorCount, 20); //工作线程数,当前允许的最大线程,默认20
|
|
});
|
|
#endregion
|
|
}
|
|
|
|
/// <summary>
|
|
/// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
|
/// </summary>
|
|
/// <param name="app"></param>
|
|
/// <param name="env"></param>
|
|
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IOptions<SenparcSetting> senparcSetting)
|
|
{
|
|
if (app != null)
|
|
{
|
|
app.UseStaticHttpContextAccessor();
|
|
IServiceProvider provider = app.ApplicationServices;
|
|
AutoMapperService.UsePack(provider);
|
|
//加载插件应用
|
|
LoadMoudleApps(env);
|
|
|
|
app.UseMiniProfiler();
|
|
if (env.IsDevelopment())
|
|
{
|
|
app.UseDeveloperExceptionPage();
|
|
}
|
|
else
|
|
{
|
|
app.UseExceptionHandler("/Home/Error");
|
|
app.UseHsts();
|
|
}
|
|
|
|
|
|
app.UseSwagger();
|
|
app.UseSwaggerUI(options =>
|
|
{
|
|
foreach (ApiVersionDescription description in apiVersionProvider.ApiVersionDescriptions)
|
|
{
|
|
options.SwaggerEndpoint($"/swagger/{description.GroupName}/swagger.json",
|
|
$"{Configuration.GetSection("SwaggerDoc:Title").Value + description.GroupName.ToUpperInvariant()}");
|
|
options.RoutePrefix = string.Empty; //这里主要是不需要再输入swagger这个默认前缀
|
|
}
|
|
});
|
|
|
|
app.Use((context, next) =>
|
|
{
|
|
context.Request.EnableBuffering();
|
|
return next();
|
|
});
|
|
app.UseStaticFiles();
|
|
app.UseRouting();
|
|
app.UseAuthentication();
|
|
app.UseAuthorization();
|
|
//跨域
|
|
app.UseMiddleware<CorsMiddleware>();
|
|
app.UseCors("Cors");
|
|
app.UseEndpoints(endpoints =>
|
|
{
|
|
endpoints.MapControllers();
|
|
endpoints.MapControllerRoute("default", "api/{controller=Home}/{action=Index}/{id?}");
|
|
});
|
|
app.UseStatusCodePages();
|
|
|
|
#region WxSdk 注册
|
|
|
|
IRegisterService register = RegisterService.Start(senparcSetting.Value)
|
|
.UseSenparcGlobal();
|
|
register.UseSenparcGlobal(true);
|
|
AccessTokenContainer.Register("wx07c574aca93ae8d9", "466411f6865d130f50cc48ae1566de99");
|
|
|
|
#endregion WxSdk 注册
|
|
|
|
#region Hangfire
|
|
var filter = new BasicAuthAuthorizationFilter(
|
|
new BasicAuthAuthorizationFilterOptions
|
|
{
|
|
SslRedirect = false,
|
|
// Require secure connection for dashboard
|
|
RequireSsl = false,
|
|
// Case sensitive login checking
|
|
LoginCaseSensitive = false,
|
|
// Users
|
|
Users = new[]
|
|
{
|
|
new BasicAuthAuthorizationUser
|
|
{
|
|
Login = Configuration.GetSection("Hangfire:Login").Value,
|
|
PasswordClear = Configuration.GetSection("Hangfire:Password").Value
|
|
}
|
|
}
|
|
});
|
|
|
|
var options = new DashboardOptions
|
|
{
|
|
AppPath = "/",//返回时跳转的地址
|
|
DisplayStorageConnectionString = false,//是否显示数据库连接信息
|
|
Authorization = new[]
|
|
{
|
|
filter
|
|
},
|
|
IsReadOnlyFunc = Context =>
|
|
{
|
|
return false;//是否只读面板
|
|
}
|
|
};
|
|
|
|
app.UseHangfireDashboard("/admin_job", options);
|
|
HangfireDispose.HangfireService();
|
|
#endregion
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// IoC初始化
|
|
/// </summary>
|
|
/// <param name="services"></param>
|
|
/// <returns></returns>
|
|
private void InitIoC(IServiceCollection services)
|
|
{
|
|
#region 缓存
|
|
|
|
CacheProvider cacheProvider = new CacheProvider
|
|
{
|
|
IsUseRedis = Configuration.GetSection("CacheProvider:UseRedis").Value.ToBool(),
|
|
ConnectionString = Configuration.GetSection("CacheProvider:Redis_ConnectionString").Value,
|
|
InstanceName = Configuration.GetSection("CacheProvider:Redis_InstanceName").Value
|
|
};
|
|
|
|
JsonSerializerOptions options = new JsonSerializerOptions();
|
|
options.Encoder = JavaScriptEncoder.Create(UnicodeRanges.All);
|
|
options.WriteIndented = true;
|
|
options.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
|
|
options.AllowTrailingCommas = true;
|
|
//设置时间格式
|
|
options.Converters.Add(new DateTimeJsonConverter());
|
|
options.Converters.Add(new DateTimeNullableConverter());
|
|
//设置bool获取格式
|
|
options.Converters.Add(new BooleanJsonConverter());
|
|
//设置数字
|
|
options.Converters.Add(new IntJsonConverter());
|
|
options.PropertyNamingPolicy = new UpperFirstCaseNamingPolicy();
|
|
options.PropertyNameCaseInsensitive = true; //忽略大小写
|
|
|
|
|
|
|
|
//判断是否使用Redis,如果不使用 Redis就默认使用 MemoryCache
|
|
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
|
|
if (cacheProvider.IsUseRedis)
|
|
{
|
|
//Use Redis
|
|
services.AddStackExchangeRedisCache(options =>
|
|
{
|
|
options.Configuration = cacheProvider.ConnectionString;
|
|
options.InstanceName = cacheProvider.InstanceName;
|
|
});
|
|
services.AddSingleton(typeof(ICacheService), new RedisCacheService(new RedisCacheOptions
|
|
{
|
|
Configuration = cacheProvider.ConnectionString,
|
|
InstanceName = cacheProvider.InstanceName
|
|
}, options));
|
|
services.Configure<DistributedCacheEntryOptions>(option =>
|
|
option.AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(5)); //设置Redis缓存有效时间为5分钟。
|
|
}
|
|
else
|
|
{
|
|
//Use MemoryCache
|
|
services.AddSingleton<IMemoryCache>(factory =>
|
|
{
|
|
MemoryCache cache = new MemoryCache(new MemoryCacheOptions());
|
|
return cache;
|
|
});
|
|
services.AddSingleton<ICacheService, MemoryCacheService>();
|
|
services.Configure<MemoryCacheEntryOptions>(
|
|
options => options.AbsoluteExpirationRelativeToNow =
|
|
TimeSpan.FromMinutes(5)); //设置MemoryCache缓存有效时间为5分钟
|
|
}
|
|
|
|
services.AddTransient<MemoryCacheService>();
|
|
services.AddMemoryCache(); // 启用MemoryCache
|
|
|
|
services.AddSingleton(cacheProvider); //注册缓存配置
|
|
|
|
#endregion 缓存
|
|
|
|
#region 身份认证授权
|
|
|
|
IConfigurationSection jwtConfig = Configuration.GetSection("Jwt");
|
|
JwtOption jwtOption = new JwtOption
|
|
{
|
|
Issuer = jwtConfig["Issuer"],
|
|
Expiration = Convert.ToInt16(jwtConfig["Expiration"]),
|
|
Secret = jwtConfig["Secret"],
|
|
Audience = jwtConfig["Audience"],
|
|
refreshJwtTime = Convert.ToInt16(jwtConfig["refreshJwtTime"])
|
|
};
|
|
services.AddAuthentication(options =>
|
|
{
|
|
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
|
|
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
|
|
;
|
|
}).AddJwtBearer(jwtBearerOptions =>
|
|
{
|
|
jwtBearerOptions.TokenValidationParameters = new TokenValidationParameters
|
|
{
|
|
ValidateIssuerSigningKey = true,
|
|
IssuerSigningKey =
|
|
new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtOption.Secret)), //秘钥
|
|
ValidateIssuer = true,
|
|
ValidIssuer = jwtOption.Issuer,
|
|
ValidateAudience = true,
|
|
ValidAudience = jwtOption.Audience,
|
|
ValidateLifetime = true,
|
|
ClockSkew = TimeSpan.FromMinutes(5)
|
|
};
|
|
});
|
|
services.AddSingleton(jwtOption); //注册配置
|
|
|
|
#endregion 身份认证授权
|
|
|
|
services.AddAutoScanInjection(); //自动化注入仓储和服务
|
|
services.AddTransient<IDbContextCore, MySqlDbContext>(); //注入EF上下文
|
|
|
|
#region automapper
|
|
|
|
System.Collections.Generic.List<System.Reflection.Assembly> myAssembly = RuntimeHelper.GetAllZnycAssemblies().ToList();
|
|
services.AddAutoMapper(myAssembly);
|
|
services.AddTransient<IMapper, Mapper>();
|
|
|
|
#endregion automapper
|
|
|
|
services
|
|
.AddMongoDBContext<MongoContext>(options =>
|
|
{
|
|
options.UseMongo(Configuration.GetSection("MongoConnection:ConnectionString").Value,
|
|
Configuration.GetSection("MongoConnection:Database").Value);
|
|
});
|
|
|
|
services
|
|
.AddMongoDbService<MongoContext>();
|
|
|
|
|
|
services.AddTransient<IGpsRealTimeRepository, GpsRealTimeRepository>();
|
|
|
|
#region Cos
|
|
|
|
CosOptions cosOptions = new CosOptions
|
|
{
|
|
SecretId = Configuration.GetSection("CosOptions:SecretId").Value,
|
|
SecretKey = Configuration.GetSection("CosOptions:SecretKey").Value,
|
|
Bucket = Configuration.GetSection("CosOptions:Bucket").Value,
|
|
Region = Configuration.GetSection("CosOptions:Region").Value,
|
|
AllowPrefix = Configuration.GetSection("CosOptions:AllowPrefix").Value,
|
|
DurationSeconds = Configuration.GetSection("CosOptions:DurationSeconds").Value.ToInt(),
|
|
AllowActions = new string[] { "name/cos:PutObject",
|
|
// 表单上传、小程序上传
|
|
"name/cos:PostObject",
|
|
// 分片上传
|
|
"name/cos:InitiateMultipartUpload",
|
|
"name/cos:ListMultipartUploads",
|
|
"name/cos:ListParts",
|
|
"name/cos:UploadPart",
|
|
"name/cos:CompleteMultipartUpload" }
|
|
};
|
|
services.AddSingleton(cosOptions); //注册缓存配置
|
|
|
|
#endregion
|
|
App.Services = services;
|
|
|
|
services.AddSingleton(HtmlEncoder.Create(UnicodeRanges.All));
|
|
|
|
|
|
|
|
}
|
|
|
|
/// <summary>
|
|
/// 加载模块应用
|
|
/// </summary>
|
|
/// <param name="env"></param>
|
|
private void LoadMoudleApps(IWebHostEnvironment env)
|
|
{
|
|
// 定位到插件应用目录 Apps
|
|
Microsoft.Extensions.FileProviders.IFileInfo apps = env.ContentRootFileProvider.GetFileInfo("Apps");
|
|
if (!Directory.Exists(apps.PhysicalPath))
|
|
{
|
|
return;
|
|
}
|
|
|
|
// 把 Apps 下的动态库拷贝一份来运行,
|
|
// 使 Apps 中的动态库不会在运行时被占用(以便重新编译)
|
|
string shadows = targetPath = PrepareShadowCopies();
|
|
// 从 Shadow Copy 目录加载 Assembly 并注册到 Mvc 中
|
|
LoadFromShadowCopies(shadows);
|
|
|
|
string PrepareShadowCopies()
|
|
{
|
|
// 准备 Shadow Copy 的目标目录
|
|
string target = Path.Combine(env.ContentRootPath, "app_data", "apps-cache");
|
|
Directory.CreateDirectory(target);
|
|
|
|
// 找到插件目录下 bin 目录中的 .dll,拷贝
|
|
Directory.EnumerateDirectories(apps.PhysicalPath)
|
|
.Select(path => Path.Combine(path, "bin"))
|
|
.Where(Directory.Exists)
|
|
.SelectMany(bin => Directory.EnumerateFiles(bin, "*.dll"))
|
|
.ForEach(dll => File.Copy(dll, Path.Combine(target, Path.GetFileName(dll)), true));
|
|
|
|
return target;
|
|
}
|
|
|
|
void LoadFromShadowCopies(string targetPath)
|
|
{
|
|
foreach (string dll in Directory.GetFiles(targetPath, "*.dll"))
|
|
{
|
|
try
|
|
{
|
|
//解决插件还引用其他主程序没有引用的第三方dll问题System.IO.FileNotFoundException
|
|
AssemblyLoadContext.Default.LoadFromAssemblyPath(dll);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
//非.net程序集类型的dll关联load时会报错,这里忽略就可以
|
|
Log4NetHelper.Error(ex.Message);
|
|
}
|
|
}
|
|
|
|
// 从 Shadow Copy 目录加载 Assembly 并注册到 Mvc 中
|
|
System.Collections.Generic.IEnumerable<System.Reflection.Assembly> groups = Directory.EnumerateFiles(targetPath, "Znyc.Cloudcar.Admin.*App.Api.dll")
|
|
.Select(AssemblyLoadContext.Default.LoadFromAssemblyPath);
|
|
|
|
// 直接加载到为 ApplicationPart
|
|
groups.ForEach(mvcBuilder.AddApplicationPart);
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|