VB Magic

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/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 ;-).

2012/11/08

OData AddTo using WinRT and VB.net

I was trying to find a way to POST a new entity to an OData feed and found it hard to find examples of doing this. After a bit of searching I found some C# code that allowed me to do an update. After converting this to VB I ended up with

    Private Sub addPersonButton_Click_1(sender As Object, e As RoutedEventArgs)

        Dim p As New Person
        p.name = personNameTextBox.Text
        If p.name <> "" Then
            _context.AddToPerson(p)
            _context.BeginSaveChanges(AddressOf ContextSaveChanges, p)
        End If
    End Sub

Person is part of an OData feed I am using e.g. http://server/odata.svc/Person

So I create a new instance of a Person and add a name to it (From a TextBox in the UI)

Then I use the AddToPerson method of the context and as there is no synchronous way in WinRT to SaveChanges, I used the BeginSaveChanges method of the context and passed through the address of the routine that will handle the callback.

    Private Function ContextSaveChanges(result As IAsyncResult) As Task
        Try
            _context.EndSaveChanges(result)
        Catch ex As Exception
            _message = ex.Message
            If ex.InnerException IsNot Nothing Then
                _message = _message & " - " & ex.InnerException.Message
            End If
            Me.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, AddressOf UpdateUI)
        End Try
    End Function

    Public Sub UpdateUI()
        personNameTextBox.Text = _message
    End

Above is the function that gets called and it will try to run EndSaveChanges and trap any error that occurred and display it on the UI. I originally put the personNameTextBox.Text = ex.Message in the catch section but that caused a Treading error [RPC_E_WRONG_THREAD]. So after a lot of routing around and the help of a colleague I discovered the Me.Dispacher.RunAsync function in which I had a lambda expression in to update the UI. This didn’t fail but also didn’t update the UI so I used a subroutine and put the AddressOf in the RunAsync command and this worked as expected.

Phew made it 😉

2012/10/30

Accessing and OData Feed in WinRT App using Visual Basic.NET

Filed under: .NET, VB.NET, Windows 8, Windows Store App — Tags: , , , — vbmagic @ 5:12 pm

As I’ve been searching the internet to try and find ways of accessing OData Feeds in visual basic .net and not finding much help I thought I’d put together a small example to help others.

This will use a demo OData feed to pull customer records from an example banking application. The feed is located in an Azure Cloud Service and is accessed by the following URL

http://t24irisdemo.cloudapp.net/tbank/Wealth.svc/

(This feed may be removed in the future)

The first thing to do is create a Visual Basic Blank Windows Store XAML application.

Also add a basic page called CustomerPage.

Add a Service Reference which points to the above URL

Visual Studio Dialog

Add Service Reference

We will only use Customers from the feed.

First display all the customers. To do this we need to add a Gridview to the main page XAML with an Item Template to display customer information. (I’m not a designer so I apologies for the rough look of the details 😉 )

        <GridView x:Name="defaultGridView" Grid.Row="1" Tapped="defaultGridView_Tapped_1" Margin="10" BorderBrush="#FFF7EC05" BorderThickness="1" FontFamily="Global User Interface" >
            <GridView.ItemTemplate>
                <DataTemplate>
                    <Grid Width="250" Height="135" Background="#002">
                        <Grid.RowDefinitions>
                            <RowDefinition Height="Auto" />
                            <RowDefinition Height="*" />
                            <RowDefinition Height="*" />
                        </Grid.RowDefinitions>
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="100" />
                            <ColumnDefinition Width="*" />
                        </Grid.ColumnDefinitions>
                        <TextBlock Grid.Column="0" Grid.ColumnSpan="2" Grid.Row="0" Text="{Binding Path=CustomerCode}" HorizontalAlignment="Center" FontWeight="ExtraBold" ></TextBlock>
                        <Image Grid.Column="0" Grid.Row="1" Grid.RowSpan="2" Source="{Binding Path=Photo}" Stretch="Uniform" Margin="4" VerticalAlignment="Center" HorizontalAlignment="Center"/>
                        <TextBlock Grid.Column="1" Grid.Row="1"  FontStyle="Italic" VerticalAlignment="Bottom">Name:</TextBlock>
                        <TextBlock Grid.Column="1" Grid.Row="2" Text="{Binding Path=Name}" VerticalAlignment="Top" HorizontalAlignment="Left" TextWrapping="Wrap"></TextBlock>
                    </Grid>
                </DataTemplate>
            </GridView.ItemTemplate>
        </GridView>

Next add the following code to the code behind for the Main Page.

Imports System.Data.Services.Client

' The Blank Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=234238

''' <summary>
''' An empty page that can be used on its own or navigated to within a Frame.
''' </summary>
Public NotInheritable Class MainPage
    Inherits Page

    Private Const IRISURL As String = "http://t24irisdemo.cloudapp.net/tbank/Wealth.svc"
    Private _context As New irisServiceReference.T24Wealth(New Uri(IRISURL))
    Private WithEvents _allCust As DataServiceCollection(Of irisServiceReference.Customers)

    ''' <summary>
    ''' Invoked when this page is about to be displayed in a Frame.
    ''' </summary>
    ''' <param name="e">Event data that describes how this page was reached.  The Parameter
    ''' property is typically used to configure the page.</param>
    Protected Overrides Sub OnNavigatedTo(e As Navigation.NavigationEventArgs)

        _allCust = New DataServiceCollection(Of irisServiceReference.Customers)
        AddHandler _allCust.LoadCompleted, AddressOf LoadAllCustComplete
		
		Dim query = _context.Customers
        _allCust.LoadAsync(query)
    End Sub

    Private Sub LoadAllCustComplete(sender As Object, e As LoadCompletedEventArgs)
        defaultGridView.ItemsSource = _allCust
    End Sub

    Private Sub defaultGridView_Tapped_1(sender As Object, e As TappedRoutedEventArgs)
        Dim gv As GridView = sender

        Dim item As irisServiceReference.Customers = gv.SelectedItem
        Dim customerNo As String = customerNo = item.CustomerCode

        Frame.Navigate(GetType(CustomerPage), customerNo)
    End Sub
End Class

The above code will pull all the customers from the feed and then bind them to the GridView. There is also a Tap event handler which will pull the customer number from the grid view item and navigate to the customer view page (I’ll leave this to you to have a go a designing the look and feel. In the following code example I assumed there is a text block called customerNameTextBlock).

If you put a debug point at the _allCust.LoadAsync(query) line. You will notice that Query is turned into a URL to the feed.

query = {http://t24irisdemo.cloudapp.net/tbank/Wealth.svc/Customers}

Next we need to put code behind the Customer info page to pull the tapped customer details back so that we can populate this page. The following code with do this.

' The Basic Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=234237
Imports System.Data.Services.Client

''' <summary>
''' A basic page that provides characteristics common to most applications.
''' </summary>
Public NotInheritable Class CustomerPage
    Inherits Common.LayoutAwarePage

    Private Const IRISURL As String = "http://t24irisdemo.cloudapp.net/tbank/Wealth.svc"
    Private _context As New irisServiceReference.T24Wealth(New Uri(IRISURL))
    Private WithEvents _allCust As DataServiceCollection(Of irisServiceReference.Customers)

        ''' <summary>
        ''' Populates the page with content passed during navigation.  Any saved state is also
        ''' provided when recreating a page from a prior session.
        ''' </summary>
        ''' <param name="navigationParameter">The parameter value passed to
        ''' <see cref="Frame.Navigate"/> when this page was initially requested.
        ''' </param>
        ''' <param name="pageState">A dictionary of state preserved by this page during an earlier
        ''' session.  This will be null the first time a page is visited.</param>
    Protected Overrides Sub LoadState(navigationParameter As Object, pageState As Dictionary(Of String, Object))

    End Sub

        ''' <summary>
        ''' Preserves state associated with this page in case the application is suspended or the
        ''' page is discarded from the navigation cache.  Values must conform to the serialization
        ''' requirements of <see cref="Common.SuspensionManager.SessionState"/>.
        ''' </summary>
        ''' <param name="pageState">An empty dictionary to be populated with serializable state.</param>
    Protected Overrides Sub SaveState(pageState As Dictionary(Of String, Object))

    End Sub

    Protected Overrides Sub OnNavigatedTo(e As Navigation.NavigationEventArgs)
        Dim customerNo As String = e.Parameter

        pageTitle.Text = "Customer " + customerNo

        _allCust = New DataServiceCollection(Of irisServiceReference.Customers)
        AddHandler _allCust.LoadCompleted, AddressOf allCustomerLoadCompleted

        Dim query = From c In _context.Customers
                    Where c.CustomerCode = customerNo
                    Select c

        _allCust.LoadAsync(query)
    End Sub

    Private Sub allCustomerLoadCompleted(sender As Object, e As LoadCompletedEventArgs)
        customerNameTextBlock.Text = _allCust.Single.Name
    End Sub

End Class

You’ll notice in this piece of code that a Linq query is used to pull the customer record from the feed. If you again put a debug point at _allCust.LoadAsync(query) you will notice the query is also transformed into a OData URL.

query = {http://t24irisdemo.cloudapp.net/tbank/Wealth.svc/Customers()?$filter=CustomerCode eq '100320'}

Hopefully this very simple example will help you get kick-started in developing WinRT applications that access OData feed.

Blog at WordPress.com.