Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/262.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 具有开放泛型的工厂模式_C#_Generics_Dependency Injection_Asp.net Core - Fatal编程技术网

C# 具有开放泛型的工厂模式

C# 具有开放泛型的工厂模式,c#,generics,dependency-injection,asp.net-core,C#,Generics,Dependency Injection,Asp.net Core,在ASP.NET Core中,可以使用Microsoft的依赖项注入框架(未绑定到具体类型的泛型类型)执行以下操作之一: public void配置服务(IServiceCollection服务){ 服务.AddSingleton(类型化(IRepository),类型化(存储库)) } 你也可以雇佣。下面是一个人为的例子: public interface IFactory<out T> { T Provide(); } public void ConfigureSer

在ASP.NET Core中,可以使用Microsoft的依赖项注入框架(未绑定到具体类型的泛型类型)执行以下操作之一:

public void配置服务(IServiceCollection服务){
服务.AddSingleton(类型化(IRepository),类型化(存储库))
}
你也可以雇佣。下面是一个人为的例子:

public interface IFactory<out T> {
    T Provide();
}

public void ConfigureServices(IServiceCollection services) {
    services.AddTransient(typeof(IFactory<>), typeof(Factory<>));

    services.AddSingleton(
        typeof(IRepository<Foo>), 
        p => p.GetRequiredService<IFactory<IRepository<Foo>>().Provide()
    ); 
}
公共接口IFactory{
T提供();
}
public void配置服务(IServiceCollection服务){
服务业(类型(工厂),类型(工厂));
服务.AddSingleton(
类型(IRepository),
p=>p.GetRequiredService{
//假设IServiceProvider正在尝试水合
//调用此lambda时的i假定。
//在这种情况下,我需要访问系统。键入
//对象,它是一个假定的对象。
//即:repositoryType=typeof(IRepository);
//如果我有这个,我可以找到一般的论点
//从IRepository和水合物工厂,如下所示:
var modelType=repositoryType.GetGenericArguments()[0];
var factoryType=typeof(IFactory)。MakeGenericType(modelType);
变量工厂=(IFactory)p.GetRequiredService(factoryType);
返回工厂。提供();
}           
); 
}
如果我尝试将
Func
functor与打开的泛型一起使用,我会收到消息
打开的泛型服务类型“IRepository”需要注册打开的泛型实现类型。
来自dotnet CLI。甚至连兰姆达都没有


Microsoft的依赖项注入框架是否可以使用这种类型的绑定?

我也不理解lambda表达式的意义,因此我将向您解释我的方法

我想你希望达到你在分享的文章中所解释的目的

这允许我在向ASP.NET核心依赖项注入系统提供依赖项之前检查传入的请求

我需要检查HTTP请求中的自定义头,以确定哪个客户正在请求我的API。然后,我可以稍后在管道中决定我的
IDatabaseRepository
(链接到SQL数据库的文件系统或实体框架)的哪个实现来提供这个独特的请求

所以我从编写一个中间件开始

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);
    }
}
app.UseMiddleware<ContextSettingsMiddleware>();
My
SettingsProvider
只是一个为我提供相应客户对象的单例

要让我们的中间件访问此
ContextSettings
,我们首先需要在Startup.cs的
ConfigureServices
中注册它

var contextSettings = new ContextSettings();
services.AddSingleton<IContextSettings>(contextSettings);
现在,我们的客户可以从其他地方访问,让我们编写我们的工厂

public class DatabaseRepositoryFactory
{
    private IHostingEnvironment _env { get; set; }

    public Func<IServiceProvider, IDatabaseRepository> DatabaseRepository { get; private set; }

    public DatabaseRepositoryFactory(IHostingEnvironment env)
    {
        _env = env;
        DatabaseRepository = GetDatabaseRepository;
    }

    private IDatabaseRepository GetDatabaseRepository(IServiceProvider serviceProvider)
    {
        var contextSettings = serviceProvider.GetService<IContextSettings>();
        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;
        }
    }
}
最后,我们可以在
ConfigureServices
方法中使用我们的工厂

var databaseRepositoryFactory = new DatabaseRepositoryFactory(_env);
services.AddScoped<IDatabaseRepository>(databaseRepositoryFactory.DatabaseRepository);
var databaseRepositoryFactory=新的databaseRepositoryFactory(_env);
AddScoped(databaseRepositoryFactory.DatabaseRepository);
因此,每个HTTP请求my
DatabaseRepository
都可能因几个参数的不同而有所不同。我可以使用文件系统或SQL数据库,我可以得到与我的客户对应的适当数据库。(是的,每个客户都有多个数据库,不要试图理解原因)


我尽可能地简化了它,我的代码实际上更复杂,但你明白了(我希望)。现在,您可以修改它以满足您的需要。

net.core依赖项不允许您在注册开放泛型类型时提供工厂方法,但您可以通过提供将实现所请求接口的类型来解决此问题,但在内部它将充当工厂。变相的工厂:

services.AddSingleton(typeof(IMongoCollection<>), typeof(MongoCollectionFactory<>)); //this is the important part
services.AddSingleton(typeof(IRepository<>), typeof(Repository<>))


public class Repository : IRepository {
    private readonly IMongoCollection _collection;
    public Repository(IMongoCollection collection)
    {
        _collection = collection;
    }

    // .. rest of the implementation
}

//and this is important as well
public class MongoCollectionFactory<T> : IMongoCollection<T> {
    private readonly _collection;

    public RepositoryFactoryAdapter(IMongoDatabase database) {
        // do the factory work here
        _collection = database.GetCollection<T>(typeof(T).Name.ToLowerInvariant())
    }

    public T Find(string id) 
    {
        return collection.Find(id);
    }   
    // ... etc. all the remaining members of the IMongoCollection<T>, 
    // you can generate this easily with ReSharper, by running 
    // delegate implementation to a new field refactoring
}

此处的原始讨论:


我对现有的解决方案也不满意

这是一个完整的解决方案,使用内置容器,支持我们需要的一切:

  • 简单的依赖关系
  • 复杂的依赖关系(需要解析
    IServiceProvider
  • 配置数据(例如连接字符串)
我们将注册我们真正想要使用的类型的代理。代理只是从预期类型继承,但通过单独注册的
Options
类型获取“困难”部分(复杂的依赖项和配置)

由于
选项
类型是非泛型的,因此很容易像往常一样进行自定义

public static class RepositoryExtensions
{
    /// <summary>
    /// A proxy that injects data based on a registered Options type.
    /// As long as we register the Options with exactly what we need, we are good to go.
    /// That's easy, since the Options are non-generic!
    /// </summary>
    private class ProxyRepository<T> : Repository<T>
    {
        public ProxyRepository(Options options, ISubdependency simpleDependency)
            : base(
                // A simple dependency is injected to us automatically - we only need to register it
                simpleDependency,
                // A complex dependency comes through the non-generic, carefully registered Options type
                options?.ComplexSubdependency ?? throw new ArgumentNullException(nameof(options)),
                // Configuration data comes through the Options type as well
                options.ConnectionString)
        {
        }
    }

    public static IServiceCollection AddRepositories(this ServiceCollection services, string connectionString)
    {
        // Register simple subdependencies (to be automatically resolved)
        services.AddSingleton<ISubdependency, Subdependency>();

        // Put all regular configuration on the Options instance
        var optionObject = new Options(services)
        {
            ConnectionString = connectionString ?? throw new ArgumentNullException(nameof(connectionString))
        };

        // Register the Options instance
        // On resolution, last-minute, add the complex subdependency to the options as well (with access to the service provider)
        services.AddSingleton(serviceProvider => optionObject.WithSubdependency(ResolveSubdependency(serviceProvider)));

        // Register the open generic type
        // All dependencies will be resolved automatically: the simple dependency, and the Options (holding everything else)
        services.AddSingleton(typeof(IRepository<>), typeof(ProxyRepository<>));

        return services;

        // Local function that resolves the subdependency according to complex logic ;-)
        ISubdependency ResolveSubdependency(IServiceProvider serviceProvider)
        {
            return new Subdependency();
        }
    }

    internal sealed class Options
    {
        internal IServiceCollection Services { get; }

        internal ISubdependency ComplexSubdependency { get; set; }
        internal string ConnectionString { get; set; }

        internal Options(IServiceCollection services)
        {
            this.Services = services ?? throw new ArgumentNullException(nameof(services));
        }

        /// <summary>
        /// Fluently sets the given subdependency, allowing to options object to be mutated and returned as a single expression.
        /// </summary>
        internal Options WithSubdependency(ISubdependency subdependency)
        {
            this.ComplexSubdependency = subdependency ?? throw new ArgumentNullException(nameof(subdependency));
            return this;
        }
    }
}
公共静态类RepositoryExtensions
{
/// 
///基于已注册的选项类型注入数据的代理。
///只要我们登记的选择与我们需要的完全一样,我们就可以去。
///这很简单,因为选项是非通用的!
/// 
私有类代理存储库:存储库
{
公共代理存储库(选项,IsSubDependency simpleDependency)
:基本(
//一个简单的依赖项会自动注入到我们身上——我们只需要注册它
简单依赖,
//复杂的依赖关系来自于非泛型的、仔细注册的选项类型
options?.ComplexSubdependency??抛出新的ArgumentNullException(nameof(options)),
//配置数据也通过选项类型提供
选项。连接字符串)
{
}
}
公共静态IServiceCollection AddRepositories(此服务集合服务,字符串连接字符串)
{
//注册简单子依赖项(自动解析)
services.AddSingleton();
//将所有常规配置放在Options实例上
var optionObject=新选项(服务)
{
ConnectionString=ConnectionString??抛出新的ArgumentNullException(nameof(ConnectionString))
};
//注册选项实例
//在解析时,最后一分钟,将复杂的子依赖项也添加到选项中(通过访问
var databaseRepositoryFactory = new DatabaseRepositoryFactory(_env);
services.AddScoped<IDatabaseRepository>(databaseRepositoryFactory.DatabaseRepository);
services.AddSingleton(typeof(IMongoCollection<>), typeof(MongoCollectionFactory<>)); //this is the important part
services.AddSingleton(typeof(IRepository<>), typeof(Repository<>))


public class Repository : IRepository {
    private readonly IMongoCollection _collection;
    public Repository(IMongoCollection collection)
    {
        _collection = collection;
    }

    // .. rest of the implementation
}

//and this is important as well
public class MongoCollectionFactory<T> : IMongoCollection<T> {
    private readonly _collection;

    public RepositoryFactoryAdapter(IMongoDatabase database) {
        // do the factory work here
        _collection = database.GetCollection<T>(typeof(T).Name.ToLowerInvariant())
    }

    public T Find(string id) 
    {
        return collection.Find(id);
    }   
    // ... etc. all the remaining members of the IMongoCollection<T>, 
    // you can generate this easily with ReSharper, by running 
    // delegate implementation to a new field refactoring
}
public class Logger<T> : ILogger<T>
{
    private readonly ILogger _logger;

    public Logger(ILoggerFactory factory)
    {
        _logger = factory.CreateLogger(TypeNameHelper.GetTypeDisplayName(typeof(T)));
    }

    void ILogger.Log<TState>(...)
    {
        _logger.Log(logLevel, eventId, state, exception, formatter);
    }
}
public static class RepositoryExtensions
{
    /// <summary>
    /// A proxy that injects data based on a registered Options type.
    /// As long as we register the Options with exactly what we need, we are good to go.
    /// That's easy, since the Options are non-generic!
    /// </summary>
    private class ProxyRepository<T> : Repository<T>
    {
        public ProxyRepository(Options options, ISubdependency simpleDependency)
            : base(
                // A simple dependency is injected to us automatically - we only need to register it
                simpleDependency,
                // A complex dependency comes through the non-generic, carefully registered Options type
                options?.ComplexSubdependency ?? throw new ArgumentNullException(nameof(options)),
                // Configuration data comes through the Options type as well
                options.ConnectionString)
        {
        }
    }

    public static IServiceCollection AddRepositories(this ServiceCollection services, string connectionString)
    {
        // Register simple subdependencies (to be automatically resolved)
        services.AddSingleton<ISubdependency, Subdependency>();

        // Put all regular configuration on the Options instance
        var optionObject = new Options(services)
        {
            ConnectionString = connectionString ?? throw new ArgumentNullException(nameof(connectionString))
        };

        // Register the Options instance
        // On resolution, last-minute, add the complex subdependency to the options as well (with access to the service provider)
        services.AddSingleton(serviceProvider => optionObject.WithSubdependency(ResolveSubdependency(serviceProvider)));

        // Register the open generic type
        // All dependencies will be resolved automatically: the simple dependency, and the Options (holding everything else)
        services.AddSingleton(typeof(IRepository<>), typeof(ProxyRepository<>));

        return services;

        // Local function that resolves the subdependency according to complex logic ;-)
        ISubdependency ResolveSubdependency(IServiceProvider serviceProvider)
        {
            return new Subdependency();
        }
    }

    internal sealed class Options
    {
        internal IServiceCollection Services { get; }

        internal ISubdependency ComplexSubdependency { get; set; }
        internal string ConnectionString { get; set; }

        internal Options(IServiceCollection services)
        {
            this.Services = services ?? throw new ArgumentNullException(nameof(services));
        }

        /// <summary>
        /// Fluently sets the given subdependency, allowing to options object to be mutated and returned as a single expression.
        /// </summary>
        internal Options WithSubdependency(ISubdependency subdependency)
        {
            this.ComplexSubdependency = subdependency ?? throw new ArgumentNullException(nameof(subdependency));
            return this;
        }
    }
}