Factory Pattern with Open Generics

前端 未结 4 458
南旧
南旧 2020-12-13 18:12

In ASP.NET Core, one of the things you can do with Microsoft\'s dependency injection framework is bind \"open generics\" (generic types unbound to a concrete type) like so:<

4条回答
  •  情书的邮戳
    2020-12-13 18:26

    I also don't understand the point of your lambda expression so I'll explain to you my way of doing it.

    I suppose what you wish is to reach what is explained in the article you shared

    This allowed me to inspect the incoming request before supplying a dependency into the ASP.NET Core dependency injection system

    My need was to inspect a custom header in the HTTP request to determine which customer is requesting my API. I could then a bit later in the pipeline decide which implementation of my IDatabaseRepository (File System or Entity Framework linked to a SQL Database) to provide for this unique request.

    So I start by writing a middleware

    public class ContextSettingsMiddleware
    {
        private readonly RequestDelegate _next;
    
        public ContextSettingsMiddleware(RequestDelegate next, IServiceProvider serviceProvider)
        {
            _next = next;
        }
    
        public async Task Invoke(HttpContext context, IServiceProvider serviceProvider, IHostingEnvironment env, IContextSettings contextSettings)
        {
            var customerName = context.Request.Headers["customer"];
            var customer = SettingsProvider.Instance.Settings.Customers.FirstOrDefault(c => c.Name == customerName);
            contextSettings.SetCurrentCustomer(customer);
    
            await _next.Invoke(context);
        }
    }
    

    My SettingsProvider is just a singleton that provides me the corresponding customer object.

    To let our middleware access this ContextSettings we first need to register it in ConfigureServices in Startup.cs

    var contextSettings = new ContextSettings();
    services.AddSingleton(contextSettings);
    

    And in the Configure method we register our middleware

    app.UseMiddleware();
    

    Now that our customer is accessible from elsewhere let's write our Factory.

    public class DatabaseRepositoryFactory
    {
        private IHostingEnvironment _env { get; set; }
    
        public Func DatabaseRepository { get; private set; }
    
        public DatabaseRepositoryFactory(IHostingEnvironment env)
        {
            _env = env;
            DatabaseRepository = GetDatabaseRepository;
        }
    
        private IDatabaseRepository GetDatabaseRepository(IServiceProvider serviceProvider)
        {
            var contextSettings = serviceProvider.GetService();
            var currentCustomer = contextSettings.GetCurrentCustomer();
    
            if(SOME CHECK)
            {
                var currentDatabase = currentCustomer.CurrentDatabase as FileSystemDatabase;
                var databaseRepository = new FileSystemDatabaseRepository(currentDatabase.Path);
                return databaseRepository;
            }
            else
            {
                var currentDatabase = currentCustomer.CurrentDatabase as EntityDatabase;
                var dbContext = new CustomDbContext(currentDatabase.ConnectionString, _env.EnvironmentName);
                var databaseRepository = new EntityFrameworkDatabaseRepository(dbContext);
                return databaseRepository;
            }
        }
    }
    

    In order to use serviceProvider.GetService<>() method you will need to include the following using in your CS file

    using Microsoft.Extensions.DependencyInjection;
    

    Finally we can use our Factory in ConfigureServices method

    var databaseRepositoryFactory = new DatabaseRepositoryFactory(_env);
    services.AddScoped(databaseRepositoryFactory.DatabaseRepository);
    

    So every single HTTP request my DatabaseRepository will may be different depending of several parameters. I could use a file system or a SQL Database and I can get the proper database corresponding to my customer. (Yes I have multiple databases per customer, don't try to understand why)

    I simplified it as possible, my code is in reality more complex but you get the idea (I hope). Now you can modify this to fit your needs.

提交回复
热议问题