Wednesday, August 3, 2011

Linking LightSwitch Data to Logged in Users

Note: if you like this post be sure to take a look at my October 4th post that contains an enhancement to this technique.

In my presentation LightSwitch in Context (which I will be presenting on Wednesday, September 28 at the Bay Area Database Developers .NET User Group) I demonstrate how LightSwitch can be used to create an administrative interface for a public facing website. In one part of the demo, I create a RIA service that can be used to query for a list of users from the .NET Membership service. I am starting to realize that this little trick can be quite handy, and I've already used in a paying project for a client. It is also a good demonstration of how easy it is to create a RIA service for a read-only data source that is outside of the normal scope of your application. RIA services can be intimidating, but if you know the right steps to follow they can be blown out in a few minutes using Visual Studio templates.

Here's how to create a RIA service that allows access to the current list of users in your application. Note that this only works if your using Forms authentication, i.e. the ASP.NET Membership provider. If you are using Windows authentication, you can do something similar but you'll have to replace the Membership code with calls to Active Directory. Also, this requires the full (Professional or better) version of Visual Studio; you can't create data extensions like this with the standard edition of LightSwitch.

Step 1: Add a WCF RIA Services Class Library to your project

With your LightSwitch application is open in Visual Studio, right-click the Solution icon and select Add->New Project... from the context menu. This will bring up the New Project dialog. Select template type Silverlight, then WCF RIA Service Class Library:


You can see that I'm calling my project UserDataService. After you add the project, you will see that template adds not one but two projects to your solution. One will be called UserDataService and one will be called UserDataService.Web. Since this blog is not discussing the fine points of RIA Services, the only thing you need to know is that you can ignore the UserDataService project completely, we'll be working only with UserDataService.Web.

Step 2: Add Membership Provider Assemblies

By default a RIA Services library doesn't have references to the library that implement the ASP.NET membership services. So you'll need to add the following references to the UserDataService.Web project:
  • System.Web
  • System.Web.ApplicationServices

    Step 3: Define Your User Entity

    In order to send a user list back to LightSwitch as a table, you'll need to define a Data Transfer Object (DTO) that will hold data about an individual user. The properties of the DTO will depend on what properties of the user you are interested in. In this example, we're sticking just to the login name:


    using System.ComponentModel.DataAnnotations;
    
    namespace UserDataService.Web
    {
        public class User
        {
            [Key]
            public Guid UserId { get; set; }
    
            public string UserName { get; set; }
        }
    }
    

    Note that we are including the Guid that is used by the Membership framework to uniquely identify users and marking this as the key to the entity. In general, you will always want to define a key for your DTOs.

    Step 4: Create The Service

    Now it's time to create the service itself. Right-click the UserDataService.Web icon and select Add->New Item.. from the context menu. In the Add New Item dialog, select template type Web, and Domain Service Class template:


    In this screenshot, I'm calling my class UserService, however in the example below, I'm using UserData as a class name. Now we get to the fun part, where we actually have to write some code:

    using System.Web.Security;
    
    public class UserData : DomainService
    {
        [Query(IsDefault = true)]
        public IQueryable<User> GetUsers()
        {
            var returnValue = new List<User>();
    
    #if DEBUG
            returnValue.Add(new User 
            {
                UserId = new Guid("{1F43C32F-D6E3-4CCE-A4A0-7BB9D7572C07}"), 
                UserName = "Tom"
            });
    
            returnValue.Add(new User 
            {
                UserId = new Guid("{F4D71F57-2CD1-4C60-B9BE-FA2529A9EE2D}"), 
                UserName = "Dick"
            });
            returnValue.Add(new User 
            {
                UserId = new Guid("{8FF1E47C-BA41-4586-8D23-A3079850FDAA}"), 
                UserName = "Harry"
            });
    #else
            var userList = Membership.GetAllUsers();
    
            foreach (MembershipUser user in userList)
            {
                returnValue.Add(new User 
                { 
                    UserId = (Guid)user.ProviderUserKey, 
                    UserName = user.UserName 
                });
            }
    #endif
    
            return returnValue.AsQueryable();
        }
    }
    
    Here are a couple of things to note:
    1. The Query attribute marks this method as an RIA service method that returns a queryable entity container.
    2. The basic structure of the code, in which you return a list of entities that you want to appear as a table, is extremely simple. You can use this same technique for any kind of data that you want to return as a read-only list.
    3. I've put a DEBUG condition in to populate my list with pretend users. This is important because LightSwitch in debug mode, with Forms authentication enabled, will have a valid Membership provider but won't have any actual users.
    Step 5: Add the Service to LightSwitch

    After you've rebuild your solution to set up the data service (important!), you are ready to add the service as a data source. Right-click on the Data Sources folder in LightSwitch, and select Add Data Source from the context menu. Then, select WCF RIA Service, click Next, then Add Reference, then select the Project tab. You want to add a reference to UserDataService.Web. Then you will be able to select your entity call User. This is a lot of wizard screens, but it goes pretty fast if you've set everthing up according to the instructions above. When you are done, a Users table will appear in LightSwitch, which looks something like this:


    Step 6: Link the User Data to LightSwitch tables

    Now that the Users table exists in LightSwitch, you can link other tables from your main data source. This is slightly different that creating a relationship within the main data source, because you have to create a foreign key in the linked table yourself. Recall that we're using a Guid as a key, so we'll want the foreign key to also be a Guid. In this example I'm linking to the SalesPersons table, so I want to create a new column call Login in SalesPersons of type Guid. Here's what the relationship dialog looks like when the relationship is set:


    Now that you've got the data linked, the RIA service data can be used just like any other LightSwitch data. This screen shot shows the selector that is automatically generated for you when the User link is formatted as an Auto Complete Box:



    Hopefully this simple example will get you started with RIA Services extensions. They're not as bad as they look, and when you need 'em, you need 'em.

    4 comments:

    edmiller47 said...

    I get the following error in step 4 of the blog: the type or namespace 'user' cannot be found. I followed the directions exactly and everything went okay until then. Any clues on what this could be?

    edmiller47 said...

    Figured out problem: I copied the code from your blog but didn't notice that the User class is capitalized in one place but lower case in another. Also needed to add
    using System.ComponentModel.DataAnnotations; to the User class definition.

    Works fine now!

    Anonymous said...

    Paul, thanks for your post, it was exactly what I needed. I'm having trouble getting a query out of the ria table and I posted about it here:

    http://stackoverflow.com/questions/8195784/lightswitch-query-error

    Would you have any thoughts?

    Unknown said...

    Great post!