MVVM, Data Channel, MEF, WCF RIA Services and ViewModel Locator – Silverlight 4.0

In the Aspen project I decided to use a ViewModel locator to find the ViewModel a View should use, I also use MEF for composition of parts. In this post I’m going to write about the MVVM implementation we use in the Aspen project, but first about the Data Channel used in Aspen.

Data Channel

The Aspen project uses WCF RIA Services to do the communication from the client to the server, we decided to use WCF RIA Services because it generates a good “base” for our ViewModel. When writing unit-test when using WCF RIA Services, we “have” to mock the WebDomainClient which is an class passed to the client-side DomainContext constructor. The WebDomainClient will handle the communication with the DomainService. When we write our tests we don’t want to mock the WebDomainClient and we don’t want to have my ViewModel to be dependent on a DomainContext, so instead we use IViewModelDataChannel. An abstraction on top of WCF RIA Services. By using the IViewModelDataChannel we can remove WCF RIA Services and use other frameworks for the communication, for example WCF, WCF Data Services, Web Service etc. Here is an example of a IViewModelDataChannel created for getting the Members stored into Aspen:

<span>public</span> <span>interface</span> IListMemberViewModelDataChannel
{
   <span>void</span> GetAllMembersAsync(Action&lt;IEnumerable&lt;MemberPM&gt;&gt; getAllMembersCompletedCallBack);
}

 

The following is the implementation of the IViewModelDataChannel using WCF RIA Services DomainContext:

[Export(<span>typeof</span>(IListMemberViewModelDataChannel))]
<span>public</span> <span>class</span> ListMemberViewModelDataChannel : IListMemberViewModelDataChannel
{
   MemberDomainContext _memberDomainContext = <span>new</span> MemberDomainContext();

   <span>public</span> <span>void</span> GetAllMembersAsync(Action&lt;IEnumerable&lt;MemberPM&gt;&gt; getAllMembersCompletedCallBack)
   {
      _memberDomainContext.Load&lt;MemberPM&gt;(
               _memberDomainContext.GetMembersQuery(),
               loadOperation =&gt;
              {
                   getAllMembersCompletedCallBack(loadOperation.Entities);
               }
              , <span>null</span>);
   }
}


Note: MEF is used to Export the IViewModelDataChannels and Import them into the ViewModel.

The following is a part of a ViewModel where the IListMemberViewModelDataChannel is used:

[ExportViewModel(<span>&quot;ListMemberViewModel&quot;</span>)]
<span>public</span> <span>class</span> ListMemberViewModel : BaseViewModel, IListMemberViewModel
{
        
 <strong><em><span>private</span> IListMemberViewModelDataChannel _listMemberViewService;</em></strong>

 <span>private</span> IEnumerable&lt;MemberPM&gt; _members = <span>null</span>;
 <span>private</span> MemberPM _selectedMember = <span>null</span>;

 <span>private</span> <span>bool</span> _isBusy = <span>false</span>;

<strong><em> [ImportingConstructor]
 <span>public</span> ListMemberViewModel(IListMemberViewModelDataChannel listMemberViewService)</em></strong>
 {
    Contract.Requires(listMemberViewService != <span>null</span>);
    Contract.Ensures(_listMemberViewService != <span>null</span>);

<strong><em>    <span>this</span>._listMemberViewService = listMemberViewService;</em></strong>

    <span>this</span>.IsBusy = <span>true</span>;
            
    <strong><em><span>this</span>._listMemberViewService.GetAllMembersAsync(GetAllMembersCompleted);</em></strong>
  }

<strong><em>  <span>private</span> <span>void</span> GetAllMembersCompleted(IEnumerable&lt;MemberPM&gt; result)
  {
     <span>this</span>.Members = result;
     <span>this</span>.IsBusy = <span>false</span>;
  }</em></strong>

  <span>public</span> <span>bool</span> IsBusy
  {
     get { <span>return</span> _isBusy; }
     <span>private</span> set
     {
         _isBusy = <span>value</span>;
          NotifyPropertyChanged(<span>&quot;IsBusy&quot;</span>);
      }
  }

   <span>public</span> IEnumerable&lt;MemberPM&gt; Members
  {
      get { <span>return</span> _members; }
      <span>private</span> set
     {
         _members = <span>value</span>;
         NotifyPropertyChanged(<span>&quot;Members&quot;</span>);
      }
  }

  <span>public</span> MemberPM SelectedMember
  {
      get { <span>return</span> _selectedMember; }
      set
      {
          _selectedMember = <span>value</span>;
          NotifyPropertyChanged(<span>&quot;SelectedMember&quot;</span>);
      }
  }
}


By using MEF’s ImportConstructor attribute, we can inject parts into the constructor, in Aspen we are injecting the IViewModelDataChannel. The ViewModel will work against the IViewModelDataChennel instead of directly with WCF RIA Services. By doing so we can write unit-test against the ViewModel without needing to mock the WCF RIA Services WebDomainClient class, and we can also easily replace the way the ViewModel need to communicate with the server.



ViewModel Locator

A ViewModel locator is used to locate the ViewModel for a View and use MEF to compose all parts needed for the ViewModel, like importing a IViewModelDataChannel. We are using the ViewModel locator that John Papa wrote, with some modification because I thought Silverlight 4 RTM will have a bugged fixed, but that wasn’t true, so we will change the code back to John’s workaround later. So my suggestion is that you take a look at John’s code instead of the one added to Aspen at the moment.

 

If you want to know when I publish a new blog post, you can follow me on twitter: http://www.twitter.com/fredrikn

Read More

Javaforum den 27:e Maj

Nu är agendan spikad och klar. Observera att vi startar tidigare än vanligt!

16:00 Macka & mingel

16:30 Intro

16:40 Googles mobilplattform Android
Android är Googles försök att ena mobiltillverkare runt en öppen plattform för framtiden…

Read More