Custom Membership, Role and Profile: Silverlight RIA Service

How to enable your Silverlight 3 Application for RIA Services and use the built-in support for your custom Membership, Role and Profile providers.

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!

By Peter Bromberg   Popularity  (14797 Views)