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

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

问题描述

在ASP.NET Core中,您可以使用Microsoft的依赖注入框架绑定打开泛型(泛型类型未绑定到具体类型),如下所示:

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

您也可以使用工厂模式保护依赖性。这是一个人为的例子:

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

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

services.AddSingleton(
typeof(IRepository< Foo>),
p => p.GetRequiredService< IFactory< IRepository< Foo>>()。Provide()
);
}

但是,我一直无法弄清楚如何将这两个概念一起。看起来好像它会从这样的事情开始,但我需要用于水化 IRepository<>

$ b实例的具体类型
$ b

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

services.AddSingleton(
typeof(IRepository<>),
provider => {
//假设IServiceProvider试图水合
// IRepository< Foo>当这个lambda被调用时
//在这种情况下,我需要访问System.Type
//对象,它是IRepository< Foo> ;.
//即:repositoryType = typeof(IRepository< Foo>);

//如果我这样做了,我可以从IRepository< Foo>获取泛型参数
//并保存工厂,所以:

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

return factory.Provide();
}
);



$ b如果我尝试使用 Func< IServiceProvider,对象> 函数与一个开放的泛型,我得到 ArgumentException 与消息打开通用服务类型'IRepository< T>'需要在dotnet CLI中注册一个开放的通用实现类型。它甚至没有达到lambda。



这种类型的绑定可能与微软的依赖注入框架有关吗?


$ $ p $ services.AddSingleton(typeof(IMongoCollection<>),typeof(MongoCollectionFactory<>)); //这是重要的部分
services.AddSingleton(typeof(IRepository<>),typeof(Repository<>))


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

// ..剩下的执行
}

//这也很重要
public class MongoCollectionFactory< T> :IMongoCollection< T> {
私人只读_collection;
$ b $ public RepositoryFactoryAdapter(IMongoDatabase database){
//在这里完成工作
_collection = database.GetCollection< T>(typeof(T).Name.ToLowerInvariant())
}

public T Find(string id)
{
return collection.Find(id);
}
// ...等IMongoCollection< T>,
的所有其余成员//您可以使用ReSharper轻松生成此代码,方法是运行
// delegate执行到一个新的字段重构
}

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



更新:
正如Kristian Hellang指出的那样,ASP.NET日志记录使用了相同的模式

  public class Logger< T> ; :ILogger< T> 
{
私人只读ILogger _logger;

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


无效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


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

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

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?

解决方案

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
}

When the container resolves the MongoCollectionFactory ti 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. :))

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

original discussion here:

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

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

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