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:
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