In Part 3 we did the basics of setting up MVVM for our module. Now lets look at getting some data into our application using RIA Services. It’s fairly straight forward to set up RIA Services as the Visual Studio templates do all the lifting for you. Follow these steps to add RIA Services to your solution:
Note: I have change the table name for the Announcements table to Announcement so that the generated class names and collections make more sense.
- We already added the project we are going to use so go to AnnouncementServices and Add a New Item and choose Linq to Sql Classes, and call it AnnouncementDataClasses.dbml.
You can use Entity Framework here if you prefer.
- Now open the Server Explorer and open your DotNetNuke database and drag the Announcement table onto the design surface. You should see this;
- Now build your solution. You need to build it so that when we add the domain service the wizard can find the entity classes.
- Now add another new item called Domain Service Class and call it AnnouncementDomainService.cs
- Check all the boxes so that you get the meta data class, Client Access and the CRUD methods
- Add a reference from your web application MySilverlightApplication.Web to the AnnouncementServices project and build your application.
- Your solution explorer should look like this now
You can see the “magic” file generated by the RIA Services link to your Silverlight Application. Investigate this file and take note of the classes in there. This is how we will communicate with the server from our client side application. You can see that we have a partial class called Announcement which is an entity class.
If you’re wondering how RIA Communicates with the server when havent made any Service References have a look at line 348:
public AnnouncementDomainContext() :
this(new HttpDomainClient(new Uri("DataService.axd/AnnouncementServices-AnnouncementDomainService/", System.UriKind.Relative))){}
RIA uses a httpHandler to manage the communication so you need to make sure that you update the web.config of your site. Look at the app.config file in the AnnouncementServices project and copy the handler. If you are using IIS 7+ you will need to also add the handler to the system.webserver section. So add the following 2 items:
This goes in system.web handlers section
<add path="DataService.axd" verb="GET,POST" type="System.Web.Ria.DataServiceFactory, System.Web.Ria, Version=2.0.0.0,
Culture=neutral, PublicKeyToken=31bf3856ad364e35" validate="false"/>
and this goes in system.webserver handlers section
<add name="DataService" verb="GET,POST" path="DataService.axd" type="System.Web.Ria.DataServiceFactory, System.Web.Ria,
Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
The next step is to update our model so that it has a method to retrieve Announcement entities from the database.
Modify the IAnnouncementModel interface like so:
namespace MySilverlightApplication.Model
{public interface IAnnouncementsModel
{void GetAnnouncementList(Action<IEnumerable<Announcement>> action);
}
}
Tip: If you are using ReSharper you can include the generated file in the project to help it find the generated classes.
Now we need to implement the interface’s new method:
namespace MySilverlightApplication.Model
{public class AnnouncementsModel : IAnnouncementsModel
{public void GetAnnouncementList(Action<IEnumerable<Announcement>> action)
{ var context = new AnnouncementDomainContext();
var qry = context.GetAnnouncementsQuery().Where(a => a.ModuleID == 376);
context.Load(qry, op => action(op.Entities), null);
}
}
}
Now let’s write our first test. We are going to write a test that tests to make sure we have two Announcement entities loaded into the ViewModel when create a new ViewModel. We are going to do some TDD here so this test will fail until we write the minimum amount of production code to make it pass.
Open up your MSTest project and change the UnitTest.cs file name to ViewModelTests.cs
Now modify it so it looks like this:
namespace MySilverlightApplication.MSTest
{ [TestClass]
public class ViewModelTests : SilverlightTest
{readonly MainPageViewModel _viewModel = new MainPageViewModel(new MockAnnouncementsModel());
[TestMethod]
public void ViewModelDataLoadsCorrectly()
{ Assert.AreEqual(2, _viewModel);
}
}
}
Here you can see why we used two constructors on the MainPageViewModel class and why we instantiate it with an interface, so we can pass in our own MockAnnouncementsModel for testing.
Run the test and it will fail because it doesnt make much sense yet. What we need is something on the VM to hold a list of Announcement entities so lets add an ObservableCollection. Add the following code to the ViewModel
private ObservableCollection<Announcement> _announcements;
public ObservableCollection<Announcement> Announcements
{ get { return _announcements; } set
{ _announcements = value;
//OnPropertyChanged("Announcements"); }
}
Don’t worry about the OnPropertyChanged bit yet, we’ll get to that soon.
Ok so now we have somewhere to store our result lets populate it. Add the following method:
public void LoadModel()
{ _announcementsList.GetAnnouncementList(op =>
{ Announcements = op.AsObservableCollection();
if (LoadComplete != null) LoadComplete(this, null);
});
}
I’m using an extension method here called AsObservableCollection because I’m always turning IEnumerable in AsObservable. To do this add a new Silverlight class library project to your solution and call it Common. Then add a class called Extensions and modify it as below:
namespace Common
{public static class Extensions
{public static ObservableCollection<T> AsObservableCollection<T>(this System.Collections.Generic.IEnumerable<T> col)
{ var newcol = new ObservableCollection<T>();
foreach (var item in col)
{ newcol.Add(item);
}
return newcol;
}
}
}
Now add a reference to it from the MySilverlightApplication project and build your solution.
Next we’ll modify the Mock data model class so it returns 2 Announcement entities.
namespace MySilverlightApplication.MSTest
{class MockAnnouncementsModel : IAnnouncementsModel
{public void GetAnnouncementList(Action<IEnumerable<Announcement>> action)
{ action(MockData());
}
public IEnumerable<Announcement> MockData()
{ return new[]
{ new Announcement()
{ Title = "Test Title 1",
CreatedDate = new DateTime(2009, 9, 9)
},
new Announcement()
{ Title = "Test Title 2",
CreatedDate = new DateTime(2009, 9, 10)
}
};
}
}
}
Lastly modify the test so it calls the Load method and does it’s assert against the count of the collection like so:
[TestMethod]
public void ViewModelDataLoadsCorrectly()
{ _viewModel.LoadModel();
Assert.AreEqual(2, _viewModel.Announcements.Count());
}
Run the test and you should get a nice green tick:
Righto, good news the test passes so now we can go to our DNN web site and have a look at the module. Make sure you have some announcements in the database first :)
Make sure you attach the debugger to the process and put a break point in the LoadModel so that you can see the results coming back properly. Now if you’re wondering why the results don’t show up in the site that’s because we need to let the UI know that we have some results. To do that we Implement INotifyPropertyChanged. Since we will use this all over the place we’ll add it into the Common project.
Add a new class called ViewModelBase and modify it to look like:
namespace Common
{public class ViewModelBase : INotifyPropertyChanged
{public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(params string[] propertyNames)
{ if (null == PropertyChanged)
return;
foreach (var each in propertyNames)
{ PropertyChanged(this, new PropertyChangedEventArgs(each));
}
}
}
}
Now go the MainPageViewModel class and make it inherit the new ViewModelBase class and then uncomment the OnPropertyChanged line. Now rebuild and check your module. You should get this:
Ok that’s it for Part 4, we looked at how to write a test for the View Model and how to hook up RIA services and get data into our UI. Remember the code is available at CodePlex http://dnnsilverlightria.codeplex.com/