问题
Currently, I'm implementing the command pattern with ninject doing the following bindings:
kernel.Bind<ICommand<InsertParams>>().To<InsertProductCommand>();
kernel.Bind<ICommand<UpdateParams>>().To<UpdateProductCommand>();
kernel.Bind<ICommand<DeleteParams>>().To<DeleteProductCommand>();
My question is: these definitions and commands can grow over the time. Is there any way to reduce all these bindings to something like 'Bind(typeof(ICommand<>))' so that every interface depending on the generic type is resolved appropriately?
Ex: If I implement a "ValidateProductCommand" which implements "ICommand" the binding is automatically resolved without adding an extra binding.
Thanks.
回答1:
Actually there's Ninject.Extensions.Conventions which can help you with that. And then it's quite easy to do:
kernel.Bind(x => x.FromThisAssembly()
.SelectAllClasses()
.InheritedFrom(typeof(ICommand<>))
.BindSingleInterface());
Full test code (using Ninject, Ninject.Extensions.Conventions, xUnit and FluentAssertions nuget packages):
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>();
}
}
}
回答2:
I can't tell from your question whether ICommand<T>
is your own interface or if you're using WPF. If it's yours you can create a non-generic ICommand
interface and have ICommand<T>
descend from it. This would get you to:
kernel.Bind<ICommand>().To<InsertProductCommand>();
kernel.Bind<ICommand>().To<UpdateProductCommand>();
kernel.Bind<ICommand>().To<DeleteProductCommand>();
Then you can use reflection to locate classes that implement ICommand
and bind them.
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);
}
Even if you can't make ICommand<T>
descend from ICommand
, you can still get it done w/reflection. It's just a little more complicated. My suggestion above borrows from (1) below but (2) shows how to adjust for ICommand<T>
directly.
- Getting all types that implement an interface
- .NET - Getting all implementations of a generic interface?
Also you might consider creating a CommandModule : NinjectModule
to keep the reflection-related bindings away from the rest of your registrations.
来源:https://stackoverflow.com/questions/27434210/ninject-how-to-implement-command-pattern-with-ninject