簡體   English   中英

AutoFac NamedParameter無法正確解析

[英]AutoFac NamedParameter not resolving correctly

我在autofac中陷入困境,並且在綁定到特定構造函數時遇到一些問題。

我有以下代碼:

var builder = new ContainerBuilder();

builder
    .RegisterType<GenericIocFactory>()
    .As<IGenericIocFactory>();

builder
    .RegisterType<Product>()
    .As<IProduct>()
    .PropertiesAutowired();

IContainer Container = builder.Build();

IGenericIocFactory Fac = Container.Resolve<IGenericIocFactory>();

_product = Fac.Get<IProduct>(new Dictionary<string,object>() { {"returnEmpty" , false} }) as Product;

然后在工廠:

public interface IGenericIocFactory
{
    T Get<T>(Dictionary<string,object> options) where T: class;
}

public class GenericIocFactory : IGenericIocFactory
{
    private readonly IComponentContext  _icoContext;
    private object _options;

    public GenericIocFactory(IComponentContext icoContext,bool isInjected = true)
    {
         _icoContext= icoContext;
    }

    public T Get<T>(Dictionary<string,object> options) where T: class
    {
        var _parameters = new List<Parameter>();
        foreach (var parameter in options)
        {
            _parameters.Add(new NamedParameter(parameter.Key, parameter.Value));
        }
        return _icoContext.Resolve<T>(_parameters);
        //operate on new object

        // tried this as well
        //return _icoContext.Resolve<T>(
            //new NamedParameter("returnEmpty" , false)
            //new TypedParameter(typeof(bool),false)
        //);
    }
}

這樣可以解決產品,但不能解決我期望的構造函數。

目標構造函數

public Product(bool returnEmpty)

解決構造函數

public Product(IList<string> productCodes, string fields = "", string orderBy = "ProductCode")

總共有23個構造函數,一個解析不是最大的(所以我不認為它是貪婪的)

public Product(string strFields, string strFrom, string strFilter, string strOrderBy, string whseCode,
        bool addExistsInWharehouse, string additionalAfterorderBy, bool forceUniqueRecords = false)

它也不是定義的第一個或最后一個。

我很沮喪,任何人都可以看到我做錯了什么。

不幸的是, Autofac不提供此機制。

您可能已經實現了IConstructorSelector ,當有多個構造函數可用時,它會選擇一個構造函數,並通過使用UsingSelector方法將其設置為注冊,但是不幸的是,無法訪問當前解析操作的可用參數。

另一個解決方案是實現IInstanceActivator ,該實例負責根據類型和參數創建實例。 要使用自定義IInstanceActivator ,還需要實現IRegistrationBuilder ,這非常困難。 為了保證良好的性能,我還建議使用ConstructorParameterBinding ,它將使用動態編譯表達式創建優化的工廠。

如果您不能更改構造函數,那么我看到的唯一解決方案是實現自己的工廠。 由於您的對象沒有任何依賴關系,因此可以在不使用Autofac的情況下創建它們。

public class GenericIocFactory : IGenericIocFactory
{
    public GenericIocFactory(ILifetimeScope scope)
    {
        this._scope = scope; 
    }

    private readonly ILifetimeScope _scope; 

    public T Get<T>(params object[] args) where T: class
    {           
        ConstructorInfo ci = this.GetConstructorInfo(args);
        if (ci == null) 
        {
            throw ...
        }

        var binder = new ConstructorParameterBinding(ci, args, this._scope);

        T value = binder.Instanciate() as T; 

        if (value == null) 
        {
            throw ...
        }
        if(value is IDisposable)
        {
            this._scope.Disposer.AddInstanceForDisposal(value);
        }
        return value; 
    }


    protected virtual ConstructorInfo GetConstructorInfo<T>(params object[] args)
    {
      // TODO 
    }
}

因此,請再次閱讀Doco。 我需要在開始時綁定構造函數。 但這並不能解決我的問題,因此我每次請求一個實例時都會創建另一個容器,並根據參數構造它。 它有點不正確,但這對於任何正在過渡到使用autofac的現有解決方案的人來說,這都是一個現實的解決方案。

希望這對某人有幫助。

public interface IGenericIocFactory
{
    T Get<T>(params object[] constructorParams) where T: class;
}

public interface ICustomAutoFacContainer
{
    IContainer BindAndReturnCustom<T>(IComponentContext context, Type[] paramsList);
}

public class CustomAutoFacContainer : ICustomAutoFacContainer
{
    public IContainer BindAndReturnCustom<T>(IComponentContext context, Type[] paramsList)
    {
        if (context.IsRegistered<T>())
        {
            // Get the current DI binding type target
            var targetType = context
                .ComponentRegistry
                .Registrations
                .First(r => ((TypedService) r.Services.First()).ServiceType == typeof(T))
                .Target
                .Activator
                .LimitType;

            // todo: exception handling and what not .targetType

            var builder = new ContainerBuilder();

            builder
               .RegisterType(targetType)
               .As<T>()
               .UsingConstructor(paramsList)
               .PropertiesAutowired();

            return builder.Build();
        }
        return null;
    }
}

public class GenericIocFactory : IGenericIocFactory
{
    private ICustomAutoFacContainer _iCustomContainer;
    private readonly IComponentContext _icoContext;
    public GenericIocFactory(ICustomAutoFacContainer iCustomContainer, IComponentContext icoContext)
    {
         _iCustomContainer = iCustomContainer;
        _icoContext = icoContext;
    }

    public T Get<T>(params object[] constructorParams) where T: class
    {
        //TODO handle reflection generation? ?? ?not needed?? ??

        var parameters = constructorParams
            .Select((t, index) => new PositionalParameter(index, t))
            .Cast<Parameter>()
            .ToList();

        var parameterTypes = constructorParams
            .Select((t, index) => t.GetType())
            .ToArray();

        return _iCustomContainer
            .BindAndReturnCustom<T>(_icoContext,parameterTypes)
            .Resolve<T>(parameters);
    }
}

設置和用法如下所示:

var builder = new ContainerBuilder();

// Usually you're only interested in exposing the type
// via its interface:
builder
    .RegisterType<GenericIocFactory>()
    .As<IGenericIocFactory>();

builder
    .RegisterType<CustomAutoFacContainer>()
    .As<ICustomAutoFacContainer>();

builder
    .RegisterType<Product>()
    .As<IProduct>()
    .PropertiesAutowired();

var container = builder.Build();

var factory = container.Resolve<IGenericIocFactory>();

_product = factory.Get<IProduct>(false) as Product;
_product = factory.Get<IProduct>("","") as Product;

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM