Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/ajax/6.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

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/meteor/3.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# 如何使用自定义工厂方法注册OpenGeneric?_C#_Autofac - Fatal编程技术网

C# 如何使用自定义工厂方法注册OpenGeneric?

C# 如何使用自定义工厂方法注册OpenGeneric?,c#,autofac,C#,Autofac,TL;DR:我可以用Autofac创建一个通用工厂,这样我就可以注入IProduct,而不是从IFactory任何我需要的地方解析它吗?有没有办法将解析从工厂任务移动到合成根目录 所以我使用的是第三方库,它公开了一些通过工厂创建的通用接口。出于演示目的,我们假设以下代码是库: 第三方库模型: public interface IFactory { IProduct<TModel> CreateProduct<TModel>(string identifier);

TL;DR:我可以用Autofac创建一个通用工厂,这样我就可以注入
IProduct
,而不是从
IFactory
任何我需要的地方解析它吗?有没有办法将解析从工厂任务移动到合成根目录

所以我使用的是第三方库,它公开了一些通过工厂创建的通用接口。出于演示目的,我们假设以下代码是库:

第三方库模型

public interface IFactory
{
    IProduct<TModel> CreateProduct<TModel>(string identifier);
}

internal class Factory : IFactory
{
    private readonly string _privateData = "somevalues";

    public IProduct<TModel> CreateProduct<TModel>(string identifier)
    {
        return new Product<TModel>(_privateData, identifier);
    }
}

public interface IProduct<TModel>
{
    void DoSomething();
}

internal sealed class Product<TModel>: IProduct<TModel>
{
    private readonly string _privateData;
    private readonly string _identifier;

    public Product(string privateData, string identifier)
    {
        _privateData = privateData;
        _identifier = identifier;
    }

    public void DoSomething()
    {
        System.Diagnostics.Debug.WriteLine($"{_privateData} + {_identifier}");
    }
}
现在,让我们假设我想要
MyService
中的
ipproduct
。我需要在那里解决这个问题:

public class MyService
{
    public MyService(IFactory factory)
    {
        IProduct<Shoe> shoeProduct = factory.CreateProduct<Shoe>("theshoe");
    }
}
然后像这样注射

public class MyService
{
    public MyService(IProduct<Shoe> shoeProduct) { }
}
但显然,这是一个糟糕的解决方案,原因有很多,最重要的是它容易受到库内部更改的影响

解决方案2:创建一个虚拟类型,并在激活时将其替换为

Type factoryType = typeof(Factory);
Type factoryField = factoryType.GetField("_privateData", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Getfield);
Type productType = typeof(Product); // this is `internal` in the third party library, so I have to look it up from the assembly in reality

builder
.RegisterGeneric(productType)
.OnPreparing(preparing =>
{
    var factory = preparing.Context.Resolve<IFactory>();
    var privateFieldValue = factoryField.GetValue(factory);
    var closedProductType = preparing.Component.Activator.LimitType;
    var productModel = closedProductType.GetGenericArguments().Single();
    var productIdentifier = productModel.GetGenericArgument<ProductIdentifierAttribute>().Identifier;

    preparing.Parameters = new List<Parameter>()
    {
        new PositionalParameter(0, privateFieldValue),
        new PositionalParameter(0, productIdentifier)
    };
})
.AsImplementedInterfaces();
public class DummyProduct<TModel> : IProduct<TModel>
{
    public void DoSomething() => throw new NotImplementedException("");
}
公共类DummyProduct:IPProduct
{
public void DoSomething()=>抛出新的NotImplementedException(“”);
}
因此,我们将其注册为开放泛型,并在注入之前替换其值:

MethodInfo openProductBuilder = this.GetType().GetMethod(nameof(CreateProduct), BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.InvokeMethod);
builder
    .RegisterGeneric(typeof(DummyProduct<>))
    .OnActivating(activating => 
    {
        var productModel = activating.Instance.GetType().GetGenericArguments().First();
        var productIdentifier = productModel.GetGenericArgument<ProductIdentifierAttribute>().Identifier;
        var factory = activating.Context.Resolve<IFactory>();
        var closedProductBuilder = openProductBuilder.MakeGenericMethod(productModel);
        object productObject = closedProductBuilder.Invoke(this, new object[] { factory, productIdentifier });
        handler.ReplaceInstance(productObject);
    })
    .AsImplementedInterfaces();
MethodInfo openProductBuilder=this.GetType().GetMethod(nameof(CreateProduct),BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.InvokeMethod);
建设者
.RegisterGeneric(类型(DummyProduct))
.on激活(激活=>
{
var productModel=activating.Instance.GetType().GetGenericArguments().First();
var productIdentifier=productModel.GetGenericArgument().Identifier;
var factory=activating.Context.Resolve();
var closedProductBuilder=openProductBuilder.MakeGenericMethod(productModel);
object productObject=closedProductBuilder.Invoke(此,新对象[]{factory,productIdentifier});
ReplaceInstance(productObject);
})
.a实现接口();
我们有一个helper方法,因此我们只依赖于Mongo模块类中的反射方法:

私有IPProduct CreateProduct(IFactory工厂,字符串标识符)
{
返回工厂。CreateProduct(标识符);
}
现在,很明显,这比第一种方法要好,并且不依赖于太多的反射。不幸的是,每次我们想要真实的对象时,它都需要创建一个虚拟对象。真糟糕

问题:是否有其他方法使用Autofac进行此操作?我是否可以创建Autofac可以使用的通用工厂方法?我的主要目标是省去创建虚拟类型的过程,直接跳到调用
CreateProduct
代码


注意事项:我删掉了一些错误检查,等。我通常会这样做,使这个问题尽可能简短,同时仍然充分演示问题和我当前的解决方案。

如果您的工厂中没有非通用的
创建
方法,您将需要调用
MakeGenericMethod

您可以使用
IRegistrationSource
组件来执行与解决方案2中相同的操作,而不是激活
事件

internal class FactoryRegistrationSource : IRegistrationSource
{
    private static MethodInfo openProductBuilder = typeof(Factory).GetMethod(nameof(Factory.CreateProduct));

    public Boolean IsAdapterForIndividualComponents => false;

    public IEnumerable<IComponentRegistration> RegistrationsFor(Service service, Func<Service, IEnumerable<IComponentRegistration>> registrationAccessor)
    {
        IServiceWithType typedService = service as IServiceWithType;

        if (typedService != null && typedService.ServiceType.IsClosedTypeOf(typeof(IProduct<>)))
        {
            IComponentRegistration registration = RegistrationBuilder.ForDelegate(typedService.ServiceType, (c, p) =>
             {
                 IFactory factory = c.Resolve<IFactory>();

                 Type productModel = typedService.ServiceType.GetGenericArguments().First();
                 String productIdentifier = productModel.GetCustomAttribute<ProductIdentifierAttribute>()?.Identifier;

                 MethodInfo closedProductBuilder = openProductBuilder.MakeGenericMethod(productModel);
                 Object productObject = closedProductBuilder.Invoke(factory, new object[] { productIdentifier });

                 return productObject;
             }).As(service).CreateRegistration();
            yield return registration;
        }
        yield break;
    }
}
内部类FactoryRegistrationSource:IRegistrationSource
{
私有静态MethodInfo openProductBuilder=typeof(Factory.GetMethod)(nameof(Factory.CreateProduct));
公共布尔值IsAdapterForIndividualComponents=>false;
公共IEnumerable Registrationfor(服务、函数注册接受器)
{
IServiceWithType typedService=作为IServiceWithType的服务;
if(typedService!=null&&typedService.ServiceType.IsClosedTypeOf(typeof(ipproduct)))
{
IComponentRegistration=RegistrationBuilder.ForDelegate(typedService.ServiceType,(c,p)=>
{
IFactory factory=c.Resolve();
类型productModel=typedService.ServiceType.GetGenericArguments().First();
字符串productIdentifier=productModel.GetCustomAttribute()?.Identifier;
MethodInfo closedProductBuilder=openProductBuilder.MakeGenericMethod(productModel);
Object productObject=closedProductBuilder.Invoke(工厂,新对象[]{productIdentifier});
返回产品对象;
}).As(service.CreateRegistration();
收益申报登记;
}
屈服断裂;
}
}
public class DummyProduct<TModel> : IProduct<TModel>
{
    public void DoSomething() => throw new NotImplementedException("");
}
MethodInfo openProductBuilder = this.GetType().GetMethod(nameof(CreateProduct), BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.InvokeMethod);
builder
    .RegisterGeneric(typeof(DummyProduct<>))
    .OnActivating(activating => 
    {
        var productModel = activating.Instance.GetType().GetGenericArguments().First();
        var productIdentifier = productModel.GetGenericArgument<ProductIdentifierAttribute>().Identifier;
        var factory = activating.Context.Resolve<IFactory>();
        var closedProductBuilder = openProductBuilder.MakeGenericMethod(productModel);
        object productObject = closedProductBuilder.Invoke(this, new object[] { factory, productIdentifier });
        handler.ReplaceInstance(productObject);
    })
    .AsImplementedInterfaces();
private IProduct<TModel> CreateProduct<TModel>(IFactory factory, string identifier)
{
    return factory.CreateProduct<TModel>(identifier);
}
internal class FactoryRegistrationSource : IRegistrationSource
{
    private static MethodInfo openProductBuilder = typeof(Factory).GetMethod(nameof(Factory.CreateProduct));

    public Boolean IsAdapterForIndividualComponents => false;

    public IEnumerable<IComponentRegistration> RegistrationsFor(Service service, Func<Service, IEnumerable<IComponentRegistration>> registrationAccessor)
    {
        IServiceWithType typedService = service as IServiceWithType;

        if (typedService != null && typedService.ServiceType.IsClosedTypeOf(typeof(IProduct<>)))
        {
            IComponentRegistration registration = RegistrationBuilder.ForDelegate(typedService.ServiceType, (c, p) =>
             {
                 IFactory factory = c.Resolve<IFactory>();

                 Type productModel = typedService.ServiceType.GetGenericArguments().First();
                 String productIdentifier = productModel.GetCustomAttribute<ProductIdentifierAttribute>()?.Identifier;

                 MethodInfo closedProductBuilder = openProductBuilder.MakeGenericMethod(productModel);
                 Object productObject = closedProductBuilder.Invoke(factory, new object[] { productIdentifier });

                 return productObject;
             }).As(service).CreateRegistration();
            yield return registration;
        }
        yield break;
    }
}