// This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app) { if (WebHostEnvironment.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseCors("DevelopmentPolicy"); app.UseSwagger(); app.UseSwaggerUI(setupAction => { setupAction.SwaggerEndpoint( $"{(WebHostEnvironment.IsDevelopment() ? "" : API_PREFIX)}/swagger/MyFinanceApiOpenAPISecification/swagger.json", "MyFinanceAPI"); setupAction.RoutePrefix = ""; }); app.UseHttpsRedirection(); app.UseRouting(); app.UseAuthentication(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); }
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(ApplicationBuilder app, WebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } else { app.UseExceptionHandler("/Error"); // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. app.UseHsts(); } app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseRouting(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapRazorPages(); } }
public void Configure(IApplicationBuilder app) { if (WebHostEnvironment.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseRouting(); app.UseEndpoints(endpoints => { endpoints.MapGet("/", async context => { TelemetryClient telemetryClient = context.RequestServices.GetRequiredService <TelemetryClient>(); // You can inject TelemetryClient in controllers and services to provide additional data to app insights sdk. using (IOperationHolder <DependencyTelemetry> exportExcelOperation = telemetryClient.StartOperation <DependencyTelemetry>("ExportToExcel")) { exportExcelOperation.Telemetry.Type = "Background"; exportExcelOperation.Telemetry.Target = "ApplicationServer"; try { await Task.Delay(1000); telemetryClient.TrackTrace($"done {30}%"); await Task.Delay(1000); telemetryClient.TrackTrace($"done {60}%"); await Task.Delay(1000); telemetryClient.TrackTrace($"done {100}%"); } catch (Exception exp) { telemetryClient.TrackException(exp); throw; } finally { telemetryClient.StopOperation(exportExcelOperation); } } telemetryClient.TrackEvent("MyEvent", new Dictionary <string, string> { { "prop1", "1" }, { "prop2", "2" } }); ILogger <ClaimsIdentity> logger = context.RequestServices.GetRequiredService <ILogger <ClaimsIdentity> >(); // 3rd party libraries which relies on MS.Ext.Logging will also be captured by app insights sdk. logger.LogWarning("User {user} was logged in...", "admin"); await context.Response.WriteAsync("Hello World!"); }); }); }
public void ConfigureServices(IServiceCollection services) { services.AddInfrastructure(WebHostEnvironment.IsDevelopment()); services.AddPersistence(Configuration); services.AddApplication(); services.AddControllers(); services.AddSpaStaticFiles(configuration => { configuration.RootPath = "ClientApp/dist"; }); }
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app) { if (WebHostEnvironment.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseHttpsRedirection(); app.UseRouting(); app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); }
// This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { services.AddControllers(); services.AddTransient <IImageService>((service) => new ImageService(new ImageSaver(), new ImageChecker())); if (WebHostEnvironment.IsDevelopment()) { services.AddDbContext <ImageContext>((options) => options.UseInMemoryDatabase("ImagesDB")); } else { services.AddDbContext <ImageContext>((options) => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"))); } }
/// <summary> /// Build configuration from AppSettings, Environment Variables, Azure Key Valut and (User Secrets - DEV only). /// </summary> /// <returns><see cref="IConfiguration"/></returns> private IConfiguration BuildConfiguration() { var configurationBuilder = new ConfigurationBuilder(); configurationBuilder.SetBasePath(Directory.GetCurrentDirectory()) .AddJsonFile("appsettings.json") .AddEnvironmentVariables() .AddUserSecrets(typeof(Startup).Assembly) .AddAzureKeyVaultIfAvailable(); if (WebHostEnvironment.IsDevelopment()) { // Re-add User secrets so it takes precedent for local development configurationBuilder.AddUserSecrets(typeof(Startup).Assembly); } return(configurationBuilder.Build()); }
public void ConfigureServices(IServiceCollection services) { services.AddResponseCompression(); services.AddDbContext <ApplicationDbContext>(options => options.UseSqlServer( Configuration.GetConnectionString("DefaultConnection"))); services.AddIdentity <ApplicationUser, ApplicationRole>() .AddEntityFrameworkStores <ApplicationDbContext>(); services.AddInfrastructure(); var mvcBuilder = services.AddRazorPages(); #if DEBUG if (WebHostEnvironment.IsDevelopment()) { mvcBuilder.AddRazorRuntimeCompilation(); } #endif }
public void Configure(IApplicationBuilder applicationBuilder) { if (WebHostEnvironment.IsDevelopment()) { applicationBuilder .UseDeveloperExceptionPage() .UseSwagger() .UseSwaggerUI(c => c.SwaggerEndpoint($"/swagger/{SwaggerVersion}/swagger.json", SwaggerName)); } applicationBuilder .UseMiddleware <ExceptionMiddleware>() .UseHttpsRedirection() .UseRouting() .UseAuthentication() .UseAuthorization() .UseEndpoints(endpoints => endpoints.MapControllers()); }
// This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { services.AddControllersWithViews(); // In production, the React files will be served from this directory services.AddSpaStaticFiles(configuration => { configuration.RootPath = "ClientApp/build"; }); if (WebHostEnvironment.IsEnvironment("Test") || WebHostEnvironment.IsDevelopment()) { services.AddDbContext <CoronaMedContext>(opt => opt.UseInMemoryDatabase("CoronaMedDB")); } else { services.AddDbContext <CoronaMedContext>(options => options.UseSqlServer(Configuration["CoronaMed_Database"])); } #region Swagger services.AddSwaggerGen(c => { c.SwaggerDoc("v1", new OpenApiInfo { Version = "v1", Title = "CornaMed API" }); }); #endregion #region IOC //Repositories services.AddTransient <IPartnerRepository, PartnerRepository>(); //CommandHandler services.AddTransient <IPartnerCommandHandler, PartnerCommandHandler>(); #endregion }
// This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { if (WebHostEnvironment.IsDevelopment()) { services.AddCors(o => { o.AddDefaultPolicy(builder => { builder.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader(); }); }); } services.AddControllers().AddFluentValidation(); services.AddSwaggerGen(c => { c.SwaggerDoc("v1", new OpenApiInfo { Title = "Hahn.ApplicationProcess.Application", Version = "v1" }); c.ExampleFilters(); }) .AddSwaggerExamplesFromAssemblyOf <Startup>(); }
// This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { services.AddControllersWithViews(); //.AddJsonOptions(options => //{ // options.JsonSerializerOptions.PropertyNameCaseInsensitive = true; // options.JsonSerializerOptions.WriteIndented = true; //}); services.AddRazorPages(); services.AddCors(); //services.AddMvc(options => options.Filters.Add(new AuthorizeFilter())) services.AddAutoMapperProfile(); services.Configure <RouteOptions>(options => options.AppendTrailingSlash = true); var appSettingsSection = Configuration.GetSection("AppSettings"); var appSettings = appSettingsSection.Get <AppSettings>(); var mqttSettings = Configuration.GetSection("MQTTSettings").Get <MQTTSettings>(); if (WebHostEnvironment.IsDevelopment()) { mqttSettings.ClientSettings.Id = System.Guid.NewGuid().ToString(); } //string apiBaseUrlAccount = appSettings.MyWebApi.BaseUrlAccount; //string apiBaseUrlEmail = appSettings.MyWebApi.BaseUrlEmail; //string apiBaseUrlLogging = appSettings.MyWebApi.BaseUrlLogging; services.Configure <AppSettings>(appSettingsSection); //Explicitly register the settings object by delegating to the IOptions object so that it can be accessed globally via AppServicesHelper. services.AddSingleton(resolver => resolver.GetRequiredService <IOptionsMonitor <AppSettings> >().CurrentValue); services.Configure <CookiePolicyOptions>(options => { // This lambda determines whether user consent for non-essential cookies is needed for a given request. options.CheckConsentNeeded = context => false; options.MinimumSameSitePolicy = SameSiteMode.Lax; }); services.AddDistributedMemoryCache(); services.AddSession(options => { options.Cookie.HttpOnly = true; options.Cookie.IsEssential = true; options.IdleTimeout = TimeSpan.FromMinutes(15); }); //services.AddDefaultIdentity<IdentityUser>().AddRoles<IdentityRole>(); //services.AddIdentity<UserModel, RoleModel>().AddUserStore<UserModel>().AddDefaultTokenProviders(); services.AddIdentityCore <UserModel>(options => { //Password settings. options.Password.RequireDigit = true; options.Password.RequiredLength = 6; options.Password.RequireNonAlphanumeric = true; options.Password.RequireUppercase = true; options.Password.RequireLowercase = true; // Default Lockout settings. options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(5); options.Lockout.MaxFailedAccessAttempts = 5; options.Lockout.AllowedForNewUsers = true; options.User.RequireUniqueEmail = true; options.SignIn.RequireConfirmedEmail = true; }) .AddRoles <RoleModel>() .AddSignInManager() .AddDefaultTokenProviders(); //services.Configure<IdentityOptions>(options => //{ // // Default Lockout settings. // options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(5); // options.Lockout.MaxFailedAccessAttempts = 5; // options.Lockout.AllowedForNewUsers = true; //}); // configure cookie authentication. //var key = Encoding.ASCII.GetBytes(appSettings.AuthenticationOptions.Secret); //services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme) services.AddAuthentication(options => { //x.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme; //x.DefaultChallengeScheme = CookieAuthenticationDefaults.AuthenticationScheme; options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme; options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme; //x.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme; options.DefaultChallengeScheme = CookieAuthenticationDefaults.AuthenticationScheme; //x.DefaultChallengeScheme = WsFederationDefaults.AuthenticationScheme; }) .AddOpenIdConnect(options => { options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme; options.Authority = appSettings.OpenIDConnect.authority; options.ResponseType = OpenIdConnectResponseType.Code; options.UsePkce = false; //options.Scope.Clear(); //options.Scope.Add("openid"); //options.Scope.Add("profile"); //options.Scope.Add("email"); options.SaveTokens = true; // MetadataAddress represents the Active Directory instance used to authenticate users. options.MetadataAddress = appSettings.OpenIDConnect.metaData; options.ClientId = appSettings.OpenIDConnect.clientId; //options.CallbackPath = "/Account/signin-oidc"; options.EventsType = typeof(CustomOidcAuthenticationEvents); }) .AddCookie(options => { //Authentication cookie policy. options.Cookie.Name = "MyWeb.Auth"; options.ClaimsIssuer = appSettings.AuthenticationOptions.issuer; options.Cookie.HttpOnly = true; options.Cookie.IsEssential = true; options.Cookie.SecurePolicy = WebHostEnvironment.IsDevelopment() ? CookieSecurePolicy.None : CookieSecurePolicy.Always; options.Cookie.SameSite = SameSiteMode.Strict; options.AccessDeniedPath = "/Account/Denied"; options.LoginPath = "/Account/Login"; options.LogoutPath = "/Account/Logout"; //options.EventsType = typeof(CustomCookieAuthenticationEvents);//If we want to overload cookie events. Also need to put this services.AddScoped<CustomCookieAuthenticationEvents>(); }); //.AddCookie("AuthenticationTypes.Federation", options => //{ //}); //.AddIdentityCookies(options => { }); //Global cookie policy services.Configure <CookiePolicyOptions>(options => { //https://www.red-gate.com/simple-talk/dotnet/net-development/using-auth-cookies-in-asp-net-core/ options.MinimumSameSitePolicy = SameSiteMode.Strict; options.HttpOnly = HttpOnlyPolicy.None; options.Secure = WebHostEnvironment.IsDevelopment() ? CookieSecurePolicy.None : CookieSecurePolicy.Always; }); //services.ConfigureApplicationCookie(options => options.LoginPath = "/Account/Login"); //Configure authorization policies services.AddSingleton <IAuthorizationHandler, RoleAuthorizationHandler>(); services.AddAuthorizationCore(x => { //var defaultAuthorizationPolicyBuilder = new AuthorizationPolicyBuilder( // JwtBearerDefaults.AuthenticationScheme); //defaultAuthorizationPolicyBuilder = defaultAuthorizationPolicyBuilder.RequireAuthenticatedUser(); x.AddPolicy(Role.Admin, p => p.Requirements.Add(new RoleAuthorizationRequirement(appSettings.AuthenticationOptions.issuer, Role.Admin))); //x.AddPolicy(Role.Admin, p => p.Requirements.Add(new RoleAuthorizationRequirement(OidcConstants.issuer, Role.Admin))); x.AddPolicy(Role.SuperUser, p => p.Requirements.Add(new RoleAuthorizationRequirement(appSettings.AuthenticationOptions.issuer, Role.SuperUser))); x.AddPolicy(Role.CustomApp, p => p.Requirements.Add(new RoleAuthorizationRequirement(appSettings.AuthenticationOptions.issuer, Role.Admin, Role.CustomApp))); }); //Don't do this b/c then new account email confirmation tokens will also expire... leave it at the default of 24hrs. //Change all data Tokens to expire after 3 hours. https://docs.microsoft.com/en-us/aspnet/core/security/authentication/accconfirm?view=aspnetcore-3.1&tabs=visual-studio#change-all-data-protection-token-lifespans //This only affects tokens generated by the userManager and maybe the signInManager. Not the tokens we keep in our DB. //services.Configure<DataProtectionTokenProviderOptions>(options => // options.TokenLifespan = TimeSpan.FromHours(3) // ); //Additional services. services.AddHttpContextAccessor(); services.AddAccountService(); services.AddCustomAppReportService(); services.AddApplicationUserService(); services.AddApplicationRoleService(); services.AddEmailService(); services.AddApiLoggingService(); services.AddAtmosphereService(mqttSettings); services.AddTransient <ICustomAppService, CustomAppService>(); services.AddTransient <IUserStore <UserModel>, UserStore>(); services.AddTransient <IRoleStore <RoleModel>, RoleStore>(); services.AddTransient <IRazorViewToStringRenderer, RazorViewToStringRenderer>(); //services.AddScoped<CustomCookieAuthenticationEvents>(); services.AddScoped <CustomOidcAuthenticationEvents>(); }
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IOptions <AppSettings> settings, ILoggerFactory logFactory, IHttpContextAccessor accessor) { //Static services lib.ApplicationLogging.LoggerFactory = logFactory; AppServicesHelper.Services = app.ApplicationServices; //AppSettings appSettings = settings.Value; //See Middleware order: https://docs.microsoft.com/en-us/aspnet/core/fundamentals/middleware/?view=aspnetcore-3.1#middleware-order if (WebHostEnvironment.IsDevelopment()) { app.UseDeveloperExceptionPage(); } else { app.UseExceptionHandler("/Home/Error"); // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. app.UseHsts(); } app.UseHttpsRedirection(); app.UseCookiePolicy(); //The cookie policy middleware is order sensitive. app.UseRouting(); // app.UseRequestLocalization(); // app.UseCors(); //app.ConfigureCustomExceptionMiddleware(); app.UseSession(); //Needs to be after UseRouting and before UseEndpoints. Needs to be before UseAuthentication and UseHttpContextItemsMiddleware since it needs the session. app.UseAuthentication(); //By invoking the authentication middleware, you will get a HttpContext.User property. app.UseHttpContextItemsMiddleware(); //Needs to be after UseAuthentication to have the cookie claims loaded and before UseAuthorization so the session can be used. app.UseAuthorization(); ////If the user is not authenticated dont allow access to the folders that startwith the options below app.UseStaticFilesMiddleware(options => { options.AddAuthSegment("/CustomApp"); options.AddAuthSegment("/js/CustomApp"); options.AddFileExtensionContentTypeMapping(".glb", "model/gltf-binary"); options.AddFileExtensionContentTypeMapping(".webmanifest", "application/manifest+json"); options.AddFileExtensionContentTypeMapping(".vue", "application/javascript"); }); app.UseRememberMeCookieSessionMiddleware(options => { options.LoginPath = "/Account/Login"; }); app.UseEndpoints(endpoints => { endpoints.MapControllerRoute(name: "areas", pattern: "{area:exists}/{controller=Default}/{action=Index}/{id?}"); //endpoints.MapAreaControllerRoute( // "Admin", // "Admin", // "Admin/{controller=Home}/{action=Index}/{id?}"); endpoints.MapControllers(); endpoints.MapDefaultControllerRoute(); //endpoints.MapRazorPages(); //endpoints.MapControllerRoute( // "default", "{controller=Home}/{action=Index}/{id?}"); }); }
// This method gets called by the runtime. Use this method to add services to the container. // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 public void ConfigureServices(IServiceCollection services) { // required for cookies and session cookies (will throw CryptographicException without) services.AddDataProtection() .SetApplicationName("ClubSite") .SetDefaultKeyLifetime(TimeSpan.FromDays(360)) .PersistKeysToFileSystem( new DirectoryInfo(Path.Combine(WebHostEnvironment.ContentRootPath, "DataProtectionKeys"))) .UseCryptographicAlgorithms(new AuthenticatedEncryptorConfiguration() { EncryptionAlgorithm = EncryptionAlgorithm.AES_256_CBC, ValidationAlgorithm = ValidationAlgorithm.HMACSHA256 }); // Make sure we can connect to the database using (var connection = new Microsoft.Data.SqlClient.SqlConnection(Configuration.GetConnectionString("VolleyballClub"))) try { connection.Open(); } finally { connection.Close(); } services.AddMemoryCache(); // Adds a default in-memory cache implementation // Custom ClubSite db context services.AddDbContext <Data.ClubDbContext>((sp, options) => options.UseSqlServer(Configuration.GetConnectionString("VolleyballClub"))); // Piranha service setup services.AddPiranha(svcBuilder => { svcBuilder.AddRazorRuntimeCompilation = WebHostEnvironment.IsDevelopment(); svcBuilder.UseCms(); svcBuilder.UseFileStorage(naming: Piranha.Local.FileStorageNaming.UniqueFolderNames); svcBuilder.UseImageSharp(); svcBuilder.UseManager(); // https://localhost:44306/manager/ initial user: admin, pw: password svcBuilder.UseTinyMCE(); svcBuilder.UseMemoryCache(); svcBuilder.UseEF <SQLServerDb>(db => db.UseSqlServer(Configuration.GetConnectionString("VolleyballClub"))); svcBuilder.UseIdentityWithSeed <IdentitySQLServerDb>(db => db.UseSqlServer(Configuration.GetConnectionString("VolleyballClub"))); }); // MUST be before AddMvc! services.AddSession(options => { options.IdleTimeout = TimeSpan.FromMinutes(60); options.Cookie.HttpOnly = true; options.Cookie.Name = ".sid"; options.Cookie.IsEssential = true; }); services.AddRazorPages().AddPiranhaManagerOptions(); services.AddMvc() .AddSessionStateTempDataProvider() .AddMvcOptions(options => { options.ModelBindingMessageProvider.SetValueMustNotBeNullAccessor(x => string.Format(Resources.ModelBindingMessageResource.ValueMustNotBeNull)); options.ModelBindingMessageProvider.SetAttemptedValueIsInvalidAccessor((x, val) => string.Format(Resources.ModelBindingMessageResource.AttemptedValueIsInvalid, x, val)); options.ModelBindingMessageProvider.SetValueIsInvalidAccessor(x => string.Format(Resources.ModelBindingMessageResource.ValueIsInvalid, x)); options.ModelBindingMessageProvider.SetValueMustBeANumberAccessor(x => string.Format(Resources.ModelBindingMessageResource.ValueMustBeANumber, x)); options.ModelBindingMessageProvider.SetMissingKeyOrValueAccessor(() => Resources.ModelBindingMessageResource.MissingKeyOrValue); }) .AddControllersAsServices(); services.AddHttpsRedirection(options => { options.RedirectStatusCode = StatusCodes.Status301MovedPermanently; }); services.Configure <ConfigurationPoco.MailSettings>( Configuration.GetSection(nameof(ConfigurationPoco.MailSettings)) ?? throw new ArgumentNullException( $"Configuration section '{nameof(ConfigurationPoco.MailSettings)}' not found.")); services.AddTransient <Services.IMailService, Services.MailService>(); }
/// <summary> /// This method gets called by the runtime. Use this method to add services to the container. /// </summary> /// <param name="services"></param> public void ConfigureServices(IServiceCollection services) { // Add services required for using options. services.AddOptions(); #region * DataProtection service configuration * // Usage: // private readonly IDataProtector protector; // public SomeController(IDataProtectionProvider provider) // { protector = provider.CreateProtector("isolation purpose");} // public IActionResult Test(string input) // { var protectedPayload = protector.Protect(input); // var unprotectedPayload = protector.Unprotect(protectedPayload) // ...} // required for cookies and session cookies (will throw CryptographicException without) services.AddDataProtection() .SetApplicationName("League") .SetDefaultKeyLifetime(TimeSpan.FromDays(360)) .PersistKeysToFileSystem( new DirectoryInfo(Path.Combine(WebHostEnvironment.ContentRootPath, "DataProtectionKeys"))) .UseCryptographicAlgorithms(new AuthenticatedEncryptorConfiguration() { EncryptionAlgorithm = EncryptionAlgorithm.AES_256_CBC, ValidationAlgorithm = ValidationAlgorithm.HMACSHA256 }); // register the multi purpose DataProtector services.AddSingleton(typeof(League.DataProtection.DataProtector)); #endregion // Configure form upload limits services.Configure <Microsoft.AspNetCore.Http.Features.FormOptions>(fo => { fo.ValueLengthLimit = int.MaxValue; fo.MultipartBodyLengthLimit = int.MaxValue; }); // The default region of this app is "us", unless configured differently // The region info is used for country-specific data like phone numbers var regionInfo = new RegionInfo(Configuration.GetSection("RegionInfo").Value ?? "us"); services.AddSingleton <RegionInfo>(regionInfo); // The default culture of this app is "en". Supported cultures: en, de CultureInfo.DefaultThreadCurrentCulture = new CultureInfo(Configuration.GetSection("CultureInfo:Culture").Value ?? $"en-{regionInfo.TwoLetterISORegionName}"); CultureInfo.DefaultThreadCurrentUICulture = new CultureInfo(Configuration.GetSection("CultureInfo:UiCulture").Value ?? $"en-{regionInfo.TwoLetterISORegionName}"); // DO NOT USE `options => options.ResourcesPath = "..."` because then resource files in other locations won't be recognized (e.g. resx in the same folder as the controller class) services.AddLocalization(); #region **** New Multi Tenancy (since v4.3.0) ***************************** services.AddSingleton <TenantStore>(sp => { var store = (TenantStore) new TenantStore(Configuration, sp.GetRequiredService <ILogger <TournamentManager.MultiTenancy.TenantStore> >()) { GetTenantConfigurationFiles = () => { var configFolderFiles = Directory.GetFiles( Path.Combine(WebHostEnvironment.ContentRootPath, Program.ConfigurationFolder), $"Tenant.*.{WebHostEnvironment.EnvironmentName}.config", SearchOption.TopDirectoryOnly).ToList(); if (WebHostEnvironment.IsDevelopment()) { configFolderFiles.AddRange(Directory.GetFiles( Path.Combine(Program.GetSecretsFolder()), $"Tenant.*.{WebHostEnvironment.EnvironmentName}.config", SearchOption.TopDirectoryOnly)); } Logger.LogInformation("Tenant config files: {Config}", configFolderFiles); return(configFolderFiles.ToArray()); } }.LoadTenants(); var tenants = store.GetTenants().Values.ToList(); if (!tenants.Any(t => t.IsDefault)) { throw new Exception("No default tenant configuration found."); } tenants.ForEach(t => { if (string.IsNullOrWhiteSpace(t.DbContext.ConnectionString)) { throw new Exception($"Tenant '{t.Identifier}': Connection string for key '{t.DbContext.ConnectionKey}' not found."); } }); ConfigureLlblgenPro(store); return(store); }); services.AddScoped <MultiTenancy.TenantResolver>(); services.AddScoped <TournamentManager.MultiTenancy.ITenantContext>(sp => sp.GetRequiredService <MultiTenancy.TenantResolver>().Resolve()); #endregion services.Configure <IISOptions>(options => { }); services.AddSingleton <IHttpContextAccessor, HttpContextAccessor>(); services.AddSingleton <IActionContextAccessor, ActionContextAccessor>(); // Make UrlHelper injectable to any component in the HttpContext services.AddScoped <IUrlHelper>(sp => { var actionContext = sp.GetRequiredService <IActionContextAccessor>().ActionContext; var factory = sp.GetRequiredService <IUrlHelperFactory>(); return(factory.GetUrlHelper(actionContext)); }); services.AddSingleton <IConfiguration>(Configuration); services.AddMemoryCache(); // Adds a default in-memory cache implementation // MUST be before AddMvc! services.AddSession(options => { options.IdleTimeout = TimeSpan.FromMinutes(60); options.Cookie.HttpOnly = true; options.Cookie.Name = ".sid"; options.Cookie.IsEssential = true; }); #region ** Identity and Authentication ** var socialLogins = Configuration.GetSection(nameof(SocialLogins)).Get <SocialLogins>(); services.AddAuthentication(options => { options.DefaultScheme = IdentityConstants.ApplicationScheme; options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme; options.DefaultChallengeScheme = CookieAuthenticationDefaults.AuthenticationScheme; options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme; }) .AddFacebook(options => { options.AppId = socialLogins.Facebook.AppId; options.AppSecret = socialLogins.Facebook.AppSecret; options.CallbackPath = new PathString("/signin-facebook"); // this path is used by the middleware only, no route necessary // add the facebook picture url as an additional claim options.ClaimActions.MapJsonKey("urn:facebook:picture", "picture", "picture.data.url"); options.SaveTokens = true; options.CorrelationCookie.Name = ".CorrAuth.League"; options.Events.OnRemoteFailure = context => { // Note: If this delegate is missing, errors with the external login lead to a System.Exception: access_denied;Description=Permissions error var qsParameter = new Dictionary <string, string> { { "remoteError", context.Request.Query["error"].ToString() }, }.Where(item => !string.IsNullOrEmpty(item.Value)).ToDictionary(i => i.Key, i => i.Value); // joins query strings from RedirectUri and qsParameter var redirectUri = QueryHelpers.AddQueryString(context.Properties?.RedirectUri ?? "/", qsParameter); context.Response.Redirect(redirectUri); context.HandleResponse(); return(Task.CompletedTask); }; }) .AddGoogle(options => { options.ClientId = socialLogins.Google.ClientId; options.ClientSecret = socialLogins.Google.ClientSecret; options.CallbackPath = new PathString("/signin-google"); // this path is used by the middleware only, no route necessary options.ClaimActions.MapJsonKey("urn:google:picture", "picture", "url");; options.SaveTokens = true; options.CorrelationCookie.Name = ".CorrAuth.League"; options.Events.OnRemoteFailure = context => { // Note: If this delegate is missing, errors with the external login lead to a System.Exception: access_denied;Description=Permissions error var qsParameter = new Dictionary <string, string> { { "remoteError", context.Request.Query["error"].ToString() }, }.Where(item => !string.IsNullOrEmpty(item.Value)).ToDictionary(i => i.Key, i => i.Value); // joins query strings from RedirectUri and qsParameter var redirectUri = QueryHelpers.AddQueryString(context.Properties?.RedirectUri ?? "/", qsParameter); context.Response.Redirect(redirectUri); context.HandleResponse(); return(Task.CompletedTask); }; }) .AddMicrosoftAccount(options => { options.ClientId = socialLogins.Microsoft.ClientId; options.ClientSecret = socialLogins.Microsoft.ClientSecret; options.CallbackPath = new PathString("/signin-microsoft"); // this path is used by the middleware only, no route necessary options.SaveTokens = true; options.CorrelationCookie.Name = ".CorrAuth.League"; options.Events.OnRemoteFailure = context => { // Note: If this delegate is missing, errors with the external login lead to a System.Exception: access_denied;Description=Permissions error var qsParameter = new Dictionary <string, string> { { "remoteError", context.Request.Query["error"].ToString() }, }.Where(item => !string.IsNullOrEmpty(item.Value)).ToDictionary(i => i.Key, i => i.Value); // joins query strings from RedirectUri and qsParameter var redirectUri = QueryHelpers.AddQueryString(context.Properties?.RedirectUri ?? "/", qsParameter); context.Response.Redirect(redirectUri); context.HandleResponse(); return(Task.CompletedTask); }; }); // Add before Application and External Cookie configuration and ConfigureApplicationCookie services.AddIdentity <ApplicationUser, ApplicationRole>(options => { Configuration.Bind("IdentityOptions", options); // bind to IdentityOptions section of appsettings.json }) .AddDefaultTokenProviders() .AddUserStore <UserStore>() .AddRoleStore <RoleStore>() .AddErrorDescriber <MultiLanguageIdentityErrorDescriber>() .AddUserValidator <LeagueUserValidator <ApplicationUser> >(); // on top of default user validator // Make custom claims be added to the ClaimsPrincipal services.AddScoped <IUserClaimsPrincipalFactory <ApplicationUser>, LeagueClaimsPrincipalFactory>(); // Defines the lifetime of tokens sent to users for email / password confirmation et al services.Configure <DataProtectionTokenProviderOptions>(options => options.TokenLifespan = TimeSpan.FromDays(3)); // default: 1 day // Add for required user name length et al services.Configure <LeagueUserValidatorOptions>(Configuration.GetSection(nameof(LeagueUserValidatorOptions))); #endregion #region *** Authorization *** services.AddAuthorization(options => { // Used on controller method level options.AddPolicy(Authorization.PolicyName.MatchPolicy, policy => policy.RequireRole(Identity.Constants.RoleName.SystemManager, Identity.Constants.RoleName.TournamentManager, Identity.Constants.RoleName.TeamManager)); // Used in team views options.AddPolicy(Authorization.PolicyName.SeeTeamContactsPolicy, policy => policy.RequireRole(Identity.Constants.RoleName.SystemManager, Identity.Constants.RoleName.TournamentManager, Identity.Constants.RoleName.TeamManager, Identity.Constants.RoleName.Player)); // Used for my team views options.AddPolicy(Authorization.PolicyName.MyTeamPolicy, policy => policy.RequireRole(Identity.Constants.RoleName.SystemManager, Identity.Constants.RoleName.TournamentManager, Identity.Constants.RoleName.TeamManager, Identity.Constants.RoleName.Player)); options.AddPolicy(Authorization.PolicyName.MyTeamAdminPolicy, policy => policy.RequireRole(Identity.Constants.RoleName.SystemManager, Identity.Constants.RoleName.TournamentManager)); }); // Handler for match date, venue and result authorization services.AddSingleton <IAuthorizationHandler, Authorization.MatchAuthorizationHandler>(); // Handler for team, team venue, team members authorization services.AddSingleton <IAuthorizationHandler, Authorization.TeamAuthorizationHandler>(); // Handler for venue authorization services.AddSingleton <IAuthorizationHandler, Authorization.VenueAuthorizationHandler>(); #endregion #region ** Application and External Cookie configuration ** services.Configure <SecurityStampValidatorOptions>(options => { // This determines the time span after which the validity of the authentication cookie // will be checked against persistent storage. It is accomplished by calling the // SecurityStampValidator for every request to the server. If the current time minus the // cookie's issue time is less or equal to ValidationInterval, a call to // SignInManager<TUser>.ValidateSecurityStampAsync will occur. This means ValidationInterval = TimeSpan.Zero // leads to calling the ValidateSecurityStampAsync for each request. options.ValidationInterval = TimeSpan.FromMinutes(15); // default: 30 minutes }); services.ConfigureApplicationCookie(options => { // Cookie settings options.Cookie.HttpOnly = true; options.Cookie.IsEssential = true; options.CookieManager = new LeagueCookieManager(); options.Cookie.Name = ".Auth"; // will be set by LeagueCookieManager options.Cookie.Path = "/"; // may be set by LeagueCookieManager options.LoginPath = new PathString("/account/sign-in"); options.LogoutPath = new PathString("/account/sign-in"); options.AccessDeniedPath = new PathString("/error/access-denied"); options.Cookie.SameSite = Microsoft.AspNetCore.Http.SameSiteMode.Lax; // don't use Strict here options.Cookie.SecurePolicy = CookieSecurePolicy.SameAsRequest; options.ExpireTimeSpan = TimeSpan.FromDays(30); options.SlidingExpiration = true; options.Events.OnRedirectToAccessDenied = context => { var returnUrl = "?ReturnUrl=" + context.Request.Path + context.Request.QueryString; // fires with [Authorize] attribute, when the user is authenticated, but does not have enough privileges var tenantContext = context.HttpContext.RequestServices.GetRequiredService <ITenantContext>(); // other context properties can be set, but are not considered in the redirect, though context.Response.Redirect(new PathString($"/{tenantContext.SiteContext.UrlSegmentValue}").Add(context.Options.AccessDeniedPath) + returnUrl); return(Task.CompletedTask); }; options.Events.OnRedirectToLogin = context => { var returnUrl = "?ReturnUrl=" + context.Request.Path + context.Request.QueryString; // fires with [Authorize] attribute, when the user is not authenticated var tenantContext = context.HttpContext.RequestServices.GetRequiredService <ITenantContext>(); // other context properties can be set, but are not considered in the redirect, though context.Response.Redirect(new PathString($"/{tenantContext.SiteContext.UrlSegmentValue}").Add(context.Options.LoginPath) + returnUrl); return(Task.CompletedTask); }; options.Events.OnRedirectToLogout = context => { var tenantContext = context.HttpContext.RequestServices.GetRequiredService <ITenantContext>(); context.Response.Redirect(new PathString($"/{tenantContext.SiteContext.UrlSegmentValue}").Add(context.Options.LogoutPath)); return(Task.CompletedTask); }; options.Events.OnSignedIn += async context => { var tenantContext = context.HttpContext.RequestServices.GetRequiredService <ITenantContext>(); var success = await tenantContext.DbContext.AppDb.UserRepository.SetLastLoginDateAsync(context.Principal.Identity.Name, null, CancellationToken.None); }; }); services.ConfigureExternalCookie(options => { options.Cookie.HttpOnly = true; options.Cookie.IsEssential = true; // MUST use the default options.CookieManager: After callback from social login, "organization" url segment will not be set, // because the CallbackPath from the provider will be sth. like "/signin-facebook" with cookie path to the same path. options.Cookie.Name = ".ExtAuth.League"; options.Cookie.Path = "/"; options.LoginPath = new PathString("/account/sign-in"); options.LogoutPath = new PathString("/account/sign-in"); options.AccessDeniedPath = new PathString("/account/access-denied"); options.Cookie.SameSite = Microsoft.AspNetCore.Http.SameSiteMode.Lax; // don't use Strict here options.ExpireTimeSpan = TimeSpan.FromDays(30); options.SlidingExpiration = true; options.Events.OnRedirectToAccessDenied = context => { var returnUrl = "?ReturnUrl=" + context.Request.Path + context.Request.QueryString; // fires with [Authorize] attribute, when the user is authenticated, but does not have enough privileges var tenantContext = context.HttpContext.RequestServices.GetRequiredService <ITenantContext>(); // other context properties can be set, but are not considered in the redirect, though context.Response.Redirect(new PathString($"/{tenantContext.SiteContext.UrlSegmentValue}").Add(context.Options.AccessDeniedPath) + returnUrl); return(Task.CompletedTask); }; options.Events.OnRedirectToLogin = context => { var returnUrl = "?ReturnUrl=" + context.Request.Path + context.Request.QueryString; // fires with [Authorize] attribute, when the user is not authenticated var tenantContext = context.HttpContext.RequestServices.GetRequiredService <ITenantContext>(); // other context properties can be set, but are not considered in the redirect, though context.Response.Redirect(new PathString($"/{tenantContext.SiteContext.UrlSegmentValue}").Add(context.Options.LoginPath) + returnUrl); return(Task.CompletedTask); }; options.Events.OnRedirectToLogout = context => { var tenantContext = context.HttpContext.RequestServices.GetRequiredService <ITenantContext>(); context.Response.Redirect(new PathString($"/{tenantContext.SiteContext.UrlSegmentValue}").Add(context.Options.LogoutPath)); return(Task.CompletedTask); }; }); #endregion #region *** MailMergeLib as a service *** services.AddMailMergeService( options => { options.Settings = Settings.Deserialize( Path.Combine(WebHostEnvironment.ContentRootPath, Program.ConfigurationFolder, $@"MailMergeLib.{WebHostEnvironment.EnvironmentName}.config"), System.Text.Encoding.UTF8); var fms = FileMessageStore.Deserialize(Path.Combine(WebHostEnvironment.ContentRootPath, Program.ConfigurationFolder, "MailMergeLibMessageStore.config"), System.Text.Encoding.UTF8); for (var i = 0; i < fms.SearchFolders.Length; i++) { // make relative paths absolute - ready to use fms.SearchFolders[i] = Path.Combine(WebHostEnvironment.WebRootPath, fms.SearchFolders[i]); } options.MessageStore = fms; }); #endregion #region ** Timezone service per request ** services.AddSingleton <NodaTime.TimeZones.DateTimeZoneCache>(sp => new NodaTime.TimeZones.DateTimeZoneCache(NodaTime.TimeZones.TzdbDateTimeZoneSource.Default)); var tzId = Configuration.GetSection("TimeZone").Value ?? "America/New_York"; // TimeZoneConverter will use the culture of the current scope services.AddScoped(sp => new Axuno.Tools.DateAndTime.TimeZoneConverter( sp.GetRequiredService <NodaTime.TimeZones.DateTimeZoneCache>(), tzId, CultureInfo.CurrentCulture, NodaTime.TimeZones.Resolvers.LenientResolver)); #endregion #region ** Phone number service ** services.AddSingleton <TournamentManager.DI.PhoneNumberService>(sp => new PhoneNumberService(PhoneNumbers.PhoneNumberUtil.GetInstance())); #endregion services.AddSingleton <Helpers.MetaDataHelper>(); services.Configure <CookiePolicyOptions>(options => { // determines whether user consent for non-essential cookies is needed for a given request. options.CheckConsentNeeded = context => false; options.MinimumSameSitePolicy = Microsoft.AspNetCore.Http.SameSiteMode.None; }); // expand search path to organization key sub-paths per HttpRequest services.Configure <RazorViewEngineOptions>(options => { options.ViewLocationExpanders.Add(new Views.LeagueViewLocationExpander()); // R# will not resolve // R# will resolve: options.ViewLocationFormats.Add("/Views/Path/{0}.cshtml"); }); #region *** Request Localization *** if (bool.TryParse(Configuration.GetSection("CultureInfo:CulturePerRequest").Value, out var isCulturePerRequest) && isCulturePerRequest) { var supportedCultures = new[] { new CultureInfo("en"), new CultureInfo("de") }; services.Configure <RequestLocalizationOptions>(options => { options.DefaultRequestCulture = new RequestCulture(CultureInfo.DefaultThreadCurrentCulture); // Formatting numbers, dates, etc. options.SupportedCultures = supportedCultures; // UI strings that we have localized. options.SupportedUICultures = supportedCultures; // e.g.: "en-US" => "en" options.FallBackToParentCultures = true; options.FallBackToParentUICultures = true; // Select the CookieRequestCultureProvider from the default RequestCultureProviders // and set another cookie name than CookieRequestCultureProvider.DefaultCookieName var cookieProvider = options.RequestCultureProviders .OfType <CookieRequestCultureProvider>() .FirstOrDefault(); if (cookieProvider != null) { cookieProvider.CookieName = ".PreferredLanguage"; } }); } #endregion services.AddRouting(options => { options.ConstraintMap.Add(TenantRouteConstraint.Name, typeof(TenantRouteConstraint)); options.LowercaseQueryStrings = false; // true does not work for UrlBase64 encoded strings! options.LowercaseUrls = true; options.AppendTrailingSlash = false; } ); services.AddRazorPages().AddRazorPagesOptions(options => { }); var mvcBuilder = services.AddMvc(options => { options.EnableEndpointRouting = true; // Add model binding messages for errors that do not reach data annotation validation options.ModelBindingMessageProvider.SetValueMustNotBeNullAccessor(x => string.Format(Resources.ModelBindingMessageResource.ValueMustNotBeNull)); options.ModelBindingMessageProvider.SetAttemptedValueIsInvalidAccessor((x, val) => string.Format(Resources.ModelBindingMessageResource.AttemptedValueIsInvalid, x, val)); options.ModelBindingMessageProvider.SetValueIsInvalidAccessor(x => string.Format(Resources.ModelBindingMessageResource.ValueIsInvalid, x)); options.ModelBindingMessageProvider.SetValueMustBeANumberAccessor(x => string.Format(Resources.ModelBindingMessageResource.ValueMustBeANumber, x)); options.ModelBindingMessageProvider.SetMissingKeyOrValueAccessor(() => Resources.ModelBindingMessageResource.MissingKeyOrValue); }) .SetCompatibilityVersion(CompatibilityVersion.Latest) .AddSessionStateTempDataProvider() .AddDataAnnotationsLocalization() .AddViewLocalization(LanguageViewLocationExpanderFormat.Suffix) .AddMvcOptions(options => { // Insert custom model binder providers before SimpleTypeModelBinderProvider options.ModelBinderProviders.Insert(0, new TimeSpanModelBinderProvider()); options.ModelBinderProviders.Insert(0, new DateTimeModelBinderProvider()); // Replace ComplexTypeModelBinder with TrimmingModelBinder (trims all strings in models) options.ModelBinderProviders[options.ModelBinderProviders.TakeWhile(p => !(p is Microsoft.AspNetCore.Mvc.ModelBinding.Binders.ComplexTypeModelBinderProvider)).Count()] = new ModelBinders.TrimmingComplexModelBinderProvider(); }) .AddControllersAsServices(); // will add controllers with ServiceLifetime.Transient #if DEBUG // Not to be added in production! if (WebHostEnvironment.IsDevelopment()) { mvcBuilder.AddRazorRuntimeCompilation(); } #endif services.AddHttpsRedirection(options => { options.RedirectStatusCode = StatusCodes.Status301MovedPermanently; }); #region *** Add CloudScribeNavigation *** // CloudscribeNavigation requires: // ~/Views/Shared/NavigationNodeChildDropdownPartial.cshtml // ~/Views/Shared/NavigationNodeChildTreePartial.cshtml // ~/Views/Shared/NavigationNodeSideNavPartial.cshtml // ~/Views/Shared/Components/Navigation/*.cshtml // ~/Views/_ViewImports.cshtml: @using cloudscribe.Web.Navigation //services.AddCloudscribeNavigation(Configuration.GetSection("NavigationOptions")); services.AddCloudscribeNavigation(null); services.AddScoped <IOptions <NavigationOptions>, Navigation.LeagueSiteNavigationOptionsResolver>(); // resolve navigation xml files per organization services.AddScoped <INavigationTreeBuilder, Navigation.HomeNavigationTreeBuilder>(); //add top nav home button per tenant services.AddScoped <INavigationTreeBuilder, Navigation.LeaguesNavigationTreeBuilder>(); //add top nav item for leagues per tenant services.AddScoped <INavigationTreeBuilder, Navigation.InfosNavigationTreeBuilder>(); //add top nav item for info menu per tenant services.AddScoped <ITreeCache, Navigation.LeagueMemoryTreeCache>(); // cache navigation tree per tenant #endregion #region *** Text Templating *** services.AddTextTemplatingModule(vfs => { // The complete Templates folder is embedded in the project file vfs.FileSets.AddEmbedded <Startup>(nameof(League) + ".Templates"); // vfs.FileSets.AddPhysical(Path.Combine(Directory.GetCurrentDirectory(), "Templates")); }, locOpt => { }, renderOptions => { #if DEBUG renderOptions.MemberNotFoundAction = RenderErrorAction.ThrowError; renderOptions.VariableNotFoundAction = RenderErrorAction.ThrowError; #else renderOptions.MemberNotFoundAction = RenderErrorAction.MaintainToken; renderOptions.VariableNotFoundAction = RenderErrorAction.MaintainToken; #endif }); #endregion #region *** HostedServices related *** // RazorViewToStringRenderer must be used with the current HttpContext // Note: RazorViewToStringRenderer is currently only used for MatchReport services.AddTransient <RazorViewToStringRenderer>(); services.Configure <BackgroundQueueConfig>(config => config.OnException = null); services.AddSingleton <IBackgroundQueue, BackgroundQueue>(); services.AddConcurrentBackgroundQueueService(); services.AddTransient <SendEmailTask>(); services.AddTransient <RankingUpdateTask>(); #endregion }
/// <summary> /// 注入服务 /// </summary> /// <param name="services"></param> public void ConfigureServices(IServiceCollection services) { services.Configure <CoreWebSite>(Configuration.GetSection("CoreWebSite")); services.Configure <CoreUpload>(Configuration.GetSection("CoreUpload")); services.AddCommonService() .AddRepositoryService() .AddDomainService(); var assmebies = new System.IO.DirectoryInfo(AppDomain.CurrentDomain.BaseDirectory).GetFiles("Core.Application*.dll") .Select(x => System.Reflection.Assembly.LoadFrom(x.FullName)).ToArray(); services.AddMvc(option => { }).AddApplicationPart(assmebies.FirstOrDefault()) .AddRazorOptions(options => { options.AreaViewLocationFormats.Add("/Core/Admin/{1}/{0}.cshtml"); options.AreaViewLocationFormats.Add("/Core/Admin/{0}.cshtml"); options.AreaViewLocationFormats.Add("/Core/Admin/Shared/{0}.cshtml"); options.ViewLocationFormats.Add("/Core/Mobile/{1}/{0}.cshtml"); options.ViewLocationFormats.Add("/Core/Mobile/{0}.cshtml"); options.ViewLocationFormats.Add("/Core/Mobile/Shared/{0}.cshtml"); }).AddJsonOptions(option => { option.JsonSerializerOptions.Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping; }); services.AddDbContext <BaseDbContext>(option => { var connectionString = Configuration.GetConnectionString("Core"); option.UseSqlServer(connectionString, b => b.UseRowNumberForPaging());//分页增加对sqlserver2008的支持 var logFactory = new LoggerFactory(); logFactory.AddLog4Net(); option.UseLoggerFactory(logFactory); //添加sql监控日志 }); //初始化数据库连接 services.AddAutoMapper(AppDomain.CurrentDomain.GetAssemblies()); //添加对象映射组件 services.AddHttpContextAccessor(); services.AddAuthentication(option => { option.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme; option.RequireAuthenticatedSignIn = false; }).AddCookie(option => { option.LoginPath = "/User/Login"; option.LogoutPath = "/User/LoginOut"; option.Events.OnSigningIn = (context) => { var cookieName = context.Options.Cookie.Name; return(Task.CompletedTask); }; }); services.AddSession(); if (WebHostEnvironment.IsDevelopment()) { services.AddMemoryCache(); } else { services.AddDistributedRedisCache(option => { option.Configuration = "127.0.0.1"; option.InstanceName = "db0"; }); } services.AddUEditorService(); services.AddDapper("sqlserver", option => { option.ConnectionString = Configuration.GetConnectionString("Core"); option.DBType = CoreEnum.DBType.SqlServer; }); ServiceCollection = services; }