Ninject не может разрешить тип интерфейса, когда конкретный тип является производным от абстрактного базового класса - PullRequest
4 голосов
/ 10 марта 2011

У меня проблема с Ninject, пытающимся разрешить тип интерфейса, в котором конкретный тип наследуется от абстрактного базового класса, который реализует интерфейс.

РЕДАКТИРОВАТЬ: это в Windows Mobile с использованием .NET CF.

Моя конкретная проблема касается докладчиков и представлений, поэтому я придерживаюсь этого в этом примере вместо foos и баров.

Я хочу внедрить фабрики для докладчиков и представлений, чтобыпозднее создание этих экземпляров в глубине стека представлений пользовательского интерфейса.

Ниже я исключил все проверки ошибок для лучшей читаемости.

Мой фабричный интерфейс:

public interface IFactory<T>
{
    T Create();
}

Мойведущий и просмотр:

public sealed class Presenter
{
    private readonly View view;

    public Presenter(View view)
    {
        this.view = view;
    }
}

public sealed class View
{
    public View()
    {
    }
}

Сначала я покажу, что отлично работает, что Ninject разрешает, как и ожидалось.Это не будет включать абстрактный базовый класс, который я упоминал в начале.После этого я добавлю небольшие модификации с абстрактным базовым классом, которые будут генерировать Ninject при попытке разрешить зависимости.

Мы видим выше, что презентатор зависит от представления, поэтому фабрика презентаторов будет зависетьна фабрике представлений:

public sealed class GoodPresenterFactory : IFactory<Presenter>
{
    private readonly IFactory<View> viewFactory;

    public GoodPresenterFactory(IFactory<View> viewFactory)
    {
        this.viewFactory = viewFactory;
    }

    public Presenter Create()
    {
        return new Presenter(this.viewFactory.Create());
    }
}

public sealed class ViewFactory : IFactory<View>
{
    public ViewFactory()
    {
    }

    public View Create()
    {
        return new View();
    }
}

Соединение с Ninject:

Bind<IFactory<Presenter>>().To<GoodPresenterFactory>();
Bind<IFactory<View>>().To<ViewFactory>();

А затем разрешение фабрики презентаторов:

var presenterFactory = container.Get<IFactory<Presenter>>();

Все до сих пор работаетв совершенстве.Зависимость от фабрики представлений внутри фабрики презентаторов разрешена, как и ожидалось.

Теперь у меня есть миллион классов, которые похожи на GoodPresenterFactory выше, и поэтому я хотел небольшой базовый класс для обработки некоторых простых вещей, таких какзависимость от фабрики представлений на фабрике презентаторов:

public abstract class FactoryBase<T, U> : IFactory<T>
{
    protected readonly U dependency;

    protected FactoryBase(U dependency)
    {
        this.dependency = dependency;
    }

    public abstract T Create();
}

Тогда фабрика презентаторов изменится, и что-то в этом изменении приведет к сбою решения Ninject:

public sealed class BadPresenterFactory : FactoryBase<Presenter, IFactory<View>>
{
    public BadPresenterFactory(IFactory<View> viewFactory)
        : base(viewFactory)
    {
    }

    public override Presenter Create()
    {
        return new Presenter(this.dependency.Create());
    }
}

И изменение проводки Ninjectсоответственно:

Bind<IFactory<Presenter>>().To<BadPresenterFactory>();
Bind<IFactory<View>>().To<ViewFactory>();

Эти изменения приведут к тому, что Ninject создаст исключение ArgumentNullException при выполнении

var presenterFactory = container.Get<IFactory<Presenter>>();

Стек вызовов из исключения:

at System.Reflection.RuntimeMethodInfo.GetParentDefinition()
at System.Reflection.CustomAttribute.IsDefined(MemberInfo member, Type caType, Boolean inherit)
at System.Reflection.RuntimeMethodInfo.IsDefined(Type attributeType, Boolean inherit)
at System.Attribute.IsDefined(MemberInfo element, Type attributeType, Boolean inherit)
at System.Attribute.IsDefined(MemberInfo element, Type attributeType)
at Ninject.Infrastructure.Language.ExtensionsForMemberInfo.HasAttribute(MemberInfo member, Type type)
at Ninject.Selection.Heuristics.StandardInjectionHeuristic.ShouldInject(MemberInfo member)
at Ninject.Selection.Selector.<>c__DisplayClassa.<SelectMethodsForInjection>b__9(IInjectionHeuristic h)
at System.Linq.Enumerable.Any[TSource](IEnumerable`1 source, Func`2 predicate)
at Ninject.Selection.Selector.<SelectMethodsForInjection>b__8(MethodInfo m)
at System.Linq.Enumerable.<WhereIterator>d__0`1.MoveNext()
at Ninject.Planning.Strategies.MethodReflectionStrategy.Execute(IPlan plan)
at Ninject.Planning.Planner.<>c__DisplayClass2.<GetPlan>b__0(IPlanningStrategy s)
at Ninject.Infrastructure.Language.ExtensionsForIEnumerableOfT.Map[T](IEnumerable`1 series, Action`1 action)
at Ninject.Planning.Planner.GetPlan(Type type)
at Ninject.Activation.Providers.StandardProvider.Create(IContext context)
at Ninject.Activation.Context.Resolve()
at Ninject.KernelBase.<Resolve>b__4(IContext context)
at System.Linq.Enumerable.<SelectIterator>d__d`2.MoveNext()
at System.Linq.Enumerable.<CastIterator>d__b0`1.MoveNext()
at System.Linq.Enumerable.Single[TSource](IEnumerable`1 source)
at Ninject.ResolutionExtensions.Get[T](IResolutionRoot root, IParameter[] parameters)
at NinjectTest.Program.Main()

Если я изменю FactoryBase так что у него нет зависимости, это просто голый базовый класс, тогда и Ninject тоже терпит неудачу.

public abstract class NakedFactoryBase<T> : IFactory<T>
{
    protected NakedFactoryBase()
    {
    }

    public abstract T Create();
}

public sealed class PointlessPresenterFactory : NakedFactoryBase<Presenter>
{
    private readonly IFactory<View> viewFactory;

    public PointlessPresenterFactory(IFactory<View> viewFactory)
    {
        this.viewFactory = viewFactory;
    }

    public override Presenter Create()
    {
        return new Presenter(this.viewFactory.Create());
    }
}

Как видите, сбой PointlessPresenterFactory идентичен последующему GoodPresenterFactory, за исключениемпрямая IFactory<Presenter> реализация в GoodPresenterFactory, в отличие от голого базового класса completeley, используемого в PointlessPresenterFactory.

Any idПочему Ninject не удается разрешить при использовании базового класса фабрики?

1 Ответ

3 голосов
/ 16 июня 2011

Эта проблема была исправлена ​​в сборке 2.3.0.46 и станет частью следующего выпуска (2.4).

ПРИМЕЧАНИЕ.Атрибут больше не может быть определен в базовых методах.Это должно быть определено в методе перегрузки.

...