As Microsoft is
leaving the Silverlight track and more and more focusing on HTML5 for cross
browser support, solutions are coming for several existing frameworks. One of
the things I’ve been looking at is the possibility to reuse your existing RIA
services ,who you currently use for your Silverlight application, and port them
to HTML5 enabled website.
The HTML client for ria services
started as an jQuery plugin called Ria.js. Currently this
project is hosted into the ASP.NET Single Page Application. This is a new
project template for building single page applications. The advantage of these
kind of application is that they run completely in the browser, this way it’s
pretty easy to enable your application to be used offline.
The JS client framework used for
communicating with the server is Upshot.js. This framework provides a rich
context for the object used on the client side, this includes change tracking,
paging, sorting, … More information of this subject can be found on the Denver
Developers blog. If you prefer watching a live demo, Steve Sanderson
gave one at TechDays Netherlands.
While searching on
the net for some more information, I noticed that the most examples focus on
the use of Web API for communication with the server. Next to the standard Web
API provider for upshot, there is also support for RIA and OData. Since my post
is handling RIA services, I‘ll be handling the riaDataProvider.
Building a live
demo
Perquisite:
·
VS 2011 beta: http://www.microsoft.com/visualstudio/11/en-us/downloads
·
WCF Ria Services V0 SP1: http://www.microsoft.com/download/en/details.aspx?displaylang=en&id=28357
So lets get started
by creating a new MVC4 project:
If we press the OK
button, we can choose which MVC4 project template we want to use. In our case
we choose for the Single Page Application.
This template
includes already some models, controls and views, but when we open up the
scripts folder, we will see the script references for upshot.js, knockout.js, …
Everything we need for building an HTML5 application with a rich context. But
these frameworks evolve fast and thank god we have a thing called Nuget so we
can easily update to the latest version. So this will be the next step in my tutorial,
updating to the latest version of the single page application. Search for
SinglePageAppliction in your Nuget packet management explorer or type the
following in your package manager explorer:
view source
print?
Install-Package SinglePageApplication.CSharp
Once this is done,
we need to add the reference to the ServiceModel so we can use the
domainService class of the RIA services. The following references need to be
added (note that these references come from the latest version of the WCF Ria
Services SDK V1, C:\Program Files (x86)\Microsoft SDKs\RIA Services\v0\Libraries\Server):
view source
print?
System.ServiceModel.DomainServices.Hosting
System.ServiceModel.DomainServices.Server
And one reference
from the SP2 of the WCF Ria Services SDK V1, C:\Program Files (x86)\Microsoft
SDKs\RIA Services\v0\Toolkit\Libraries\Server\SP2\
view source
print?
Microsoft.ServiceModel.DomainServices.Hosting
After adding the
references, we need to configure our DomainServices. First thing we need to do
is adding a new configuration section for the DomainService
view source
print?
<configSections>
<sectionGroup name="system.serviceModel">
<section name="domainServices"type="System.ServiceModel.DomainServices.Hosting.DomainServicesSection,
System.ServiceModel.DomainServices.Hosting, Version=0,
Culture=neutral, PublicKeyToken=31bf3856ad364e35"
allowDefinition="MachineToApplication" requirePermission="false" />
</sectionGroup>
</configSections>
Next we add a new
httpModule for the DomainService to the system.web section
view source
print?
<system.web>
<httpModules>
<add name="DomainServiceModule"type="System.ServiceModel.DomainServices.Hosting.DomainServiceHttpModule,
System.ServiceModel.DomainServices.Hosting, Version=0,
Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
</httpModules>
</system.web>
In the
system.webServer section, we add a new module for the DomainService
view source
print?
<system.webServer>
<validation validateIntegratedModeConfiguration="false" />
<modules runAllManagedModulesForAllRequests="true">
<add name="DomainServiceModule" preCondition="managedHandler"type="System.ServiceModel.DomainServices.Hosting.DomainServiceHttpModule,
System.ServiceModel.DomainServices.Hosting, Version=0,
Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
</modules>
</system.webServer>
As last we add the
following system.serviceModel config section. Most important thing here is that
we add a JSON endpoint, and on this endpoint we add the attribute
transmitMetadata=”true” so the metadata is send to our HTML client.
view source
print?
<system.serviceModel>
<domainServices>
<endpoints>
<add name="JSON"type="Microsoft.ServiceModel.DomainServices.Hosting.JsonEndpointFactory,
Microsoft.ServiceModel.DomainServices.Hosting, Version=0,
Culture=neutral, PublicKeyToken=31bf3856ad364e35"
transmitMetadata="true" />
</endpoints>
</domainServices>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true"
multipleSiteBindingsEnabled="true" />
</system.serviceModel>
Once all the
references and configuration sections are added, we can start to code. The
first thing we do is adding a new class and let it derive from DomainService.
view source
print?
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel.DomainServices.Hosting;
using System.ServiceModel.DomainServices.Server;
using SPAWithRiaServices.Models;
namespace SPAWithRiaServices.Services
{
/// <summary>
/// Domain Service responsible for todo
items.
/// </summary>
[EnableClientAccess]
public class TodoItemDomainService : DomainService
{
[Query(IsDefault = true)]
public IQueryable<TodoItem>
GetTodoItems()
{
IList<TodoItem> todoItems = new List<TodoItem>();
todoItems.Add(new TodoItem()
{ TodoItemId = 1, Title = "Todo
item 1", IsDone = false });
todoItems.Add(new TodoItem()
{ TodoItemId = 2, Title = "Todo
item 2", IsDone = false });
todoItems.Add(new TodoItem()
{ TodoItemId = 3, Title = "Todo
item 3", IsDone = false });
todoItems.Add(new TodoItem()
{ TodoItemId = 4, Title = "Todo
item 4", IsDone = false });
return todoItems.AsQueryable<TodoItem>();
}
}
}
The TodoItem Class
looks as follows:
view source
print?
using System.ComponentModel.DataAnnotations;
namespace SPAWithRiaServices.Models
{
public class TodoItem
{
[Key]
public int TodoItemId { get; set; }
[Required]
public string Title { get; set; }
public bool IsDone { get; set; }
}
}
Now we can go to
edit the views. The first view we need to change is the _Layout.cshtml in the
shared folder. In here we delete the following line:
view source
print?
<scriptsrc="@System.Web.Optimization.BundleTable.Bundles.ResolveBundleUrl("~/Scripts/js")"></script>
And add the
following references instead:
view source
print?
<script src="~/Scripts/jquery-js"
type="text/javascript"></script>
<script src="~/Scripts/modernizr-6-development-only.js"
type="text/javascript"></script>
<script src="~/Scripts/knockout-debug.js"
type="text/javascript"></script>
<script src="~/Scripts/upshot.js"
type="text/javascript"></script>
<script src="~/Scripts/upshot.compat.knockout.js"
type="text/javascript"></script>
<script src="~/Scripts/upshot.knockout.extensions.js"
type="text/javascript"></script>
<script src="~/Scripts/native.history.js"
type="text/javascript"></script>
<script src="~/Scripts/nav.js"
type="text/javascript"></script>
In the index.cshtml
in the home folder, we add the following:
The configuration for using upshot
view source
print?
<script type='text/javascript'>
upshot.dataSources = upshot.dataSources
|| {};
upshot.dataSources.GetTodoItems =
upshot.RemoteDataSource({
providerParameters: {
url: "/SPAWithRiaServices-Services-TodoItemDomainService.svc",
operationName: "GetTodoItems"
},
provider: upshot.riaDataProvider,
bufferChanges: true,
dataContext: undefined,
mapping: {}
});
</script>
The knockout view
view source
print?
<ol data-bind="foreach: todoItems">
<li><strong data-bind="text: Title"></strong>
<label>
<input data-bind="checked: IsDone" type="checkbox" />
Done
</label>
</li>
</ol>
And as last the
ViewModel definition
view source
print?
<script type="text/javascript">
function TodoItemViewModel() {
// Data
var self = this;
self.dataSource =
upshot.dataSources.GetTodoItems.refresh();
self.localDataSource =
upshot.LocalDataSource({
source: self.dataSource
, autoRefresh: true });
self.todoItems =
self.localDataSource.getEntities();
}
$(function () {
ko.applyBindings(new TodoItemViewModel());
});
</script>
When you run the
application now, you should see the todo items appear on your screen with a
little delay.
Conclusion
In your HTML applications you can
take advantage of your existing RIA services with a minimum of effort. For now
the possibilities are limited, but I think in the future we will be able to use
all features as they are present in SL. By adding the transmitMetadata
attribute to our JSON endpoint, it isn’t necessary to define the model again on
the client side.
No comments:
Post a Comment