2
votes

I'm building a WP7 app that gets all its data through WCF services. I want to implement MVVM-Light in it but in tutorials I've done, I see that in the ViewModelLocator it wants to create a static instance of all my ViewModels when the app starts. My problem is that in my constructors for my VM's is where I make my WCF calls with the results of course coming back in the callback. It is in the callback where I assign the results to my observable collection that my Views see. This works fine when not using MVVM-Light but if I implement it I can't have these WCF calls at startup as they pass parameters not known/available until the user uses the app (selecting items, etc.) I tried moving my calls to the WCF to the getter of my observable collection but it keeps calling the WCF in an endless loop. In all the samples of MVVM-light that I've seen I haven't seen anyone calling WCF services. Any suggestions as to where to put my WCF calls in the model?

3

3 Answers

2
votes

As ever in MVVM there is no right way ... as it is about feedom ... :-)

You can put the code into your view model if you want. However, you then will have to also generate the design time data in the view model too - if you want to use it.

The big disadvantage with this approach is that you introduce a coupling between your view model and your service code (as you will have to instantiate the service in your view model). The general approach to de-couple the components is to create an interface describing your service and injecting an instance of an object implementing this interface into your view model in it's constructor. This enables you to create a design time and a run time implementation and your view model does not care which one it uses - i.e. the objects are decoupled.

Edit

Injecting in my post does not imply hat you use an injection container/framework, it only means that you use an interface to abstract your service behavior, and then pass an implementor or of this interface into the constructor of your view model. You now can pass different implementatios of the interface when you create your view model, e.g. in a view locator.

The pattern suggested is called "inversion of control" and injecting is a technique to pass objects created elsewhere into the class you creating. Through this your our class is now shielded from any detail knowledge of the implementators and they become interchangeable.

Inversion of control containers - like ninject, unity, etc. - just help you automatically resolving dependencies, however, they are not required for using the in version of control pattern.

1
votes

I don't think it's good to put WCF call in ViewModel, you should encapsulate the WCF call in another class. Your ViewModel should only contain the GUI logic. I think what you get from the WCF call is the model, you can use the model to create ViewModel.

1
votes

Here is my example of my CommonServiceHelper.cs file

   public void GetUserSettings(UserInfoIn input, Action<UserInfoOut, Exception> callback)
    {
        var proxy = new CommonServiceClient();

        try
        {
            proxy.GetUserSettingsCompleted += (sender, eventargs) =>
            {
                var userCallback = eventargs.UserState as Action<UserInfoOut, Exception>;
                if (userCallback == null)
                    return;

                if (eventargs.Error != null)
                {
                    userCallback(null, eventargs.Error);
                    return;
                }
                userCallback(eventargs.Result, null);
            };
            proxy.GetUserSettingsAsync(input, callback);
        }
        catch (Exception ex)
        {
            proxy.Abort();
            //ErrorHelper.WriteErrorLog(ex.ToString());
        }
        finally
        {
            if (proxy.State != CommunicationState.Closed)
            {
                proxy.CloseAsync();
            }
        }
    }

Then in the ViewModel I call it like this:

               var serviceCommon = new CommonServiceHelper();
                            serviceCommon.GetUserSettings(userSettingsInput, (result, error) =>
                            {
                                if (result != null && error != null)
                                {
                                    //everything is ok
                                }
                                else
                                {
                                    //handle errors
                                }
                            });