2011/03/24

I thought I might as well start with what I’m currently working on outside of work. It’s mainly a learning exercise to (re)teach myself WCF/MVC and learn to develop using windows phone 7.

I decided to start with the back end so created a new VB MVC3 project using Razor. (I’ve not used Razor yet so this is something else new to learn)

The reason I used MVC rather than just hosting a WCF service was to make use of the Membership system that automatically gets created with a new MVC site.

On this new site, I created a services directory and created a service called UserService which will make use of the ASP.net membership provider. I have also created a few Data Contracts that make the passing of data around a lot easier:

Imports System.ServiceModel
Imports System.Runtime.Serialization
<DataContract()>
Public Class LoginType
<DataMember()>
Public Property Username() As String
<DataMember()>
Public Property Password() As String
End Class
<DataContract()>
Public Class IntegerType
<DataMember()>
Public Property IntegerValue() As Integer
<DataMember()>
Public Property ErrorMessage() As String
End Class
<DataContract()>
Public Class TokenType
<DataMember()>
Public Property UserGroup() As IntegerType
<DataMember()>
Public Property TokenGUID() As Guid
<DataMember()>
Public Property errorMessage() As String
End Class
<DataContract()> _
Public Class UserDetailsType
<DataMember()>
Public Property name As String
<DataMember()>
Public Property email As String
<DataMember()>
Public Property lastLogin As Date
<DataMember()>
Public Property errorMessage As String
End Class

These are much smaller since VB10 where you do not need to put in the setter and getter part of properties.

I have also created a table call Players which will contain information about the user which is not already stored in the membership databases and then added a Linq to SQL class so that the service can make use of it:

Linq to SQL Class

Behind this class is the code to interrogate and update the database:

Partial Class TyranntDataContext
#Region "Player"
Public Function GeneratePlayer(ByVal username As String) As Guid
' We first need to access the user ID from the Users table
Dim usr = From user In aspnet_Users
Where user.UserName = username
Select user
' now check to see if the user record exists in the Player Table
If usr.Count <> 1 Then
Return Nothing
End If
Dim q = From player In Players
Where player.userID = usr.Single().UserId
Select player
' if player exists then exit
If q.Count > 0 Then
Return q.Single.guid
End If
' Create the player entry
Dim aPlayer As New Player
aPlayer.userID = usr.Single.UserId
aPlayer.creationDate = DateTime.Now
aPlayer.displayName = usr.Single.UserName
aPlayer.lastLogin = aPlayer.creationDate
' Create a false guid for now, this is only used by the client
aPlayer.guid = System.Guid.NewGuid
Players.InsertOnSubmit(aPlayer)
SubmitChanges()
Return aPlayer.guid
End Function
Public Function GeneratePlayerGuid(ByVal username As String) As Guid
' generate new guid
Dim g As Guid = System.Guid.NewGuid
' find the player record
Dim p = From player In Players
Where player.aspnet_User.UserName = username
Select player
' Update player record with new GUID
p.Single.guid = g
SubmitChanges()
Return g
End Function
Public Function GetPlayerDetails(token As Guid) As UserDetailsType
Dim p = From player In Players
Where player.guid = token
Select player
Dim details As New UserDetailsType
details.name = p.Single.displayName
details.email = p.Single.aspnet_User.aspnet_Membership.Email
details.lastLogin = p.Single.aspnet_User.LastActivityDate
Return details
End Function
Public Sub UpdatePlayerDetails(token As Guid, details As UserDetailsType)
Dim p As Player = GetPlayer(token)
p.displayName = details.name
p.aspnet_User.aspnet_Membership.Email = details.email
SubmitChanges()
End Sub
Private Function GetPlayer(token As Guid) As Player
Dim p = From player In Players
Where player.guid = token
Select player
Return p.Single
End Function
#End Region
End Class

And now the Service contract which allows users to log in, get their details and update part of the details. The Service Contract code is:

Imports System.ServiceModel
' NOTE: You can use the "Rename" command on the context menu to change the interface name "IUserService" in both code and config file together.
<ServiceContract()>
Public Interface IUserService
' User management routines
' Log in to website created user
<OperationContract()>
Function Login(ByVal loginData As LoginType) As TokenType
' Get user details
<OperationContract()>
Function GetUserDetails(token As String) As UserDetailsType
' Update user details
<OperationContract()>
Function UpdateUserDetails(token As String, details As UserDetailsType) As String
End Interface

And now the Bit the does all the work:

' NOTE: You can use the "Rename" command on the context menu to change the class name "UserService" in code, svc and config file together.
Public Class UserService
Implements IUserService
Public Function Login(loginData As LoginType) As TokenType Implements IUserService.Login
Dim token As New TokenType
Try
If Membership.ValidateUser(loginData.Username, loginData.Password) = True Then
' User is valid so now update the player record with GUID
Using db As New TyranntDataContext
' Make sure that the user has a player record
Dim playerGUID As Guid = db.GeneratePlayer(loginData.Username)
If IsNothing(playerGUID) Then
' something went wrong
End If
token.TokenGUID = playerGUID
token.errorMessage = ""
End Using
Else
token.errorMessage = "fail"
End If
Catch ex As Exception
token.errorMessage = ErrorMessage(ex)
End Try
Return token
End Function
Public Function GetUserDetails(token As String) As UserDetailsType Implements IUserService.GetUserDetails
Dim details As New UserDetailsType
Dim playerGUID As Guid = New Guid(token)
Try
Using db As New TyranntDataContext
details = db.GetPlayerDetails(playerGUID)
End Using
Catch ex As Exception
details.errorMessage = ErrorMessage(ex)
End Try
Return details
End Function
Public Function UpdateUserDetails(token As String, details As UserDetailsType) As String Implements IUserService.UpdateUserDetails
Dim playerGUID As Guid = New Guid(token)
Try
Using db As New TyranntDataContext
db.UpdatePlayerDetails(playerGUID, details)
End Using
Catch ex As Exception
Return ErrorMessage(ex)
End Try
Return ""
End Function
Private Function ErrorMessage(ex As Exception) As String
Dim err = ex.Message
If Not IsNothing(ex.InnerException) Then
err += +vbCr + ex.InnerException.Message
End If
Return err
End Function
End Class

For now the idea is if an exception occurs, it displays on the phone to try to help debug the code.

I did not have to code a membership system as this is already handled by asp.net so all I needed the service to do was to call this function:

I have used a token based system so that once the user is logged in, and has received a GUID token, that will be used to identify them from that point onwards. I plan to put an expiry date and time on the token which will make the user have to re-login after a period of inactivity.

I’ve not really gone into using MVC3 yet as I’m mainly working on learning to code on the Windows Phone 7, but I needed something for the phone to connect to.

The code above is in early stages and will most likely change but setting up basic web services is a very easy thing to do in .net

Next entry will show the code for the Windows Phone 7 project.

You may have noticed Tyrannt mentioned above. For anyone interested, it’s a link to this project I’ve been toying with for years 🙂