具有开放泛型的工厂模式 [英] Factory Pattern with Open Generics

查看:31
本文介绍了具有开放泛型的工厂模式的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在 ASP.NET Core 中,你可以用微软的依赖注入框架做的一件事是绑定开放泛型"(泛型类型未绑定到具体类型)像这样:

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:

public void ConfigureServices(IServiceCollection services) {
    services.AddSingleton(typeof(IRepository<>), typeof(Repository<>))
}

您也可以使用 水合依赖的工厂模式.这是一个人为的例子:

You can also employ the factory pattern to hydrate dependencies. Here's a contrived example:

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()
    ); 
}

但是,我一直无法弄清楚如何将这两个概念结合在一起.似乎它会以这样的方式开始,但我需要用于混合 IRepository<> 实例的具体类型.

However, I have not been able to figure out how to combine the two concepts together. It seems like it would start with something like this, but I need the concrete type that is being used to hydrate an instance of IRepository<>.

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

    services.AddSingleton(
        typeof(IRepository<>), 
        provider => {
            // Say the IServiceProvider is trying to hydrate 
            // IRepository<Foo> when this lambda is invoked. 
            // In that case, I need access to a System.Type 
            // object which is IRepository<Foo>. 
            // i.e.: repositoryType = typeof(IRepository<Foo>);

            // If I had that, I could snag the generic argument
            // from IRepository<Foo> and hydrate the factory, like so:

            var modelType = repositoryType.GetGenericArguments()[0];
            var factoryType = typeof(IFactory<IRepository<>>).MakeGenericType(modelType);
            var factory = (IFactory<object>)p.GetRequiredService(factoryType);

            return factory.Provide();
        }           
    ); 
}

如果我尝试使用带有开放泛型的 Func 仿函数,我会得到 这个 ArgumentException 带有消息 Open generic service type 'IRepository;'需要注册一个开放的通用实现类型. 来自 dotnet CLI.它甚至没有到达 lambda.

If I try to use the Func<IServiceProvider, object> functor with an open generic, I get this ArgumentException with the message Open generic service type 'IRepository<T>' requires registering an open generic implementation type. from the dotnet CLI. It doesn't even get to the lambda.

微软的依赖注入框架可以实现这种类型的绑定吗?

Is this type of binding possible with Microsoft's dependency injection framework?

推荐答案

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

The net.core dependency does not allow you to provide a factory method when registering an open generic type, but you can work around this by providing a type that will implement the requested interface, but internally it will act as a factory. A factory in disguise:

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
}

当容器解析 MongoCollectionFactory 时,它会知道 T 是什么类型并正确创建集合.然后我们将创建的集合保存在内部,并将所有调用委托给它.(我们正在模仿 this=factory.Create() 这在 csharp 中是不允许的.:))

When the container resolves the MongoCollectionFactory it will know what type T is and will create the collection correctly. Then we take that created collection save it internally, and delegate all calls to it. ( We are mimicking this=factory.Create() which is not allowed in csharp. :))

更新:正如 Kristian Hellang 指出的,ASP.NET Logging 使用相同的模式

Update: As pointed out by Kristian Hellang the same pattern is used by ASP.NET Logging

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);
    }
}

https:///github.com/aspnet/Logging/blob/dev/src/Microsoft.Extensions.Logging.Abstractions/LoggerOfT.cs#L29

此处的原始讨论:

https://twitter.com/khellang/status/839120286222012416

这篇关于具有开放泛型的工厂模式的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆