views:

86

answers:

1

I have been trying to follow some WCF Data Services examples and have the following code:

private void OnSaveCompleted(IAsyncResult result)
    {
        Dispatcher.BeginInvoke(() =>
        {
            context.EndSaveChanges(result);
        });
    }

Which is called by the following:

this.context.BeginSaveChanges(SaveChangesOptions.Batch, this.OnSaveCompleted, null);

Now I am getting a little confused here. Firstly, the first bit of code is showing a syntax error of "Argument type lambda expression is not assignable to parameter type System.Delegate". So instead of blindly trying to follow the example code I tried to understand what was going on here. Unfortunately I am struggling to understand the error plus what is actually happening.

I feel a bit stupid as I am sure this is easy.

Thanks in advance for any enlightenment!

+1  A: 

The problem is that the compiler doesn't know what kind of delegate you're trying to convert the lambda expression to. You can fix that either with a cast, or a separate variable:

private void OnSaveCompleted(IAsyncResult result)
{        
    Dispatcher.BeginInvoke((Action) (() =>
    {
        context.EndSaveChanges(result);
    }));
}

or

private void OnSaveCompleted(IAsyncResult result)
{
    Action action = () =>
    {
        context.EndSaveChanges(result);
    };
    Dispatcher.BeginInvoke(action);
}
Jon Skeet
Thanks, but now I am getting "Cannot access non-static method 'BeginInvoke' in static context. I am more confused now, as this isn't a static method?
Jon Archway
@Jon: It thinks you're trying to use BeginInvoke as a static method on the Dispatcher class - whereas you want to use the Dispatcher *property* and then call BeginInvoke on the relevant instance. My guess is that this isn't in an appropriate class with a Dispatcher property. Having just seen that this is WCF, I'm not sure sure where you'd get a Dispatcher from. I'm more used to using it from WPF and Silverlight.
Jon Skeet
This is actually on a ViewModel class in a WPF application
Jon Archway
The ViewModel typically has no knowledge of the view, much less an associated dispatcher. You could go about using Dispatcher.CurrentDispatcher, but I strongly advise against it (you could easily end up in the wrong thread and the delegate will never be invoked); the best way I think would be to use something like MVVMLight's Messenger and send a message to the view - the message could contain the Action and the View could invoke it using its dispatcher.
Alex Paven
@Jon: It would *definitely* be worth updating the question to reflect that. The WCF part is irrelevant, but the WPF part is highly relevant. As for getting the dispatcher to the ViewModel - you could use the messenger as Alex suggested, or you could inject the Dispatcher nito the ViewModel... not as a Dispatcher, but in terms of a wrapper around it, implementing your own interface. That way you can test that you're doing the right thing in terms of thread safety. I've done this before, and it's worked well.
Jon Skeet
@Jon (Skeet) - Interesting idea; however I vaguely remember running into problems with something similar because under a test environment like MSTest (console app), the Dispatcher isn't started automatically and you must jump through some hoops to get your delegates to be invoked correctly. However I might be confusing it with something else... not sure anymore, it's late and I'm tired.
Alex Paven
@my previous comment: Actually nevermind, of course if you inject the dispatcher using an interface you can mock it while testing.
Alex Paven