VB Magic

2011/03/24

Client/Server experiment with MVC3 and Windows Phone 7 (Part 1 – The back end)

Filed under: Learning — Tags: , , , , — vbmagic @ 4:27 pm

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

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:

'...
If Membership.ValidateUser(loginData.Username, loginData.Password) = True Then
'...

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 🙂

http://www.tyranntrpg.org/

Blog at WordPress.com.