Как реализовать командный шаблон с 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>
непосредственно.
- Получение всех типов, которые реализуют интерфейс
- .NET - Получение всех реализаций универсального интерфейса?
Также вы можете рассмотреть возможность создания CommandModule : NinjectModule
держать привязки, связанные с отражением, вдали от остальных ваших регистраций.