How to inject an action into a command using Ninject?

空扰寡人 提交于 2020-01-06 19:34:01

问题


Actually exploring the Command Pattern and finds it pretty interesting. I'm writing a WPF Windows App following the MVVM Architectural Pattern.

I've begun with these post which explain the basics.

  • Basic MVVM and ICommand usuage example
  • Simplify Distributed System Design Using the Command Pattern, MSMQ, and .NET

Now that I was able to break user actions into commands, I thought this could be great to inject the commands that I want. I noticed that the commands are found into the ViewModel in the first referenced article, So I thought that would be great if I could use them along Ninject and actually inject my command into my view model using a binding that would look like the following:

kernel
    .Bind<ICommand>()
    .To<RelayCommand>()
    .WithConstructorArgument("execute", new Action<object>(???));

But then, what to put in here ???. The expected answer is a method. Great! I just need a method to be put in there.

Because the first article simply initialize its commands within the ViewModel constructor, it is easy to say what method should be executed on the command execute call.

But from within the CompositionRoot? This is no place to put a method that will do anything else than bind types together through whatever DI container you're using!

So now, I've come across the Interceptor Pattern using Ninject Extensions. This looks like it could suits my requirements, and there is a bit of confusion here, if I may say. Not that the articles are confusing, they're not. I'm confused!

  • Using Ninject.Extensions.Interception Part 1 : The Basics

  • Using Ninject.Extensions.Interception Part 2 : Working With Interceptors

Also, there is this answer from BatteryBackupUnit who always sets great answers.

  • Ninject - How to implement Command Pattern with Ninject?

But now, I can't see how to glue it all up together! Humbly, I'm lost.

So here's my code so far.

RelayCommand

public class RelayCommand : ICommand {
    public RelayCommand(Action<object> methodToExecute, Predicate<object> canExecute) {
        if(methodToExecute == null)
            throw new ArgumentNullException("methodToExecute");

        if(canExecute == null)
            throw new ArgumentNullException("canExecute");

        this.canExecute = canExecute;
        this.methodToExecute = methodToExecute;
    }

    public bool CanExecute(object parameter) {
        return canExecute != null && canExecute(parameter);
    }

    public event EventHandler CanExecuteChanged {
        add {
            CommandManager.RequerySuggested += value;
            canExecuteChanged += value;
        }
        remove {
            CommandManager.RequerySuggested -= value;
            canExecuteChanged -= value;
        }
    }

    public static bool DefaultCanExecute(object parameter) { return true; }
    public void Execute(object parameter) { methodToExecute(parameter); }
    public void OnCanExecuteChanged() {
        var handler = canExecuteChanged;
        if(handler != null) handler(this, EventArgs.Empty);
    }

    public void Destroy() {
        canExecute = _ => false;
        methodToExecute = _ => { return; };
    }

    private Predicate<object> canExecute;
    private Action<object> methodToExecute;
    private event EventHandler canExecuteChanged;
}

CategoriesManagementViewModel

public class CategoriesManagementViewModel : ViewModel<IList<Category>> {
    public CategoriesManagementViewModel(IList<Category> categories
        , ICommand changeCommand
        , ICommand createCommand
        , ICommand deleteCommand) : base(categories) {
        if(changeCommand == null) 
            throw new ArgumentNullException("changeCommand");

        if(createCommand == null)
            throw new ArgumentNullException("createCommand");

        if(deleteCommand == null)
            throw new ArgumentNullException("deleteCommand");

        this.changeCommand = changeCommand;
        this.createCommand = createCommand;
        this.deleteCommand = deleteCommand;
    }

    public ICommand ChangeCommand { get { return changeCommand; } }
    public ICommand CreateCommand { get { return createCommand; } }
    public ICommand DeleteCommand { get { return deleteCommand; } }

    private readonly ICommand changeCommand;
    private readonly ICommand createCommand;
    private readonly ICommand deleteCommand;
}

I wonder, would it be better off using Property Injection, though I tend not to use it all?

Let's say I have CategoriesManagementView that calls another window, let's say CreateCategoryView.Show(), and then the CreateCategoryView takes over until the user is back to the management window.

The Create Command then needs to call CreateCategoryView.Show(), and that is what I tried from within the CompositionRoot.

CompositionRoot

public class CompositionRoot {
    public CompositionRoot(IKernel kernel) {
        if(kernel == null) throw new ArgumentNullException("kernel");
        this.kernel = kernel;
    }

    // 
    // Unrelated code suppressed for simplicity sake.
    //

    public IKernel ComposeObjectGraph() {
        BindCommandsByConvention();
        return kernel;
    }

    private void BindCommandsByConvention() {
        //
        // This is where I'm lost. I can't see any way to tell Ninject
        // what I want it to inject into my RelayCommand class constructor.
        //
        kernel
            .Bind<ICommand>()
            .To<RelayCommand>()
            .WithConstructorArgument("methodToExecute", new Action<object>());

        // 
        // I have also tried:
        //
        kernel
            .Bind<ICommand>()
            .ToConstructor(ctx => 
                 new RelayCommand(new Action<object>(
                     ctx.Context.Kernel
                         .Get<ICreateCategoryView>().ShowSelf()), true);
        //
        // And this would complain that there is no implicit conversion
        // between void and Action and so forth.
        //
    }

    private readonly IKernel kernel;
}

Perhaps I am overcomplicating things, that is generally what happends when one gets confused. =)

I just wonder whether the Ninject Interception Extension could be the right tool for the job, and how to use it effectively?


回答1:


I created a simple example of a command interacting with an injected service. might not compile since i'm going from memory. Maybe this can help you.

public class TestViewModel
{
    private readonly IAuthenticationService _authenticationService;

    public DelegateCommand SignInCommand { get; private set; }

    public TestViewModel(IAuthenticationService authenticationService) //Inject auth service
    {
        _authenticationService = authenticationService

        SignInCommand = new DelegateCommand(OnSignInRequest)
    }

    private void OnSignInRequest(Action<bool> isSuccessCallback)
    {
        var isSuccess = _authenticationService.SignIn();

        isSuccessCallback(isSuccess);
    }
}


}


来源:https://stackoverflow.com/questions/29035014/how-to-inject-an-action-into-a-command-using-ninject

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!