May 2010 - Posts

There is a religious argument over whether it is important for view parameters to refrain from exposing implementation details such as the underpinning data store.

Why might that be bad?

The public interface of an object is effectively a contract. The consumer of such an interface has an opportunity to depend on the capabilities exposed to it, and once that happens the provider of those capabilities is obliged to guarantee their availability forever.

In short, don’t make any promises you aren’t prepared to keep forever.

Posted by peterw | with no comments

The good news

Manipulating the profile of the currently logged in user is easy. WebContext.Current.User providers a derivative of ProfileBase exposing profile properties as strongly typed object properties. You can set the properties and invoke WebContext.Current.User.Save() to commit the change to whatever profile provider is configured.

It’s dead easy to find out whether the current user is in a role: the method ProfileBase.IsInRole(string rolename) returns true or false. It also sports a Roles collection for more elaborate comparisons, as well as an IsAuthenticated Boolean property the meaning of which should be obvious.

The bad news

Where the support for authorisation is much less complete is in creating the user, assigning roles, and setting the profile properties.

Or maybe it’s not so bad

The rationale for this is it’s not hard to code this up yourself in a web service. Knock up a page that collects all the info, and pass it all to a web method. This runs at the server, and you can write methods like these:

   68     [OperationContract]

   69     public MembershipCreateStatus CreateUser(string username, string password, string email, string question, string answer, string backingStore, string[] roles)

   70     {

   71       MembershipCreateStatus createStatus;

   72       Membership.CreateUser(username, password, email, question, answer, true, null, out createStatus);

   73       if (createStatus == MembershipCreateStatus.Success)

   74       {

   75         if (roles.Any())

   76           Roles.AddUserToRoles(username, roles);

   77         ProfileBase profile = ProfileBase.Create(username);

   78         profile.SetPropertyValue("BackingStore", backingStore);

   79         profile.Save();

   80       }

   81       return createStatus;

   82     }

   83 

   84     [OperationContract]

   85     public Guid GetUserId(string username)

   86     {

   87       return (Guid)Membership.FindUsersByName(username)[username].ProviderUserKey;

   88     }

   89 

   90     [OperationContract]

   91     public void CreateUserPrefs(string dbName, Guid userId)

   92     {

   93       using (wasEntities entities = new wasEntities())

   94         entities.CreateUserPrefs(dbName, userId);

   95     }

   96 

BackingStore is a profile property in the application from which this was clipped. It’s a sort of bureau service and we have a database per corporate customer so the data can be backed up and held in escrow per customer. The important thing to know about BackingStore is that is must be set before the login can be used, because application start-up uses this information.

wasEntities is the Entities object for an EF model of the Windows Authentication Services (WAS) database ASPNETDB. CreateUserPrefs creates an entry in a table of user preferences. This information ought to live in profile properties but older systems sharing the database depend on this table. It’s a legacy thing predating our use of WAS and profiles; once upon a time this was a desktop application.

The stored procedure behind this web method looks like this:

    1 ALTER PROC [dbo].[CreateUserPrefs](@dbName varchar(50), @userId uniqueidentifier) as

    2 EXEC ('DELETE '+@dbName+'..UserPreference WHERE UserId='''+@userId+'''')

    3 EXEC ('INSERT INTO '+@dbName+'..UserPreference([UserId],[HomeLatitude],[HomeLongitude]...

It was brought into wasEntities as a function import.

This is a good example of a technique I cooked up to get around the fact that while it’s not hard to set the connection string at session start-up, it’s difficult to change dynamically. This stored procedure is defined in the WAS database, but operates on the database named in its first parameter.

Posted by peterw | with no comments