namespace Microsoft.eShopOnContainers.Services.Catalog.API { using Autofac; using Autofac.Extensions.DependencyInjection; using global::Catalog.API.Infrastructure.Filters; using global::Catalog.API.IntegrationEvents; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.Azure.ServiceBus; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Diagnostics; using Microsoft.eShopOnContainers.BuildingBlocks.EventBus; using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; using Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ; using Microsoft.eShopOnContainers.BuildingBlocks.EventBusServiceBus; using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF; using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF.Services; using Microsoft.eShopOnContainers.Services.Catalog.API.Infrastructure; using Microsoft.eShopOnContainers.Services.Catalog.API.IntegrationEvents.EventHandling; using Microsoft.eShopOnContainers.Services.Catalog.API.IntegrationEvents.Events; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.HealthChecks; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using RabbitMQ.Client; using System; using System.Data.Common; using System.Reflection; public class Startup { public Startup(IConfiguration configuration) { Configuration = configuration; } public IConfiguration Configuration { get; } public IServiceProvider ConfigureServices(IServiceCollection services) { // Add framework services. services.AddHealthChecks(checks => { var minutes = 1; if (int.TryParse(Configuration["HealthCheck:Timeout"], out var minutesParsed)) { minutes = minutesParsed; } checks.AddSqlCheck("CatalogDb", Configuration["ConnectionString"], TimeSpan.FromMinutes(minutes)); var accountName = Configuration.GetValue<string>("AzureStorageAccountName"); var accountKey = Configuration.GetValue<string>("AzureStorageAccountKey"); if (!string.IsNullOrEmpty(accountName) && !string.IsNullOrEmpty(accountKey)) { checks.AddAzureBlobStorageCheck(accountName, accountKey); } }); services.AddMvc(options => { options.Filters.Add(typeof(HttpGlobalExceptionFilter)); }).AddControllersAsServices(); services.AddDbContext<CatalogContext>(options => { options.UseSqlServer(Configuration["ConnectionString"], sqlServerOptionsAction: sqlOptions => { sqlOptions.MigrationsAssembly(typeof(Startup).GetTypeInfo().Assembly.GetName().Name); //Configuring Connection Resiliency: https://docs.microsoft.com/en-us/ef/core/miscellaneous/connection-resiliency sqlOptions.EnableRetryOnFailure(maxRetryCount: 5, maxRetryDelay: TimeSpan.FromSeconds(30), errorNumbersToAdd: null); }); // Changing default behavior when client evaluation occurs to throw. // Default in EF Core would be to log a warning when client evaluation is performed. options.ConfigureWarnings(warnings => warnings.Throw(RelationalEventId.QueryClientEvaluationWarning)); //Check Client vs. Server evaluation: https://docs.microsoft.com/en-us/ef/core/querying/client-eval }); services.AddDbContext<IntegrationEventLogContext>(options => { options.UseSqlServer(Configuration["ConnectionString"], opts => opts.MigrationsAssembly("Catalog.API")); }); services.Configure<CatalogSettings>(Configuration); // Add framework services. services.AddSwaggerGen(options => { options.DescribeAllEnumsAsStrings(); options.SwaggerDoc("v1", new Swashbuckle.AspNetCore.Swagger.Info { Title = "eShopOnContainers - Catalog HTTP API", Version = "v1", Description = "The Catalog Microservice HTTP API. This is a Data-Driven/CRUD microservice sample", TermsOfService = "Terms Of Service" }); }); services.AddCors(options => { options.AddPolicy("CorsPolicy", builder => builder.AllowAnyOrigin() .AllowAnyMethod() .AllowAnyHeader() .AllowCredentials()); }); services.AddTransient<Func<DbConnection, IIntegrationEventLogService>>( sp => (DbConnection c) => new IntegrationEventLogService(c)); services.AddTransient<ICatalogIntegrationEventService, CatalogIntegrationEventService>(); if (Configuration.GetValue<bool>("AzureServiceBusEnabled")) { services.AddSingleton<IServiceBusPersisterConnection>(sp => { var settings = sp.GetRequiredService<IOptions<CatalogSettings>>().Value; var logger = sp.GetRequiredService<ILogger<DefaultServiceBusPersisterConnection>>(); var serviceBusConnection = new ServiceBusConnectionStringBuilder(settings.EventBusConnection); return new DefaultServiceBusPersisterConnection(serviceBusConnection, logger); }); } else { services.AddSingleton<IRabbitMQPersistentConnection>(sp => { var settings = sp.GetRequiredService<IOptions<CatalogSettings>>().Value; var logger = sp.GetRequiredService<ILogger<DefaultRabbitMQPersistentConnection>>(); var factory = new ConnectionFactory() { HostName = Configuration["EventBusConnection"] }; if (!string.IsNullOrEmpty(Configuration["EventBusUserName"])) { factory.UserName = Configuration["EventBusUserName"]; } if (!string.IsNullOrEmpty(Configuration["EventBusPassword"])) { factory.Password = Configuration["EventBusPassword"]; } return new DefaultRabbitMQPersistentConnection(factory, logger); }); } RegisterEventBus(services); var container = new ContainerBuilder(); container.Populate(services); return new AutofacServiceProvider(container.Build()); } public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) { //Configure logs loggerFactory.AddConsole(Configuration.GetSection("Logging")); loggerFactory.AddDebug(); var pathBase = Configuration["PATH_BASE"]; if (!string.IsNullOrEmpty(pathBase)) { loggerFactory.CreateLogger("init").LogDebug($"Using PATH BASE '{pathBase}'"); app.UsePathBase(pathBase); } app.UseCors("CorsPolicy"); app.UseMvcWithDefaultRoute(); app.UseSwagger() .UseSwaggerUI(c => { c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1"); }); ConfigureEventBus(app); } private void RegisterEventBus(IServiceCollection services) { if (Configuration.GetValue<bool>("AzureServiceBusEnabled")) { services.AddSingleton<IEventBus, EventBusServiceBus>(sp => { var serviceBusPersisterConnection = sp.GetRequiredService<IServiceBusPersisterConnection>(); var iLifetimeScope = sp.GetRequiredService<ILifetimeScope>(); var logger = sp.GetRequiredService<ILogger<EventBusServiceBus>>(); var eventBusSubcriptionsManager = sp.GetRequiredService<IEventBusSubscriptionsManager>(); var subscriptionClientName = Configuration["SubscriptionClientName"]; return new EventBusServiceBus(serviceBusPersisterConnection, logger, eventBusSubcriptionsManager, subscriptionClientName, iLifetimeScope); }); } else { services.AddSingleton<IEventBus, EventBusRabbitMQ>(); } services.AddSingleton<IEventBusSubscriptionsManager, InMemoryEventBusSubscriptionsManager>(); services.AddTransient<OrderStatusChangedToAwaitingValidationIntegrationEventHandler>(); services.AddTransient<OrderStatusChangedToPaidIntegrationEventHandler>(); } protected virtual void ConfigureEventBus(IApplicationBuilder app) { var eventBus = app.ApplicationServices.GetRequiredService<IEventBus>(); eventBus.Subscribe<OrderStatusChangedToAwaitingValidationIntegrationEvent, OrderStatusChangedToAwaitingValidationIntegrationEventHandler>(); eventBus.Subscribe<OrderStatusChangedToPaidIntegrationEvent, OrderStatusChangedToPaidIntegrationEventHandler>(); } } }