VB Magic

2013/01/09

Cannot import wsdl:portType error when adding a service reference

Filed under: .NET — Tags: , , , — vbmagic @ 1:25 pm

I came across this problem over the holiday period. The full error was:

Warning    4    Custom tool warning: Cannot import wsdl:portType
Detail: An exception was thrown while running a WSDL import extension: System.ServiceModel.Description.DataContractSerializerMessageContractImporter
Error: Type 'Newtonsoft.Json.Linq.JToken' is a recursive collection data contract which is not supported. Consider modifying the definition of collection 'Newtonsoft.Json.Linq.JToken' to remove references to itself.
XPath to Error Source: //wsdl:definitions[@targetNamespace='http://tempuri.org/']/wsdl:portType[@name='Ixxxxx']    C:\projects\Tyrannt RPG\Tyrannt\Tyrannt.Client.Web\Service References\CodexUpdateServiceReference\Reference.svcmap    1    1    Tyrannt.Client.Web

After doing a bit of searching I found the way to fix it was to remove the Newtonsoft.Json.Linq.JToken from the “Resue Types”:

Right click the service reference…

Service Reference

Select the Reuse Types in Selected References option and tick all the boxes except the Newtonsoft.Json package.

This was the blogpost that helped me solve it “Type ‘Newtonsoft.Json.Linq.JToken’ is a recursive collection data contract” While Adding Service Reference in VS2012

2012/12/19

Plumbing in Place

Filed under: .NET, Learning, VB.NET — Tags: , , , , , — vbmagic @ 1:54 pm

Spent last weekend and some of the start of my Xmas holiday trying to finalise the plumbing between my three tiers.

There are now two types of services in the middle tier.

  1. OData read-only service. This is where things like the web client can get access to the game entities that get generated and allow for the game codex to be built. These will remain open and available to anyone that wants to view them.
  2. Updates and membership information. This will be a WCF based service mainly due to requiring the “Updates” to be sent over the service bus to the back end part of the system. And this is what will end up being secured at a later date when I’ve managed to research it more.

The only part of the feed currently available is the Latest News feed which is shown on the front page of the client web site currently.

There is also a hidden part of the client website which handles adding new news articles.

This is basically how adding a new news article works…

  • Client Website sends a news article to the middle tier WCF Service AddNewsArticle
  • The middle tier web service generates a GUID (Checking job result list in the DB to make sure it doesn’t already exists) and then creates a Service Bus “Job” which contains the GUID and passes GUID back to client.
  • The back end tier receives the news article job and then adds the article to the database and writes an entry in the job result list saying that it successfully created or it failed to create the news article using the GUID as a key.
  • While the back end is processing this job the web client has requested the job result from the middle service tier using the GUID key it recieved earlier. This service will wait up to thirty seconds for the job result to be generated (Checking every second) and then return the result back to the web client. It will also send a job to the back end to delete the job result.
  • The web client will either go back to the front page and the news article will then be displayed, or if there was an error, it will display the error on the add news article page so the user can decided what to do (I.e. me try to fix it 😉 )

The upshot all this recent work on the plumbing side of things means that I can now proceed and create the more interesting bits of the game and hopefully make very few changes to the way the plumbing works.

The heart of this system is the Job that passes from the Service Tier to the Back end Tier. All the code required to do a “Job of work” is held in these jobs. All the service tier has to do is create a job of a specific type and populate the required information, then send this job over the service bus to the back end. All the back end has to the is “Process” the job which makes the back end very small and simple in the way of code. All the work is done in the Job Process function. Hence most of the future development will be in these jobs and the client.

Onwards and upwards.

2012/12/12

Just a quick update … It begins

Filed under: VB.NET — Tags: , , , , — vbmagic @ 11:26 am

The OData feed now has it’s first collection. As an initial test I created a news feed to keep the website updated on progress. I’ve not done the code to update the news feed just yet as I’m investigating securing WCF Rest services with the Azure ACS (Planning to go through the Windows Identity Foundation Patterns: On-Premise and Cloud Pluralsight course to help with this)

But the website now consumes this feed by displaying the last 5 news articles on the front page:

https://tyranntrpg.org/

This is the code in the controller that accesses the service:

    Private Const CODEXURL As String = "https://tyranntrpg.org:8443/OData/Codex.svc"
    Private _codexContext As New TranntCodexServiceReference.GlobalDbContext(New Uri(CODEXURL))

    Function Index() As ActionResult
        Dim model As List(Of NewsArticle)

        model = (From a In _codexContext.NewsArticles
                Order By a.DateAdded
                Select a).Take(5).ToList

        Return View(model)
    End Function

And this is the code in the Razor View which displays it:

    <li>
        <h4>Latest News</h4>
        <ul>
            @For Each a In Model
                @<li>@a.DateAdded.Date.ToLongDateString : <b>@a.Subject</b>
                    <p>@a.Body</p>
                 </li>
            Next
        </ul>
    </li>

2012/12/10

Multiple Database Context Mess and Intercepting writes on a WCF Data Service

Filed under: .NET, Azure, VB.NET — Tags: , , , , , , , — vbmagic @ 3:00 pm

Hi,

Another weekend on hacking code to get the Tyrannt project off the ground. Again I am concentrating on the middle OData web service tier. One of the rules I set myself was to not allow this tier to update the database. These update requests are only meant to be done on the back end tier via messages passed along the Azure Service Bus (ASB).

After an initial hiccup which resulted in my WCF service request falling over with “Not enough memory” (These are hosted in Azure extra small compute instances). I managed to get a working service in the cloud that exposed all my current tables.

Next I wanted to split the tables over multiple data services. I initially achieved this by creating multiple database contexts. This also allowed me to intercept the SaveChanges call in the database context when someone did a Post or Put (Below is my current thinking of how to do this, although this may change when I find it doesn’t work 😉 )

    Public Overrides Function SaveChanges() As Integer
        For Each change In ChangeTracker.Entries
            Dim job As tJob
            Dim entity = change.Entity
            Dim entityType = change.Entity.GetType.Name
            Select Case entityType
                Case "NewsArticle"
                    job = New tjUpdateNewsArticle
                    Dim na As NewsArticle = CType(entity, NewsArticle)
                    '.... Etc
            End Select
        Next

        Return MyBase.SaveChanges()
    End Function

But when it came to trying to actually run this, I kept getting an error saying:

The model backing the 'TyranntSubsetContext' context has changed since the database was created.

After a lot of searching it seemed like some people said this was possible and other said it was not. I decided to change tack and use a single database context that had all my tables in it, but make a duplicate one in my service project which I can use to intercept the saves. (The Back end tier will need normal database access as this will be doing the writes)

Anyway I still needed to expose different tables in different services. And as a nice surprise this time, it was very easy to do.

Here is my DB Context class:

Imports System.Data.Entity
Imports Tyrannt.Model.Email
Imports Tyrannt.Model.News
Imports Tyrannt.Model.Errors
Imports Tyrannt.Model.Membership
Imports Tyrannt.Infrastructure.Jobs
Imports Tyrannt.Infrastructure.Jobs.News

Public Class GlobalDbContext
    Inherits DbContext

    Public Property NewsArticles As DbSet(Of NewsArticle)
    Public Property EmailMessages As DbSet(Of EmailMessage)
    Public Property ErrorMessages As DbSet(Of ErrorMessage)
    Public Property Members As DbSet(Of Member)
    Public Property MemberTypes As DbSet(Of MemberType)

    'Public Overrides Function SaveChanges() As Integer
    '    For Each change In ChangeTracker.Entries
    '        Dim job As tJob
    '        Dim entity = change.Entity
    '        Dim entityType = change.Entity.GetType.Name
    '        Select Case entityType
    '            Case "NewsArticle"
    '                job = New tjUpdateNewsArticle
    '                Dim na As NewsArticle = CType(entity, NewsArticle)

    '        End Select
    '    Next

    '    Return MyBase.SaveChanges()
    'End Function

End Class

And this is my service code (For now I only want to expose the NewsArticles one) :

Imports System.Data.Services
Imports System.Data.Services.Common
Imports System.Linq
Imports System.ServiceModel.Web

Public Class News
    Inherits DataService(Of GlobalDbContext)

    ' This method is called only once to initialize service-wide policies.
    Public Shared Sub InitializeService(ByVal config As DataServiceConfiguration)

        ' Expose only the required tables with the relevant access rights
        config.SetEntitySetAccessRule("NewsArticles", EntitySetRights.All)

        ' General settings
        config.UseVerboseErrors = True
        config.DataServiceBehavior.MaxProtocolVersion = DataServiceProtocolVersion.V3
    End Sub

End Class

And it worked 🙂

For those curious, here is the service URI:

https://tyranntrpg.org:8443/OData/Codex.svc/

I cannot guarantee this service will always work or still exist in future but it’ll be there while I test client side code.

2012/12/06

WCF Service Application and “is ambiguous in the namespace ‘System.Data.Services.*'”

Hi,

I created a WCF Service Application project and then added nuget packages to enable me to use entity framework and Windows Azure. After this point. all my services were getting the following error:

Error 5
'DataServiceProtocolVersion' is ambiguous in the namespace 'System.Data.Services.Common'.

Which was referencing this line of code:

config.DataServiceBehavior.MaxProtocolVersion = DataServiceProtocolVersion.V3

In-particular the DataServiceProtocolVersion enum

After doing a lot of searching around on the web. I could not find any solution to this, so I started to go through removing references. (Thinking that something I added with Nuget is causing the conflict)

I removed the System.Data.Services.Client reference and Everything started working again.

I then used the Object Explorer which showed that DataServiceProtocolVersion now resided in the:

Microsoft.Data.Services.Client

Assembly.

Just posting this in case I can help save some time for anyone else who is suffering from this issue.

2012/12/03

Decisions Decisions – Solution Structure and how to get things done

Filed under: .NET, Azure, Learning — Tags: , , , , , , , — vbmagic @ 2:44 pm

I spent most of this weekend, researching different ways to achieve my project. There are certain things that I know I want to do:

Adopt a Three Tier Approach.

  1. Multiple Clients (Including a Website)
  2. A middle service layer that all the clients talk to which can read from the database but not update.
  3. A back end which is the only part of the solution allowed to update the database

The communication between the middle tier and the back end tier will be via the Azure Service bus.

I want the user of the application to be able to log in to the various client using their Windows Account/Google/Facebook ID’s.

With this in mind I was looking into ways to achieve these goals. I was mainly concentrating on what the middle service tier should be.

I wanted something that will make it easy to implement clients on non Microsoft platforms which drew me towards using the new ASP.Net MVC Web API. The downside of this is there is no metadata to describe the service which makes the client much harder to write. After a few discussions with a colleague, they suggested using WCF OData service which should allow making clients easy on the windows side. (Automatic generation of model classes when adding a service reference)

But I’m still open to suggestions from anyone reading this if they think there may be a better way.

The solution structure for the project as it stands now will be:

Tyrannt.Model (Holds the Code First EF Classes)

Tyrannt.Backoffice (Windows Azure Worker Role – The back end tier)

Tyrannt.OData (Web Role – the Middle WCF OData tier)

Tyrannt.Website (First client – will consume the OData service)

I would also like to make use of database migrations that are in Entity Framework 5. Which also brings up another question which it’s been hard to answer. Where is the best place to put the Database context. An easy solution would be to put one in both the Backoffice and OData projects but that means maintaining two separate classes that basically do the same job)

I’ve seen some posts about not having the database context in the same project as the Model classes. I’m trying to work out the best way to do this and still allow database migrations.

Feel free to comment this posts with suggestions ideas or even telling me that I’ve got it all wrong ;-).

2011/04/04

Dynamically creating pivot items and populating with data

Filed under: Learning — Tags: , , , , , , , — vbmagic @ 5:01 pm

I’ve spent the weekend trying to work out how to create the pages you see on a Pivot Page dynamically through code. It took a lot of web searching and trial and errors but I seem to have managed to get the basics of this worked out. I’m posting this to help other people who are trying to do the same thing. In this case it works, but if anyone else has a better/more efficient way of doing this I would be interested in looking at it.

The database/Web server has a directory full of images which the web service will pass back to the client using XML. Each image can belong to a image type. Each of these types will be a Pivot Item in the Pivot page. The list of images for each of these types will be displayed on each of these Pivot items.

I started by creating a pivot page and deleted any pivot items that where in the XAML. I then created the following Page Load event code:

    Private _client As New adminServiceReference.AdminServiceClient

    Private Sub PhoneApplicationPage_Loaded(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles MyBase.Loaded
        ' setup the event handlers for retrieving image types and image lists
        AddHandler _client.GetImageTypesCompleted, AddressOf client_GetImageTypesCompleted
        AddHandler _client.GetImagesCompleted, AddressOf client_GetImagesCompleted

        ' request the image types
        Dim str As New StrType
        str.TokenGUID = GetToken()
        _client.GetImageTypesAsync(str)
    End Sub

    Private Function GetToken() As Guid
        Dim result As Guid
        If _phoneAppService.State.TryGetValue("token", result) Then
            Return result
        Else
            MessageBox.Show("Failed to retrieve token")
            Return Nothing
        End If
    End Function

This will call the GetImageTypes web service on the server (I will document these in another post)
Once the request has been completed, all the image types will be sent back to the client in XML format.

    ' Routine to handle the Get Image Types completed event.
    Private Sub client_GetImageTypesCompleted(ByVal sender As Object, ByVal e As adminServiceReference.GetImageTypesCompletedEventArgs)
        If e.Error Is Nothing Then
            Dim types = e.Result
            ' did the server side report an error?
            If types.ErrorMessage = "" Then
                ' no so loop through each Image type in the xml result
                For Each ele In types.xml...<type>
                    ' We need to create a new Pivot item for each image type
                    Dim p As New PivotItem
                    ' set the header name to the lowercase version of the type name
                    ' this is to fit in with the standard way of showing pivot items
                    p.Header = ele.Value.ToLower
                    ' add the pivot item to the pivot page
                    imagePivotControl.Items.Add(p)
                    ' now request all the images under of that type
                    Dim str As New StrType
                    str.text = ele.Value
                    _client.GetImagesAsync(str)
                Next
            Else
                ' The server side got an error so display the error
                ' to aid in debugging.
                MessageBox.Show(types.ErrorMessage)
            End If
        Else
            ' the request to the webservice caused an error so
            ' display this error to try and help debug the problem.
            MessageBox.Show(e.Error.Message)
        End If
    End Sub

The comments in the above code should lead you through the creation and titling of the PivotItem and used XML Literals a very handy feature in VB to make code more readable when interacting with XML. (This should be implemented in C#)

For each image type pivot item created it will also request all the images for that image type which the following code will handle.

    ' a routine to handle the Get Images completed event.
    Private Sub client_GetImagesCompleted(ByVal sender As Object, ByVal e As adminServiceReference.GetImagesCompletedEventArgs)
        ' Did the request cause an error
        If e.Error Is Nothing Then
            Dim imgs = e.Result
            ' Did the web server get an error while processing the request.
            If imgs.ErrorMessage = "" Then
                ' no error so start searching the PivotItems to find the one matching
                ' the type that we requested the images for.
                For Each pg As PivotItem In imagePivotControl.Items
                    If pg.Header.ToString = imgs.type.ToLower Then
                        ' We have a match so create a listbox item to hold the details
                        Dim lb As New ListBox
                        ' we need a counter to display next to the image
                        ' this is only temorary until we have an image there
                        Dim cnt As Integer = 0
                        ' Loop through all the image elements in the returned XML
                        For Each ele In imgs.xml...<image>
                            ' We need to create a grid to store the information in the
                            ' list box item this is the equivilent to the following XAML
                            '
                            '   <grid>
                            '       <Grid.RowDefinitions>
                            '           <RowDefinition Height="Auto"/>
                            '       </Grid.RowDefinitions>
                            '       <Grid.ColDefinitions>
                            '           <ColDefinition Width="100"/>
                            '           <ColDefinition Width="Auto"/>
                            '       </Grid.RowDefinitions>
                            '    </grid>

                            ' Create the grid object
                            Dim grd As New Grid
                            ' Create the row definition objec
                            Dim rowdef As New RowDefinition
                            ' the row definition object uses a object called gridlength
                            ' we create one of these and set it to Auto
                            Dim glen As New GridLength
                            glen = GridLength.Auto
                            ' we add the height information to the row definition
                            rowdef.Height = glen
                            ' now we add the row definition to the list of row definitions
                            ' in the grid object
                            grd.RowDefinitions.Add(rowdef)
                            ' Now we need to create a column definition
                            Dim coldef As New ColumnDefinition
                            ' the first column has a width of 100 pixels for now, it will
                            ' contain an image eventually and we can leave it set to auto.
                            coldef.Width = New GridLength(100)
                            ' add the column definition to the list of column definitions in
                            ' the grid object.
                            grd.ColumnDefinitions.Add(coldef)
                            ' we now create the second column definition to auto which is the
                            ' same as we used with the row definiton so lets just re-use
                            ' this object.
                            coldef = New ColumnDefinition
                            coldef.Width = glen
                            ' add the final column definition to the collection of column
                            ' definitions in the grid object.
                            grd.ColumnDefinitions.Add(coldef)

                            ' Now we start to add the data, first we create a textblock that
                            ' will be used to hold the count.
                            ' This is the equivilent to the following XAML.
                            '
                            '   <TextBox Grid.Column="0" Grid.Row="0" Text="{cnt.string}"/>
                            '
                            Dim tb = New TextBlock
                            ' we set the row and column of the textblock to make it appear in
                            ' the correct loctation
                            Grid.SetColumn(tb, 0)
                            Grid.SetRow(tb, 0)
                            ' set the text property of the text block to the current count
                            tb.Text = cnt.ToString
                            ' add the textblock to the children objects of the grid.
                            grd.Children.Add(tb)
                            ' Now we add a new text block which will display the name of the
                            ' image file on the server.
                            ' This is the equivilent to the following XAML
                            '
                            '   <TextBox Grid.Column="1" Grid.Row="0" Text="{ele.value}"/>
                            '
                            tb = New TextBlock
                            ' set the row and column information of the text block.
                            Grid.SetColumn(tb, 1)
                            Grid.SetRow(tb, 0)
                            ' pull the content of the image item ( <image>value</image> ) and
                            ' put this into the text property of the text block
                            tb.Text = ele.Value
                            ' add the text block to the child objects of the grid.
                            grd.Children.Add(tb)

                            ' Now we create the list box item to add to the list box in
                            ' the pivot item.
                            Dim lbi As New ListBoxItem
                            ' add the grid to the listbox item
                            lbi.Content = grd
                            ' and add the list box item to the list box
                            lb.Items.Add(lbi)
                            ' increment the counter
                            cnt += 1
                        Next
                        ' add the list box to the pivot item.
                        pg.Content = lb
                    End If
                Next
            Else
                ' the server produced and error so display to aid in debugging
                MessageBox.Show(imgs.ErrorMessage)
            End If
        Else
            ' the request caused and error so display this to aid in debugging
            MessageBox.Show(e.Error.Message)
        End If
    End Sub

Again I have commented the code to lead you through how the listbox and listbox items are created from the XML results and used to populate the pivot item for each image type.

Jas

2011/04/01

Client/Server experiment with MVC3 and Windows Phone 7 (Part 2.2 – Login Page)

Filed under: Learning — Tags: , , , , , , — vbmagic @ 3:59 pm

After getting over the “Add Service Reference” problem mentioned in the previous post, the Login page was created:

login page graphic

Login Page

XAML file below:

https://vbmagic.wordpress.com/wp-admin/edit.php
<phone:PhoneApplicationPage 
    x:Class="TyranntPhone.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
    xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d" d:DesignWidth="480" d:DesignHeight="768"
    FontFamily="{StaticResource PhoneFontFamilyNormal}"
    FontSize="{StaticResource PhoneFontSizeNormal}"
    Foreground="{StaticResource PhoneForegroundBrush}"
    SupportedOrientations="PortraitOrLandscape" Orientation="Portrait"
    shell:SystemTray.IsVisible="True">

    <!--LayoutRoot is the root grid where all page content is placed-->
    <Grid x:Name="LayoutRoot" Background="Transparent">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>

        <!--TitlePanel contains the name of the application and page title-->
        <StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
            <TextBlock x:Name="ApplicationTitle" Text="TYRANNT" Style="{StaticResource PhoneTextNormalStyle}"/>
            <TextBlock x:Name="PageTitle" Text="login" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
        </StackPanel>

        <!--ContentPanel - place additional content here-->
        <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
            <Grid x:Name="LoginGrid" Background="Transparent">
                <Grid.RowDefinitions>
                    <RowDefinition Height="Auto"/>
                    <RowDefinition Height="Auto"/>
                    <RowDefinition Height="Auto"/>
                    <RowDefinition Height="Auto"/>
                    <RowDefinition Height="Auto"/>
                </Grid.RowDefinitions>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="Auto"/>
                    <ColumnDefinition Width="*"/>
                </Grid.ColumnDefinitions>
                <TextBlock x:Name="usernameTextBlock" Grid.Row="0" Grid.Column="0" Text="Username" VerticalAlignment="Center" />
                <TextBox x:Name="usernameTextBox" Grid.Row="0" Grid.Column="1" Text="" VerticalAlignment="Center" />
                <TextBlock x:Name="passwordTextBlock" Grid.Row="1" Grid.Column="0" Text="Password" VerticalAlignment="Center" />
                <PasswordBox x:Name="passwordPasswordBox" Grid.Row="1" Grid.Column="1" Password="" VerticalAlignment="Center" IsEnabled="False" />
                <CheckBox x:Name="rememberMeCheckBox" Grid.Row="2" Grid.Column="1" Content="Remember Me" IsEnabled="False" />
                <Button x:Name="loginButton" Grid.Row="3" Grid.ColumnSpan="2" Content="Login" IsEnabled="False" />
                <Button x:Name="logoutButton" Grid.Row="4" Grid.ColumnSpan="2" Content="Logout" Visibility="Collapsed" />
            </Grid>
        </Grid>
    </Grid>

</phone:PhoneApplicationPage>

Then helper methods were created to adjust the page layout and to help with passing information between pages. Also, to help with the application tomb-stoning, the following routines were created in the App.xaml.vb page to store and retrieve information:

    Private Sub StoreDetails()
		' The PhoneApplicationService stores information while the application is "Active"
        Dim phoneAppService As PhoneApplicationService = PhoneApplicationService.Current
		' The IsolatedStorageSettings will store informaiton on the phones flash memory to be
		' retrieved after the app has shut down and re-started.
        Dim settings As IsolatedStorageSettings = IsolatedStorageSettings.ApplicationSettings

		' Pull the token GUID from temp storage in the phones memory and write into the 
		' permanante storage on the phone's flash disk. Only if it exists
        Dim token As Guid
        If phoneAppService.State.TryGetValue("token", token) = True Then
            settings("token") = token
        End If
		' The same for the remembered username, this will allow the name to be recalled
		' after the app has been shut down in any way.
        Dim rname As String = ""
        If phoneAppService.State.TryGetValue("rememberedUsername", rname) = True Then
            settings("rememberedUsername") = rname
        End If
		' This stores the user group, for the moment, this should only really be done
		' if the app is paused to allow another app to run. If the app is exited the
		' user will have to log on again anyway.
        Dim group As String = ""
        If phoneAppService.State.TryGetValue("group", group) = True Then
            settings("group") = group
        End If
    End Sub

    Private Sub GetDetails()
		' The PhoneApplicationService stores information while the application is "Active"
        Dim phoneAppService As PhoneApplicationService = PhoneApplicationService.Current
		' The IsolatedStorageSettings will store informaiton on the phones flash memory to be
		' retrieved after the app has shut down and re-started.
        Dim settings As IsolatedStorageSettings = IsolatedStorageSettings.ApplicationSettings

		' The token us used to identify the user without the need for a username and password.
        Dim token As Guid
        If settings.TryGetValue("token", token) = True Then
            phoneAppService.State("token") = token
        End If
		' if the user has ticked the remember me box, this is the name they used to log on.
        Dim rname As String = ""
        If settings.TryGetValue("rememberedUsername", rname) = True Then
            phoneAppService.State("rememberedUsername") = rname
        End If
		' this will show if the user is a member of any special user group such as administrator
        Dim group As String = ""
        If settings.TryGetValue("group", group) = True Then
            phoneAppService.State("group") = group
        End If
    End Sub

I got the idea for this from the WP7 course at http://learnvisualstudio.net a good resource for learning most things .net.

Here is the code to handle the enabling and disabling of sections of the login page:

Partial Public Class MainPage
    Inherits PhoneApplicationPage

    Private _phoneAppService As PhoneApplicationService = PhoneApplicationService.Current

    ' Constructor
    Public Sub New()
        InitializeComponent()
    End Sub

    Private Sub EnableLogin(ByVal state As Boolean)
        usernameTextBox.IsEnabled = state
        passwordPasswordBox.IsEnabled = state
        loginButton.IsEnabled = state
    End Sub

    Private Sub usernameTextBox_TextChanged(ByVal sender As System.Object, ByVal e As System.Windows.Controls.TextChangedEventArgs) Handles usernameTextBox.TextChanged
        If usernameTextBlock.Text.Length > 3 And passwordPasswordBox.IsEnabled = False Then
            passwordPasswordBox.IsEnabled = True
            rememberMeCheckBox.IsEnabled = True
        ElseIf usernameTextBox.Text.Length <= 3 And passwordPasswordBox.IsEnabled = True Then
            passwordPasswordBox.IsEnabled = False
        End If
    End Sub

    Private Sub passwordPasswordBox_PasswordChanged(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles passwordPasswordBox.PasswordChanged
        If passwordPasswordBox.Password.Length > 3 And loginButton.IsEnabled = False Then
            loginButton.IsEnabled = True
        ElseIf passwordPasswordBox.Password.Length <= 3 And loginButton.IsEnabled = True Then
            loginButton.IsEnabled = False
        End If
    End Sub

    Private Sub PhoneApplicationPage_Loaded(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles MyBase.Loaded
        If RetrieveDetails() = True Then
            passwordPasswordBox.Focus()
            rememberMeCheckBox.IsChecked = True
        Else
            usernameTextBox.Focus()
        End If

    End Sub

    Private Sub PhoneApplicationPage_BackKeyPress(ByVal sender As System.Object, ByVal e As System.ComponentModel.CancelEventArgs) Handles MyBase.BackKeyPress
        Dim res As MessageBoxResult = MessageBox.Show("Are you sure you wish to exit", "Exit", MessageBoxButton.OKCancel)
        If res = MessageBoxResult.Cancel Then
            e.Cancel = True
        End If
    End Sub

    Private Sub StoreDetails()
        _phoneAppService.State("rememberedUsername") = usernameTextBox.Text
    End Sub

    Private Function RetrieveDetails() As Boolean
        Dim tmp As String = ""
        If _phoneAppService.State.TryGetValue("rememberedUsername", tmp) = False Then
            Return False
        Else
            usernameTextBox.Text = tmp
            Return True
        End If
    End Function

    Private Sub rememberMeCheckBox_Checked(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles rememberMeCheckBox.Checked
            StoreDetails()
    End Sub

    Private Sub DeleteDetails()
        _phoneAppService.State.Remove("rememberedUsername")
    End Sub

    Private Sub rememberMeCheckBox_Unchecked(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles rememberMeCheckBox.Unchecked
        DeleteDetails()
    End Sub

End Class

Now that this infrastructure is in place we get to the good bit, consuming the login service. The following routines were created to handle the login button click event and code to handle the call back for the login service.

    Private Sub loginButton_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles loginButton.Click
        Dim client As New userServiceReference.UserServiceClient
        AddHandler client.LoginCompleted, AddressOf client_LoginCompleted
        Dim login As New userServiceReference.LoginType
        login.Username = usernameTextBox.Text
        login.Password = passwordPasswordBox.Password
        client.LoginAsync(login)
        EnableLogin(False)
        If rememberMeCheckBox.IsChecked = True Then
            StoreDetails()
        End If
    End Sub

    Private Sub client_LoginCompleted(ByVal sender As Object, ByVal e As userServiceReference.LoginCompletedEventArgs)
        If e.Error Is Nothing Then
            Dim res As userServiceReference.TokenType = e.Result
            If res.errorMessage <> "" And res.errorMessage <> "fail" Then
                MessageBox.Show(res.errorMessage)
                EnableLogin(True)
            ElseIf res.errorMessage = "fail" Then
                MessageBox.Show("Username/Password incorrect")
                EnableLogin(True)
            Else
                _phoneAppService.State("token") = res.TokenGUID
                _phoneAppService.State("group") = res.UserGroup
                NavigationService.Navigate(New Uri("/pages/MemberPivotPage.xaml", UriKind.Relative))
                loginButton.Visibility = Windows.Visibility.Collapsed
                logoutButton.Visibility = Windows.Visibility.Visible
            End If
        Else
            MessageBox.Show(e.Error.Message)
            EnableLogin(True)
        End If
    End Sub

The contents of the username and password from the boxes on the page are used to generate a login type which is defined in the web service but I’ll re-show here:

<DataContract()>
Public Class LoginType

    <DataMember()>
    Public Property Username() As String

    <DataMember()>
    Public Property Password() As String
 
End Class

Then the callback routine to handle the completed login web service call was created and an event handler put into place. Then the ASYNC version of the service (Which is the only one available on Silverlight for Windows Phone) is called and the UI is changed to stop the user from trying to log in twice.
At the server end, the Membership service will attempt to validate the credentials. (A side note: To add a new user, you need to register on the website. Which is all handled automatically if you create an MVC3 website rather than a blank project.)

When the service returns the result of the login, firstly the app checks to see if the e.Error object has been set, this will indicate something went wrong with the service call and for now, it will just display the error message in a message box on the phone (Example of this is a timeout). Next the app will have a look at the returned object which is a TokenType which again is defined in the web service and I’ve shown again below:

<DataContract()>
Public Class TokenType

    <DataMember()>
    Public Property UserGroup() As String

    <DataMember()>
    Public Property TokenGUID() As Guid

    <DataMember()>
    Public Property errorMessage() As String

End Class

The errorMessage property of the TokenType class is used to pass back in the login failed for a username/password error. Basically if errorMessage is “” then everything went OK. If it contains the text “fail” then the username/password combination isn’t valid so the phone app will display that in a message box and reset the display to allow the user to attempt to log on again. If an exception occurred during the login process this is captured by the service and put into the errorMessage property. The phone app will check this and for now display that error message to aid in debugging.

If everything went well, then there should be a GUID in the TokenGUID property which will be used from that point onwards to identify that user. Later I will make every attempt to access the services change this GUID and pass the new one back. I will also add a time out so that the app can get the user to re-login after a period of inactivity has occurred.

If the login was successful we then enable a Logout button and collapse the login button and navigate to the “Member” section of the app using the following command:

NavigationService.Navigate(New Uri("/pages/MemberPivotPage.xaml", UriKind.Relative))

Next post on this experiment will be about what happens on this page.

2011/03/28

Client/Server experiment with MVC3 and Windows Phone 7 (Part 2.1 – A few problems)

The back end code was very basic and really only like it is to allow me to start attempting to write a client which will consume services.

Switching over to the Windows Phone 7 development, I hit my first problem. I created a VB.Net Windows Phone application and tried to add a Service Reference to the project. I gave the URL of the service that I created in the previous entry and it came back with the correct information. I then named the service and clicked the Ok button. A few seconds later I started to get errors and warnings. The main error is shown here:

Custom tool error: Failed to generate code for the service reference

One of the things that seemed to cause issues is the service definition using wsHttpBinding, but after double checking, I had used basicHttpBinding which is currently the only supported binding for Windows Phone 7

Initially, I thought this was a VB.Net problem, but after some searching there were a few C# users out there with the same issue. Most of them had one thing in common. They had Visual Studio 2010 Ultimate edition installed. I then found someone who mentioned that the Visual Studio Express edition does not have this issue. So I un-installed Ultimate from my laptop (Which took me a whole weekend due to the amount of things that got installed, think before clicking complete in future 😉 ) and then went to download Visual Studio 2010 Express edition. Once this was installed I then re-installed the Windows Phone 7 SDK and the VB extension to the SDK and hit my second problem.

This first release of Visual Basic support for the Windows Phone Developer Tools requires that you have Visual Studio 2010 Professional or higher, and does not yet support Visual Studio 2010 Express for Windows Phone

So stopped there as well. At this point I was starting to get annoyed at the lack of VB.net support in general after the promise of joint evolution of the two languages. So I went back to web searching to try to see if there was any other solutions apart from the manual approach. I found that one person mentioned that it didn’t work in Visual Studio 2010 Ultimate bit DID work in Visual Studio 2010 Professional. So back to installing software, This time Visual Studio 2010 Professional.

That done, I re-loaded the Windows Phone 7 project and right clicked the service and selected Update Service Definition. I waited with bated breath to see if it would work and it did. A huge sigh of relief I went on to start coding a client side of the learning project.

Note to self: “Just because you have an MSDN subscription which includes the Ultimate version of visual studio, it doesn’t mean that you should install it!”

…To be continued

Jas

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.