Как реализовать командный шаблон с Ninject?

В настоящее время я реализую шаблон команды с помощью ninject, выполняя следующие привязки:

kernel.Bind<ICommand<InsertParams>>().To<InsertProductCommand>();
kernel.Bind<ICommand<UpdateParams>>().To<UpdateProductCommand>();
kernel.Bind<ICommand<DeleteParams>>().To<DeleteProductCommand>();

Мой вопрос: эти определения и команды могут расти со временем. Есть ли способ свести все эти привязки к чему-то вроде "Bind(typeof(ICommand<>))", чтобы каждый интерфейс, зависящий от универсального типа, был разрешен соответствующим образом?

Пример: Если я реализую "ValidateProductCommand", который реализует "ICommand", привязка автоматически разрешается без добавления дополнительной привязки.

Благодарю.

2 ответа

Решение

На самом деле есть https://github.com/ninject/ninject.extensions.conventions, которые могут помочь вам в этом. И тогда это довольно легко сделать:

kernel.Bind(x => x.FromThisAssembly()
    .SelectAllClasses()
    .InheritedFrom(typeof(ICommand<>))
    .BindSingleInterface());

Полный тестовый код (с использованием пакетов nuget Ninject, Ninject.Extensions.Conventions, xUnit и FluentAssertions):

using FluentAssertions;
using Ninject;
using Ninject.Extensions.Conventions;
using Xunit;

namespace NinjectTest.ClosedGenericConvention
{
    public interface ICommand<TParam> { }

    public class FloatCommand : ICommand<float> { }

    public class IntCommand : ICommand<int> { }

    public class Test
    {
        [Fact]
        public void Fact()
        {
            var kernel = new StandardKernel();

            kernel.Bind(x => x.FromThisAssembly()
                .SelectAllClasses()
                .InheritedFrom(typeof(ICommand<>))
                .BindSingleInterface());

            kernel.Get<ICommand<float>>().Should().BeOfType<FloatCommand>();
            kernel.Get<ICommand<int>>().Should().BeOfType<IntCommand>();
        }
    }
}

Я не могу сказать по вашему вопросу, ICommand<T> это ваш собственный интерфейс или если вы используете WPF. Если это ваше, вы можете создать неуниверсальный ICommand интерфейс и есть ICommand<T> сойди с него. Это поможет вам:

kernel.Bind<ICommand>().To<InsertProductCommand>();
kernel.Bind<ICommand>().To<UpdateProductCommand>();
kernel.Bind<ICommand>().To<DeleteProductCommand>();

Затем вы можете использовать отражение, чтобы найти классы, которые реализуют ICommand и связать их.

var types = AppDomain.CurrentDomain.GetAssemblies()
    .SelectMany(s => s.GetTypes())
    .Where(p => typeof(ICommand).IsAssignableFrom(p));

foreach (var type in types) {
    kernel.Bind<ICommand>().To(type);
}

Даже если вы не можете сделать ICommand<T> спуститься с ICommand, вы все еще можете сделать это с отражением. Это немного сложнее. Мое предложение выше заимствовано из (1) ниже, но (2) показывает, как настроить ICommand<T> непосредственно.

  1. Получение всех типов, которые реализуют интерфейс
  2. .NET - Получение всех реализаций универсального интерфейса?

Также вы можете рассмотреть возможность создания CommandModule : NinjectModule держать привязки, связанные с отражением, вдали от остальных ваших регистраций.

Другие вопросы по тегам