f you haven't had a look at Silverlight RIA Services yet, I'd recommend it. This
infrastructure simplifies two-way data access via services with Silverlight,
and includes support for Membership, Roles and Profiles. It really represents
a big step in the right direction for simplifying bidirectional data access and
business logic, especially with Silverlight.
In this article I will illustrate how to "hook up" your Silverlight Application
for authentication via Membership, authorization with Roles, and the ability
to save user-entered Profile data. I will use a modified version of the Altairis
classes that are available on codeplex.com. This means we will only have four
tables added to our database - Users, Roles, UsersInRole, and Profiles. All the
data is stored in visible columns which makes it easy to search the profiles
table, for example, for all those users who want to receive your email newsletter.
There are no stored procedures involved.
You will first need to install the RIA Services preview. I recommend that you also download the accompanying PDF, which provides a lot of
detail, and plenty of good code samples. We start by creating a new Silverlight
Project. Note the new checkbox at the bottom of the New Application dialog:

Checking this Link Option is what enables your project for the magic of RIA Services,
along with support for ASP.NET Membership, Roles and Profiles. In my UnBlog post here, I provifde a number of additional resource links that "didn't make it"
into the MIX '09 presentations. Check them out, please.
In the web.config, we need to enable the custom providers with the requisite elements:
connectionStrings>
<clear/>
<add name="LocalSqlServer" providerName="System.Data.SqlClient" connectionString="server=(local);Integrated Security=SSPI;database=TEST"/>
</connectionStrings>
authentication mode="Forms"/>
<membership defaultProvider="MyMembershipProvider">
<providers>
<clear />
<add name="MyMembershipProvider" type="PAB.Web.Providers.SimpleSqlMembershipProvider,PAB.Web.Providers" connectionStringName="LocalSqlServer" RequiresQuestionAndAnswer="true" EnablePasswordRetrieval="true" />
</providers>
</membership>
<roleManager enabled="true" defaultProvider="MyRoleProvider">
<providers>
<clear />
<add name="MyRoleProvider" type="PAB.Web.Providers.SimpleSqlRoleProvider,PAB.Web.Providers" connectionStringName="LocalSqlServer" cacheRolesInCookie="true" />
</providers>
</roleManager>
<profile enabled="true" automaticSaveEnabled="false" defaultProvider="MyProfileProvider">
<providers>
<clear />
<add name="MyProfileProvider" type="PAB.Web.Providers.SimpleSqlProfileProvider,PAB.Web.Providers" connectionStringName="LocalSqlServer" tableName="Profiles" keyColumnName="UserName" lastUpdateColumnName="LastUpdate" />
</providers>
<properties>
<add name="UserName" type="String" customProviderData="UserName;varchar;50" />
<add name="LastUpdate" type="DateTime" customProviderData="LastUpdate;datetime" />
<add name="FirstName" type="String" customProviderData="FirstName;varchar;50" />
<add name="LastName" type="String" customProviderData="LastName;varchar;50" />
<add name="Email" type="String" customProviderData="Email;varchar;300" />
<add name="Newsletter" type="Bool" defaultValue="True" customProviderData="Newsletter;bit;1" />
</properties>
</profile>
You can see above that with the custom Profile provider I use, we can actually define
the fields that match up with our Profile Table. Unlike the opaque way data is
stored in the default provider that ships with ASP.NET, all our "stuff"
is visible in columns that are easily SQL-searchable.
Now we need to create the server-side Service class that RIA will magically pick
up and create a Silverlight proxy for:
using System.Web.Profile;
using System.Web.Ria.ApplicationServices;
using System.Web.Security;
using PAB.Web.Providers;
namespace SLRIAAuth.Web
{
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Web.Ria;
using System.Web.Ria.Data;
using System.Web.DomainServices;
[EnableClientAccess]
public class AuthenticationService : AuthenticationBase<User>
{
}
[EnableClientAccess]
public class User : UserBase
{
public string UserName { get; set; }
public DateTime LastUpdate { get; set; }
public string Firstname { get; set; }
public string Lastname { get; set; }
public string Email { get; set; }
public bool Newsletter { get; set; }
}
[EnableClientAccess]
public class MembershipService : DomainService
{
[ServiceOperation]
public bool Authenticate(string userName, string password)
{
return Membership.ValidateUser(userName, password);
}
[ServiceOperation]
public void AddUser(string userName, string password, string email)
{
MembershipCreateStatus createStatus;
Membership.CreateUser(userName, password, email, null, null, true, out createStatus);
if (createStatus != MembershipCreateStatus.Success)
{
throw new DomainServiceException(createStatus.ToString());
}
}
}
}
We need an AuthenticationService, a User class, and a MembershipService. All these
need the [EnableClientAccess] attribute, and methods within them need the [ServiceOperation]
attribute.
One other thing, we need to change the Silverlight control on the ASPX page to host
the User DataContext that enables our Silverlight controls to be "user aware":
<% @ Register Assembly="System.Web.Ria" Namespace="System.Web.Ria" TagPrefix="ria" %>
<ria:SilverlightApplication ID="Silverlight1" runat="server" Source="~/ClientBin/SLRIAAuth.xap" MinimumVersion="3.0.40307.0" Width="100%" Height="100%" />
I don't do any data binding in this sample, but the above lines are your key to two-way
databinding with your Silverlight controls when RIA Services is used.
That takes care of the Server side. Whenever you do anything to your AuthenticationService
class and save it or build the app, a generated proxy class called (In this case)
SLRIAAuth.Web.g.cs is regenerated in your Silverlight application. You'll need
to enable "Show all Files" at the top of the Solution Explorer window
to see this. I generally bring it into the project to get Intellisense. This
will also clear up some VS / Resharper errors related to the fact that the generated
class file was not "included the project".
In the Silverlight Application, you should see something new in the App.xaml file:
<Application.Services>
<appsvc:WebUserService x:Name="UserService" />
</Application.Services>
This is what identifies our service proxy so we can code against it. Now here is
what I do in MainPage.xaml.cs:
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Ria.ApplicationServices;
using System.Windows.Shapes;
namespace SLRIAAuth
{
public partial class MainPage : UserControl
{
UserService _service;
public MainPage()
{
InitializeComponent();
Login();
}
void Login()
{
_service = App.Current.Services[
"UserService"]
as UserService;
UserContext ctx =
new UserContext();
ctx.Service.LoginCompleted +=
new EventHandler<LoginCompletedEventArgs>(Service_LoginCompleted);
LoginParameters parms =
new LoginParameters(
"admin",
"admin",
true);
ctx.Service.Login(parms);
}
void Service_LoginCompleted(
object sender, LoginCompletedEventArgs e)
{
string userName = _service.User.Name;
bool authenticated = _service.User.IsAuthenticated;
bool isAdmin = _service.User.IsInRole(
"admin");
MessageBox.Show(
"User: " +userName +
" Is Authenticated: " +authenticated.ToString( ) +
" Is Admin: " + isAdmin.ToString( ));
SLRIAAuth.Web.User user = (_service.User
as SLRIAAuth.Web.User);
user.Email =
"email@user.com";
user.Newsletter =
true;
user.LastUpdate = DateTime.Now;
user.Firstname=
"Joe";
user.Lastname =
"Blow";
_service.SaveUserCompleted +=
new EventHandler<SaveUserCompletedEventArgs>(service_SaveUserCompleted);
_service.SaveUser();
}
void
service_SaveUserCompleted(object sender, SaveUserCompletedEventArgs e)
{
if (e.Error!=null)
MessageBox.Show(e.Error.ToString());
else
MessageBox.Show("User logged in; profile properties created and saved");
}
}
}
We have a _service variable that is used to hold a reference to our proxy Service,
then use the UserContext to handle a normal call to the Login method. The sample
database SQL script installs a single user, "admin" with password "admin".
In the LoginCompleted event handler, I pop up a MessageBox showing that the user
was authenticated, and their role. Finally, I add the Profile properties for
the user and call _service.SaveUser(); The SaveUserCompleted handler shows the
results. And, if you go look in your database Profiles table, you'll see that
indeed, this user's profile data, which was not in the Profile table after you
ran my table script, has been saved. In a real app, you would of course have
nice Login and User Profile forms and so on; this sample is just a "proof
of concept".
You can download the complete Visual Studio 2008 Silverlight 3 solution here. Be sure to create a database "TEST", and run the provided SQL Script
to populate it. You can do a lot more with Silverlight RIA Services, this is
just the beginning.
It is easy for developers to be intimidated by the complexity of some of these new
offerings. Silverlight RIA services is relatively easy to learn, and it brings
a great deal of new power to you as a Silverlight developer. The March MIX 09
preview does everything it is supposed to do, and it works. The newest May '09
preview fixes bugs, and is even more stable. The addition of RIA Services to
Silverlight applications moves Silverlight 3 into a whole new realm for developers
of LOB Silverlight applications. In particular, have a look at the "Shared"
classes option which allows you to automatically replicate server-side entities
into the Silverlight client. If you take the time to study this, as I have, you
should definitely get "religion" about what Silverlight 3 and RIA Services
is going to offer!