Startup Code - WebAPI

Flexbase Solution Structure

Lets understand the startup file that is given by default in the hosting layer.

Particularly the WebAPI startup file. We go back to the solution and then to the startup file.

For a startup, the Swagger is configured for the specific startup. All the default configurations are there for the Swagger.

services.AddSwaggerGen(c =>
            {
                c.SwaggerDoc("v1", new OpenApiInfo
                {
                    Version = "v1",
                    Title = "ECommerceDemo.EndPoint.WebAPI",
                    Description = "ECommerceDemo.EndPoint.WebAPI",
                    TermsOfService = new Uri("https://example.com/terms"),
                    Contact = new OpenApiContact
                    {
                        Name = "Your Project",
                        Email = string.Empty,
                        Url = new Uri("http://www.yourwebsite.com"),
                    },
                    License = new OpenApiLicense
                    {
                        Name = "Use under LICX",
                        Url = new Uri("http://www.yourwebsite.com/license"),
                    }
                });
                c.CustomSchemaIds(x => x.FullName);
            });

Then we have FlexBase registered with the services. This will register all the Flexbase dependency with the DI container. Along with that, we give the ‘EcommerceDemo*.dll’ that is starting with e-commerce. We need the assembly there also, and you can have multiple assemblies.

private void RegisterFlexServices(IServiceCollection services)
        {
            services.AddFlexBase(new List<AssembliesToLoad> { new AssembliesToLoad("ECommerceDemo*.dll") });

            //Add PK Generator
            services.AddTransient<IFlexPrimaryKeyGeneratorBridge, SequentialGuidPrimaryKeyGeneratorBridge>();
            services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
            services.AddTransient<IFlexHostHttpContextAccesorBridge, FlexDefaultHttpContextAccessorBridge>();

            services.AddAutoMapper(typeof(CoreMapperConfiguration).Assembly,typeof(HttpContextInfoAutomapperConfiguration).Assembly);

            ConfigureBusServices(services);
            ConfigureDbServices(services);

        }

Then we have the PrimaryKeyGeneratorBridge as a sequential GUID primary key. We will discuss that when we go into Crud operations. To configure the HTTP context accessor, it needs to be configured Then there is HttpContextAccessorr which we will see in detail. These are all required dependencies for Flex to work.

The AutoMapper is configured,

And then we have the default database configuration.

private void ConfigureDbServices(IServiceCollection services)
        {

            //Default Settings for connection provider
            services.AddTransient<IWriteDbConnectionProviderBridge, AppSettingsWriteDbConnectionProvider>();
            services.AddTransient<IReadDbConnectionProviderBridge, AppSettingsReadDbConnectionProvider>();

            //Configure multitenant application
            //services.AddTransient<IWriteDbConnectionProviderBridge, NativeWriteDbTenantConnectionProvider>();
            //services.AddTransient<IReadDbConnectionProviderBridge, NativeReadDbTenantConnectionProvider>();
            //services.AddTransient<IFlexNativeHostTenantProviderBridge, FlexNativeHostTenantProviderBridge>();

            services.AddTransient<FlexEFDbContext, ApplicationEFDbContext>();
            
            services.AddTransient<IFlexRepositoryBridge, FlexWriteDbRepositoryEFSQLServer>();
            services.AddTransient<IFlexQueryRepositoryBridge, FlexReadDbRepositoryEFSQLServer>();
        }

The EFDbContext is registered with the container and the FlexRepositoryBridge and FlexQueryRepositoryBridge which is used for the write operations and query operations, respectively. Both the repositories can be kept separate. If there is a need for only one database then the connection string needs to be kept same for both the connection providers.

One can configure their own repository based on the needs. Most of it is done by default for the ease of the user.

Then we have the configuration for Bus services.

private void ConfigureBusServices(IServiceCollection services)
        {
            List<BusRouteConfig> routes = new List<BusRouteConfig>();

            //TODO: Set the default destination address
            string defaultDestinationEndPoint = "ECommerceDemo-EndPoint-Handlers";

            //TODO: Uncomment this line of code and Configure your route
            //routes.Add(new BusRouteConfig(typeof(OneOfYourCommand).Assembly, defaultDestinationEndPoint));

            Guard.AgainstNullAndEmpty("End point name cannot be empty", _configuration.GetSection("FlexBase")["EndPoint"]);
            string endPointName = _configuration.GetSection("FlexBase")["EndPoint"];

            //You can change your Bus implementation here

            FlexBusGammaDefaultEndpointConfiguration endPointConfig = null;

            if (_env.IsDevelopment())
            {
                endPointConfig = new LearningBusConfiguration(endPointName, routes);
            }
            if (_env.IsStaging())
            {
                Guard.AgainstNullAndEmpty("AzureBusStorageConnectionString for connection string cannot be empty", _configuration.GetSection("FlexBase")["AzureStorageConnectionString"]);
                string azureConnectionString = _configuration.GetSection("FlexBase")["AzureStorageConnectionString"];
                endPointConfig = new AzureBusConfiguration(endPointName, routes, azureConnectionString, azureConnectionString);
            }
            if (_env.IsProduction())
            {
                Guard.AgainstNullAndEmpty("AzureBusStorageConnectionString for connection string cannot be empty", _configuration.GetSection("FlexBase")["AzureStorageConnectionString"]);
                string azureConnectionString = _configuration.GetSection("FlexBase")["AzureStorageConnectionString"];
                endPointConfig = new AzureBusConfiguration(endPointName, routes, azureConnectionString, azureConnectionString);
            }

            services.InitializeBusGammaWithExternallyManagedServiceProvider(endPointConfig);

            services.AddSingleton<IFlexServiceBusBridge>(bus => new BusGammaServiceBusBridge(endPointConfig));
        }

The first thing that needs to be done is the routing. There's only one configuration that needs to be done is the route which configures the route for one of your command. We will see that when we configure for our Crud operations. All the Bus configuration as explained earlier is configured and the Flex Bus Service Bridges added to the dependency container.

That's all about the configuration startup. In the program.cs, all the logger configuration is done. Underneath we use Microsoft ILogger so that it becomes easier to implement one’s own logger. ILogger is used across Flexbase internally. ILogger is configured to use Serilog by default.

.UseSerilog((context, loggerConfig) =>
                {
                    if (context.HostingEnvironment.IsDevelopment())
                    {
                        loggerConfig.WithFlexDefaultWebConfiguration("ECommerceDemo.EndPoint.WebAPI", _configuration)
                       .MinimumLevel.Verbose()
                       .MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
                       .MinimumLevel.Override("System", LogEventLevel.Warning)
                       .MinimumLevel.Override("Microsoft.AspNetCore.Authentication", LogEventLevel.Information)
                       .WriteToColoredConsole()
                       .WritePerformanceMonitorToColoredConsole()
                       .WriteUsageToColoredConsole()
                       .WriteToFile("ECommerceDemo.EndPoint.WebAPI.log.txt")
                       .WritePerformanceMonitorToFile("ECommerceDemo.EndPoint.WebAPI.log.perf.txt")
                       .WriteUsageToFile("ECommerceDemo.EndPoint.WebAPI.log.usage.txt");
                    }
                    if (context.HostingEnvironment.IsStaging())
                    {
                        Guard.AgainstNullAndEmpty("AzureStorageConnectionString cannot be empty", _configuration.GetSection("FlexBase")["AzureStorageConnectionString"]);
                        string azureStorageConnectionString = _configuration.GetSection("FlexBase")["AzureStorageConnectionString"];

                        loggerConfig.WithFlexDefaultWebConfiguration("ECommerceDemo.EndPoint.WebAPI", _configuration)
                        .MinimumLevel.Debug()
                        .MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
                        .MinimumLevel.Override("System", LogEventLevel.Warning)
                        .MinimumLevel.Override("Microsoft.AspNetCore.Authentication", LogEventLevel.Information)
                        .WriteToColoredConsole()
                        .WritePerformanceMonitorToColoredConsole()
                        .WriteUsageToColoredConsole()
                        .WriteToAzureTableStorage(azureStorageConnectionString, "GeneralLogs")
                        .WritePerformanceMonitorToAzureTableStorage(azureStorageConnectionString, "PerformanceTable")
                        .WriteUsageToAzureTableStorage(azureStorageConnectionString, "UsageTable");
                    }
                    if (context.HostingEnvironment.IsProduction())
                    {
                        Guard.AgainstNullAndEmpty("AzureStorageConnectionString cannot be empty", _configuration.GetSection("FlexBase")["AzureStorageConnectionString"]);
                        string azureStorageConnectionString = _configuration.GetSection("FlexBase")["AzureStorageConnectionString"];

                        loggerConfig.WithFlexDefaultWebConfiguration("ECommerceDemo.EndPoint.WebAPI", _configuration)
                        .MinimumLevel.Information()
                        .MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
                        .MinimumLevel.Override("System", LogEventLevel.Warning)
                        .MinimumLevel.Override("Microsoft.AspNetCore.Authentication", LogEventLevel.Information)
                        .WriteToColoredConsole()
                        .WritePerformanceMonitorToColoredConsole()
                        .WriteUsageToColoredConsole()
                        .WriteToAzureTableStorage(azureStorageConnectionString, "GeneralLogs")
                        .WritePerformanceMonitorToAzureTableStorage(azureStorageConnectionString, "PerformanceTable")
                        .WriteUsageToAzureTableStorage(azureStorageConnectionString, "UsageTable");
                    }
                });

Video

Last updated