build: 📦 完善构建脚本 (#66)

* style: 💄 代码样式

* style: 💄 代码格式整理

* build: 📦 完善构建脚本
This commit is contained in:
nsnail 2023-11-23 18:54:26 +08:00 committed by GitHub
parent 9134c4fe01
commit 0049536d2e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
33 changed files with 72 additions and 83 deletions

View File

@ -36,8 +36,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "scripts", "scripts", "{BB0B
ProjectSection(SolutionItems) = preProject ProjectSection(SolutionItems) = preProject
code.clean.csx = scripts/code.clean.csx code.clean.csx = scripts/code.clean.csx
code.clean.ps1 = scripts/code.clean.ps1 code.clean.ps1 = scripts/code.clean.ps1
code.cleanup.full.ps1 = scripts/code.cleanup.full.ps1
dot.clean.cmd = scripts/dot.clean.cmd
gen.cs.tt = scripts/gen.cs.tt gen.cs.tt = scripts/gen.cs.tt
gen.id.linq = scripts/gen.id.linq gen.id.linq = scripts/gen.id.linq
gen.ln.cmd = scripts/gen.ln.cmd gen.ln.cmd = scripts/gen.ln.cmd
@ -47,6 +45,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "scripts", "scripts", "{BB0B
image.optimize.csx = scripts/image.optimize.csx image.optimize.csx = scripts/image.optimize.csx
install.as.tpl.ps1 = scripts/install.as.tpl.ps1 install.as.tpl.ps1 = scripts/install.as.tpl.ps1
rename.csx = scripts/rename.csx rename.csx = scripts/rename.csx
resharper.full.ps1 = scripts/resharper.full.ps1
switcher.freesql.json = scripts/switcher.freesql.json switcher.freesql.json = scripts/switcher.freesql.json
switcher.furion.json = scripts/switcher.furion.json switcher.furion.json = scripts/switcher.furion.json
switcher.nsext.json = scripts/switcher.nsext.json switcher.nsext.json = scripts/switcher.nsext.json

View File

@ -1,13 +1,13 @@
<Project> <Project>
<Target Name="PreBuild" BeforeTargets="PreBuildEvent"> <Target Name="PreBuild" BeforeTargets="PreBuildEvent">
<Exec Command="dotnet tool restore" StdOutEncoding="utf-8"/> <Exec Command="dotnet tool restore" StdOutEncoding="utf-8" />
<Exec Condition="!Exists('$(SolutionDir)/assets/res/Ln.resx')" WorkingDirectory="$(SolutionDir)/scripts" <Exec Condition="!Exists('$(SolutionDir)/assets/res/Ln.resx')" WorkingDirectory="$(SolutionDir)/scripts"
Command="dotnet t4 ./gen.resx.tt -o ../assets/res/Ln.resx" Command="dotnet t4 ./gen.resx.tt -o ../assets/res/Ln.resx"
StdOutEncoding="utf-8"/> StdOutEncoding="utf-8" />
<Exec Condition="!Exists('$(SolutionDir)/dist/backend/$(ProjectName)/Ln.cs')" <Exec Condition="!Exists('$(SolutionDir)/dist/backend/$(ProjectName)/Ln.cs')"
WorkingDirectory="$(SolutionDir)/scripts" WorkingDirectory="$(SolutionDir)/scripts"
Command="dotnet t4 ./gen.cs.tt -o ../dist/backend/$(ProjectName)/Ln.cs" Command="dotnet t4 ./gen.cs.tt -o ../dist/backend/$(ProjectName)/Ln.cs"
StdOutEncoding="utf-8"/> StdOutEncoding="utf-8" />
</Target> </Target>
<ItemGroup> <ItemGroup>
<None Include="$(SolutionDir)/assets/res/Ln.txt"> <None Include="$(SolutionDir)/assets/res/Ln.txt">

View File

@ -1,7 +1,5 @@
$files = $( foreach ($line in $( git diff head origin/dev --stat-width 200 ) | findstr '\|') npm --prefix ../src/frontend/admin run prettier
{ dotnet jb cleanupcode --no-build --include=$($(git status --porcelain | Where-Object { $_ -match "^\s*[MA]" } | ForEach-Object { $_.TrimStart(" M").TrimStart(" A") }) -join ";") ../NetAdmin.sln
$line.split('\|')[0].trim() dot rbom -w -e refs -e .git -e node_modules ../
} ) -join ';' dot trim -w -e refs -e .git -e node_modules ../
echo $files dot tolf -w -e refs -e .git -e node_modules ../
dotnet jb cleanupcode --no-build --include = "$files" ./NetAdmin.sln
dotnet script ./PushSign.csx

View File

@ -1,4 +0,0 @@
call npm --prefix ../src/frontend/admin run prettier
dot rbom -w -e refs -e .git -e node_modules ../
dot trim -w -e refs -e .git -e node_modules ../
dot tolf -w -e refs -e .git -e node_modules ../

View File

@ -1,5 +1,6 @@
$branch = $( git branch --show-current ) $branch = $( git branch --show-current )
./dot.clean.cmd git add ../
./code.clean.ps1
git add ../ git add ../
../node_modules/.bin/git-cz.ps1 ../node_modules/.bin/git-cz.ps1
git pull git pull

View File

@ -6,7 +6,8 @@ namespace NetAdmin.Application.Repositories;
/// <summary> /// <summary>
/// 默认仓储 /// 默认仓储
/// </summary> /// </summary>
public sealed class DefaultRepository<TEntity>(IFreeSql fSql // public sealed class DefaultRepository<TEntity>(
IFreeSql fSql //
, UnitOfWorkManager uowManger // , UnitOfWorkManager uowManger //
, ContextUserToken userToken) // , ContextUserToken userToken) //
: DefaultRepository<TEntity, long>(fSql, uowManger) : DefaultRepository<TEntity, long>(fSql, uowManger)

View File

@ -18,8 +18,8 @@ namespace NetAdmin.BizServer.Tests;
/// 所有测试 /// 所有测试
/// </summary> /// </summary>
[SuppressMessage("Usage", "xUnit1028:Test method must have valid return type")] [SuppressMessage("Usage", "xUnit1028:Test method must have valid return type")]
public class AllTests(WebApplicationFactory<Startup> factory, ITestOutputHelper testOutputHelper) : public class AllTests(WebApplicationFactory<Startup> factory, ITestOutputHelper testOutputHelper)
WebApiTestBase<Startup>(factory, testOutputHelper), IToolsModule, ICacheModule, IApiModule, IConfigModule : WebApiTestBase<Startup>(factory, testOutputHelper), IToolsModule, ICacheModule, IApiModule, IConfigModule
{ {
/// <inheritdoc /> /// <inheritdoc />

View File

@ -6,7 +6,7 @@ namespace NetAdmin.Domain.Attributes.DataValidation;
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Parameter)] [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Parameter)]
#pragma warning disable DesignedForInheritance #pragma warning disable DesignedForInheritance
public class RegexAttribute : RegularExpressionAttribute public class RegexAttribute : RegularExpressionAttribute
#pragma warning restore DesignedForInheritance #pragma warning restore DesignedForInheritance
{ {
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="RegexAttribute" /> class. /// Initializes a new instance of the <see cref="RegexAttribute" /> class.

View File

@ -7,8 +7,8 @@ namespace NetAdmin.Host.Controllers;
/// 健康控制器 /// 健康控制器
/// </summary> /// </summary>
[ApiDescriptionSettings("Health")] [ApiDescriptionSettings("Health")]
public sealed class HealthController public sealed class HealthController(ICache<IDistributedCache, IService> cache)
(ICache<IDistributedCache, IService> cache) : ControllerBase<ICache<IDistributedCache, IService>, IService>(cache) : ControllerBase<ICache<IDistributedCache, IService>, IService>(cache)
{ {
/// <summary> /// <summary>
/// 健康检查 /// 健康检查

View File

@ -1,9 +1,10 @@
using Microsoft.AspNetCore.HttpOverrides;
#if DEBUG #if DEBUG
using IGeekFan.AspNetCore.Knife4jUI; using IGeekFan.AspNetCore.Knife4jUI;
#else #else
using Prometheus; using Prometheus;
#endif #endif
using Microsoft.AspNetCore.HttpOverrides;
namespace NetAdmin.Host.Extensions; namespace NetAdmin.Host.Extensions;

View File

@ -8,9 +8,10 @@ namespace NetAdmin.Host.Middlewares;
/// <remarks> /// <remarks>
/// 放在所有中间件最前面 /// 放在所有中间件最前面
/// </remarks> /// </remarks>
public sealed class RequestAuditMiddleware(RequestDelegate next public sealed class RequestAuditMiddleware(
, IOptions<DynamicApiControllerSettingsOptions> RequestDelegate next
dynamicApiControllerSettingsOptions, RequestLogger requestLogger) , IOptions<DynamicApiControllerSettingsOptions> dynamicApiControllerSettingsOptions
, RequestLogger requestLogger)
{ {
private readonly PathString _defaultRoutePrefix private readonly PathString _defaultRoutePrefix
= new($"/{dynamicApiControllerSettingsOptions.Value.DefaultRoutePrefix}"); = new($"/{dynamicApiControllerSettingsOptions.Value.DefaultRoutePrefix}");

View File

@ -7,7 +7,8 @@ namespace NetAdmin.Host.Utils;
/// <summary> /// <summary>
/// 请求日志记录器 /// 请求日志记录器
/// </summary> /// </summary>
public sealed class RequestLogger(ILogger<RequestLogger> logger public sealed class RequestLogger(
ILogger<RequestLogger> logger
, IOptions<SpecificationDocumentSettingsOptions> specificationDocumentSettingsOptions , IOptions<SpecificationDocumentSettingsOptions> specificationDocumentSettingsOptions
, IEventPublisher eventPublisher) : ISingleton , IEventPublisher eventPublisher) : ISingleton
{ {

View File

@ -5,7 +5,7 @@ namespace NetAdmin.Infrastructure.Exceptions;
/// </summary> /// </summary>
#pragma warning disable RCS1194 #pragma warning disable RCS1194
public abstract class NetAdminException : Exception public abstract class NetAdminException : Exception
#pragma warning restore RCS1194 #pragma warning restore RCS1194
{ {
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="NetAdminException" /> class. /// Initializes a new instance of the <see cref="NetAdminException" /> class.

View File

@ -5,5 +5,5 @@ namespace NetAdmin.Infrastructure.Exceptions;
/// </summary> /// </summary>
#pragma warning disable RCS1194 #pragma warning disable RCS1194
public sealed class NetAdminGetLockerException : NetAdminUnexpectedException public sealed class NetAdminGetLockerException : NetAdminUnexpectedException
#pragma warning restore RCS1194 #pragma warning restore RCS1194
{ } { }

View File

@ -7,8 +7,7 @@ namespace NetAdmin.Infrastructure.Exceptions;
/// 参数格式错误、内容校验错误等 /// 参数格式错误、内容校验错误等
/// </remarks> /// </remarks>
#pragma warning disable RCS1194 #pragma warning disable RCS1194
public sealed class NetAdminInvalidInputException public sealed class NetAdminInvalidInputException(string message = null, Exception innerException = null)
(string message = null, Exception innerException = null) : NetAdminException( : NetAdminException(ErrorCodes.InvalidInput, message, innerException)
ErrorCodes.InvalidInput, message, innerException) #pragma warning restore RCS1194
#pragma warning restore RCS1194
{ } { }

View File

@ -8,7 +8,7 @@ namespace NetAdmin.Infrastructure.Exceptions;
/// </remarks> /// </remarks>
#pragma warning disable RCS1194, DesignedForInheritance #pragma warning disable RCS1194, DesignedForInheritance
public class NetAdminInvalidOperationException : NetAdminException public class NetAdminInvalidOperationException : NetAdminException
#pragma warning restore DesignedForInheritance, RCS1194 #pragma warning restore DesignedForInheritance, RCS1194
{ {
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="NetAdminInvalidOperationException" /> class. /// Initializes a new instance of the <see cref="NetAdminInvalidOperationException" /> class.

View File

@ -8,7 +8,7 @@ namespace NetAdmin.Infrastructure.Exceptions;
/// </remarks> /// </remarks>
#pragma warning disable RCS1194, DesignedForInheritance #pragma warning disable RCS1194, DesignedForInheritance
public class NetAdminUnexpectedException : NetAdminException public class NetAdminUnexpectedException : NetAdminException
#pragma warning restore DesignedForInheritance, RCS1194 #pragma warning restore DesignedForInheritance, RCS1194
{ {
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="NetAdminUnexpectedException" /> class. /// Initializes a new instance of the <see cref="NetAdminUnexpectedException" /> class.

View File

@ -9,7 +9,7 @@ namespace NetAdmin.Infrastructure.Utils;
/// </summary> /// </summary>
#pragma warning disable DesignedForInheritance #pragma warning disable DesignedForInheritance
public class RedLocker : IDisposable, ISingleton public class RedLocker : IDisposable, ISingleton
#pragma warning restore DesignedForInheritance #pragma warning restore DesignedForInheritance
{ {
// Track whether Dispose has been called. // Track whether Dispose has been called.
private bool _disposed; private bool _disposed;

View File

@ -8,7 +8,8 @@ using NetAdmin.SysComponent.Application.Services.Sys.Dependency;
namespace NetAdmin.SysComponent.Application.Services.Sys; namespace NetAdmin.SysComponent.Application.Services.Sys;
/// <inheritdoc cref="IApiService" /> /// <inheritdoc cref="IApiService" />
public sealed class ApiService(DefaultRepository<Sys_Api> rpo // public sealed class ApiService(
DefaultRepository<Sys_Api> rpo //
, XmlCommentReader xmlCommentReader // , XmlCommentReader xmlCommentReader //
, IActionDescriptorCollectionProvider actionDescriptorCollectionProvider) // , IActionDescriptorCollectionProvider actionDescriptorCollectionProvider) //
: RepositoryService<Sys_Api, IApiService>(rpo), IApiService : RepositoryService<Sys_Api, IApiService>(rpo), IApiService

View File

@ -12,7 +12,9 @@ using NetAdmin.SysComponent.Application.Services.Sys.Dependency;
namespace NetAdmin.SysComponent.Application.Services.Sys; namespace NetAdmin.SysComponent.Application.Services.Sys;
/// <inheritdoc cref="ISiteMsgService" /> /// <inheritdoc cref="ISiteMsgService" />
public sealed class SiteMsgService(DefaultRepository<Sys_SiteMsg> rpo, ContextUserInfo contextUserInfo public sealed class SiteMsgService(
DefaultRepository<Sys_SiteMsg> rpo
, ContextUserInfo contextUserInfo
, ISiteMsgFlagService siteMsgFlagService) // , ISiteMsgFlagService siteMsgFlagService) //
: RepositoryService<Sys_SiteMsg, ISiteMsgService>(rpo), ISiteMsgService : RepositoryService<Sys_SiteMsg, ISiteMsgService>(rpo), ISiteMsgService
{ {

View File

@ -14,7 +14,8 @@ using NetAdmin.SysComponent.Application.Services.Sys.Dependency;
namespace NetAdmin.SysComponent.Application.Services.Sys; namespace NetAdmin.SysComponent.Application.Services.Sys;
/// <inheritdoc cref="IUserService" /> /// <inheritdoc cref="IUserService" />
public sealed class UserService(DefaultRepository<Sys_User> rpo // public sealed class UserService(
DefaultRepository<Sys_User> rpo //
, IUserProfileService userProfileService // , IUserProfileService userProfileService //
, IVerifyCodeService verifyCodeService // , IVerifyCodeService verifyCodeService //
, IEventPublisher eventPublisher) // , IEventPublisher eventPublisher) //

View File

@ -8,9 +8,8 @@ using NetAdmin.SysComponent.Cache.Sys.Dependency;
namespace NetAdmin.SysComponent.Cache.Sys; namespace NetAdmin.SysComponent.Cache.Sys;
/// <inheritdoc cref="ISiteMsgCache" /> /// <inheritdoc cref="ISiteMsgCache" />
public sealed class SiteMsgCache public sealed class SiteMsgCache(IDistributedCache cache, ISiteMsgService service)
(IDistributedCache cache, ISiteMsgService service) : DistributedCache<ISiteMsgService>(cache, service), IScoped : DistributedCache<ISiteMsgService>(cache, service), IScoped, ISiteMsgCache
, ISiteMsgCache
{ {
/// <inheritdoc /> /// <inheritdoc />
public Task<int> BulkDeleteAsync(BulkReq<DelReq> req) public Task<int> BulkDeleteAsync(BulkReq<DelReq> req)

View File

@ -7,10 +7,8 @@ using NetAdmin.SysComponent.Cache.Sys.Dependency;
namespace NetAdmin.SysComponent.Cache.Sys; namespace NetAdmin.SysComponent.Cache.Sys;
/// <inheritdoc cref="ISiteMsgDeptCache" /> /// <inheritdoc cref="ISiteMsgDeptCache" />
public sealed class SiteMsgDeptCache public sealed class SiteMsgDeptCache(IDistributedCache cache, ISiteMsgDeptService service)
(IDistributedCache cache, ISiteMsgDeptService service) : DistributedCache<ISiteMsgDeptService>(cache, service) : DistributedCache<ISiteMsgDeptService>(cache, service), IScoped, ISiteMsgDeptCache
, IScoped
, ISiteMsgDeptCache
{ {
/// <inheritdoc /> /// <inheritdoc />
public Task<int> BulkDeleteAsync(BulkReq<DelReq> req) public Task<int> BulkDeleteAsync(BulkReq<DelReq> req)

View File

@ -7,10 +7,8 @@ using NetAdmin.SysComponent.Cache.Sys.Dependency;
namespace NetAdmin.SysComponent.Cache.Sys; namespace NetAdmin.SysComponent.Cache.Sys;
/// <inheritdoc cref="ISiteMsgFlagCache" /> /// <inheritdoc cref="ISiteMsgFlagCache" />
public sealed class SiteMsgFlagCache public sealed class SiteMsgFlagCache(IDistributedCache cache, ISiteMsgFlagService service)
(IDistributedCache cache, ISiteMsgFlagService service) : DistributedCache<ISiteMsgFlagService>(cache, service) : DistributedCache<ISiteMsgFlagService>(cache, service), IScoped, ISiteMsgFlagCache
, IScoped
, ISiteMsgFlagCache
{ {
/// <inheritdoc /> /// <inheritdoc />
public Task<int> BulkDeleteAsync(BulkReq<DelReq> req) public Task<int> BulkDeleteAsync(BulkReq<DelReq> req)

View File

@ -7,10 +7,8 @@ using NetAdmin.SysComponent.Cache.Sys.Dependency;
namespace NetAdmin.SysComponent.Cache.Sys; namespace NetAdmin.SysComponent.Cache.Sys;
/// <inheritdoc cref="ISiteMsgRoleCache" /> /// <inheritdoc cref="ISiteMsgRoleCache" />
public sealed class SiteMsgRoleCache public sealed class SiteMsgRoleCache(IDistributedCache cache, ISiteMsgRoleService service)
(IDistributedCache cache, ISiteMsgRoleService service) : DistributedCache<ISiteMsgRoleService>(cache, service) : DistributedCache<ISiteMsgRoleService>(cache, service), IScoped, ISiteMsgRoleCache
, IScoped
, ISiteMsgRoleCache
{ {
/// <inheritdoc /> /// <inheritdoc />
public Task<int> BulkDeleteAsync(BulkReq<DelReq> req) public Task<int> BulkDeleteAsync(BulkReq<DelReq> req)

View File

@ -7,10 +7,8 @@ using NetAdmin.SysComponent.Cache.Sys.Dependency;
namespace NetAdmin.SysComponent.Cache.Sys; namespace NetAdmin.SysComponent.Cache.Sys;
/// <inheritdoc cref="ISiteMsgUserCache" /> /// <inheritdoc cref="ISiteMsgUserCache" />
public sealed class SiteMsgUserCache public sealed class SiteMsgUserCache(IDistributedCache cache, ISiteMsgUserService service)
(IDistributedCache cache, ISiteMsgUserService service) : DistributedCache<ISiteMsgUserService>(cache, service) : DistributedCache<ISiteMsgUserService>(cache, service), IScoped, ISiteMsgUserCache
, IScoped
, ISiteMsgUserCache
{ {
/// <inheritdoc /> /// <inheritdoc />
public Task<int> BulkDeleteAsync(BulkReq<DelReq> req) public Task<int> BulkDeleteAsync(BulkReq<DelReq> req)

View File

@ -7,9 +7,8 @@ using NetAdmin.SysComponent.Cache.Tpl.Dependency;
namespace NetAdmin.SysComponent.Cache.Tpl; namespace NetAdmin.SysComponent.Cache.Tpl;
/// <inheritdoc cref="IExampleCache" /> /// <inheritdoc cref="IExampleCache" />
public sealed class ExampleCache public sealed class ExampleCache(IDistributedCache cache, IExampleService service)
(IDistributedCache cache, IExampleService service) : DistributedCache<IExampleService>(cache, service), IScoped : DistributedCache<IExampleService>(cache, service), IScoped, IExampleCache
, IExampleCache
{ {
/// <inheritdoc /> /// <inheritdoc />
public Task<int> BulkDeleteAsync(BulkReq<DelReq> req) public Task<int> BulkDeleteAsync(BulkReq<DelReq> req)

View File

@ -11,9 +11,8 @@ namespace NetAdmin.SysComponent.Host.Controllers.Sys;
/// </summary> /// </summary>
[AllowAnonymous] [AllowAnonymous]
[ApiDescriptionSettings(nameof(Sys), Module = nameof(Sys))] [ApiDescriptionSettings(nameof(Sys), Module = nameof(Sys))]
public sealed class ConstantController public sealed class ConstantController(IConstantCache cache, IOptions<JsonOptions> jsonOptions)
(IConstantCache cache, IOptions<JsonOptions> jsonOptions) : ControllerBase<IConstantCache, IConstantService>(cache) : ControllerBase<IConstantCache, IConstantService>(cache), IConstantModule
, IConstantModule
{ {
/// <summary> /// <summary>
/// 获得常量字符串 /// 获得常量字符串

View File

@ -13,8 +13,8 @@ namespace NetAdmin.SysComponent.Host.Controllers.Sys;
/// 用户服务 /// 用户服务
/// </summary> /// </summary>
[ApiDescriptionSettings(nameof(Sys), Module = nameof(Sys))] [ApiDescriptionSettings(nameof(Sys), Module = nameof(Sys))]
public sealed class UserController public sealed class UserController(IUserCache cache, IConfigCache configCache)
(IUserCache cache, IConfigCache configCache) : ControllerBase<IUserCache, IUserService>(cache), IUserModule : ControllerBase<IUserCache, IUserService>(cache), IUserModule
{ {
/// <summary> /// <summary>
/// 批量删除用户 /// 批量删除用户

View File

@ -12,9 +12,8 @@ namespace NetAdmin.SysComponent.Host.Controllers.Sys;
/// 验证码服务 /// 验证码服务
/// </summary> /// </summary>
[ApiDescriptionSettings(nameof(Sys), Module = nameof(Sys))] [ApiDescriptionSettings(nameof(Sys), Module = nameof(Sys))]
public sealed class VerifyCodeController public sealed class VerifyCodeController(IVerifyCodeCache cache, ICaptchaCache captchaCache)
(IVerifyCodeCache cache, ICaptchaCache captchaCache) : ControllerBase<IVerifyCodeCache, IVerifyCodeService>(cache) : ControllerBase<IVerifyCodeCache, IVerifyCodeService>(cache), IVerifyCodeModule
, IVerifyCodeModule
{ {
/// <inheritdoc /> /// <inheritdoc />
[NonAction] [NonAction]

View File

@ -11,8 +11,8 @@ namespace NetAdmin.Tests;
/// <summary> /// <summary>
/// WebApi 测试用例基类 /// WebApi 测试用例基类
/// </summary> /// </summary>
public abstract class WebApiTestBase<T> public abstract class WebApiTestBase<T>(WebApplicationFactory<T> factory, ITestOutputHelper testOutputHelper)
(WebApplicationFactory<T> factory, ITestOutputHelper testOutputHelper) : IClassFixture<WebApplicationFactory<T>> : IClassFixture<WebApplicationFactory<T>>
where T : AppStartup where T : AppStartup
{ {
private const string _ACCOUNT = "root"; private const string _ACCOUNT = "root";