Friday, May 23, 2008

EntitySpaces and Silverlight Demo

Part 2: Filtering and Changing EntitySpaces Data

kick it on DotNetKicks.com

Jon_Davis
Written by Jon Davis
http://www.jondavis.net/

 

 

Run the Demo Here  ==> http://developer.entityspaces.net/ES2008/Demos/Silverlight/PartTwo
Download the Source Here ==> http://www.developer.entityspaces.net/downloads/EntitySpacesSilverlightDemo2.zip 
 

Review of Part One

In Part One, I described the process of getting Silverlight to interact with a WCF web service, and I showed how I bound EntitySpaces based data objects (WCF client proxies) to Silverlight controls, specifically a DataGrid control. While there were a lot of pages of review in Part One, it was mostly point-and-click “configuration coding”, as MyGeneration, EntitySpaces, and Visual Studio did most of the work for me.

linqII_one

Figure 1 - EntitySpaces RIA running in Safari 3.1 (on Windows) with just about 20 lines of hand-written client-server code and XAML markup.

 

But showing data in a grid isn’t very exciting. It’s time to look at some interaction with data and give EntitySpaces a little more credit than just a data wrapper.

 

Category-Filtered Products (Client-Server Method)

Let’s assume that the user wants to filter the products list by category. I want to keep this demonstration as simple as possible, while proving out technical implementations of basic scenarios, so I’m not going to make any significant effort just yet to make this look pretty.

linqII_two  

Here’s the XAML:

<UserControl
    x:Class="EntitySpacesSilverlightDemo_Silverlight.Page"
    xmlns:Controls="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data" 
    xmlns="http://schemas.microsoft.com/client/2007"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Grid x:Name="LayoutRoot" Background="Gray">
        <Grid.RowDefinitions>
            <RowDefinition Height="75" />
            <RowDefinition />
        </Grid.RowDefinitions>
        <TextBlock Grid.Row="0" Text="EntitySpaces on Silverlight ~ Northwind Demo" VerticalAlignment="Center" HorizontalAlignment="Center" />
        <Grid Grid.Row="1">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="200" />
                <ColumnDefinition />
            </Grid.ColumnDefinitions>

            <TextBlock VerticalAlignment="Center" HorizontalAlignment="Center" Text="Please Wait" Visibility="Collapsed" x:Name="WaitText" />
            <ListBox Width="300" Grid.Column="0" x:Name="ViewSelector" SelectionChanged="ViewSelector_SelectionChanged">
                <ListBoxItem Content="Products: All"></ListBoxItem>
            </ListBox>

            <Controls:DataGrid AutoGenerateColumns="True" x:Name="ESDataGrid" Grid.Column="1" />
        </Grid>
    </Grid>
</UserControl>

For starters and for the sake of continuing the discussion of ES over WCF, I’m going to make this a client-server filter—I’ll pass in a CategoryID as a parameter in my WCF request for products.

Here’s the complete client-side code-behind for Page.xaml.cs, additions in bold+brown:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Net;

using EntitySpacesSilverlightDemo_Silverlight.Northwind;

namespace EntitySpacesSilverlightDemo_Silverlight
{
    public partial class Page : UserControl
    {
        NorthwindClient.NorthwindClient ESNorthwind;

        public Page()
        {
            InitializeComponent();
            ESNorthwind = new NorthwindClient.NorthwindClient();

            ESNorthwind.GetCategoriesCompleted += ApplyCategoriesToList;
            ESNorthwind.GetProductsCompleted += new EventHandler<NorthwindClient.GetProductsCompletedEventArgs>(NorthwindClient_GetProductsCompleted);
            ESNorthwind.GetProductsByCategoryCompleted += new EventHandler<GetProductsByCategoryCompletedEventArgs>(Northwind_GetProductsByCategoryCompleted);

            GetCategories();

            LoadAllProducts();
        }

        private void GetCategories()
        {
            ESNorthwind.GetCategoriesAsync();
        }

        private void ApplyCategoriesToList(object sender, GetCategoriesCompletedEventArgs e)
        {
            if (e.Error == null)
            {
                Categories[] cc = e.Result.Collection;
                for (int i = cc.Length-1; i >=0; i--)
                {
                    Categories category = cc[i];
                    ListBoxItem lbi = new ListBoxItem();
                    lbi.Tag = "CategoryID: " + category.CategoryID;
                    lbi.Content = "Products: " + category.CategoryName;
                    ViewSelector.Items.Insert(1, lbi);
                }
            }
        }

        private void LoadAllProducts()
        {
            WaitText.Visibility = Visibility.Visible;
            ESNorthwind.GetProductsAsync();
        }

        void LoadProductsByCategory(int categoryId)
        {
            WaitText.Visibility = Visibility.Visible;
            ESNorthwind.GetProductsByCategoryAsync(categoryId);
        }

        void Northwind_GetProductsByCategoryCompleted(object sender, GetProductsByCategoryCompletedEventArgs e)
        {
            WaitText.Visibility = Visibility.Collapsed;
            ProductsCollection pc = e.Result;
            ESDataGrid.ItemsSource = pc.Collection;
        }

        void NorthwindClient_GetProductsCompleted(object sender, Northwind.GetProductsCompletedEventArgs e)
        {
            WaitText.Visibility = Visibility.Collapsed;
            ProductsCollection pc = e.Result;
            ESDataGrid.ItemsSource = pc.Collection;
            ESDataGrid.Columns[ESDataGrid.Columns.Count - 1].Visibility = Visibility.Collapsed; // hide "esRowState"
        }

        private void ViewSelector_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            ListBoxItem lbi = ViewSelector.SelectedItem as ListBoxItem;
            if (lbi != null)
            {
                if (lbi.Content.ToString() == "Products: All")
                {
                    LoadAllProducts();
                    return;
                }
                string tag = lbi.Tag as string;
                if (tag != null && tag.StartsWith("CategoryID: "))
                {
                    tag = tag.Substring(12);  // length of "CategoryID:"
                    LoadProductsByCategory(int.Parse(tag));
                }
            }
        }

    }
}

On the server, we will perform a basic EntitySpaces query operation.

INorthwind service interface:
[ServiceContract]
public interface INorthwind
{
    [OperationContract]
    ProductsCollectionProxyStub GetProducts();

    [OperationContract]
    ProductsCollectionProxyStub GetProductsByCategory(int categoryId);
    [OperationContract]
    CategoriesCollectionProxyStub GetCategories();
}

Northwind service implementation:
public class Northwind : INorthwind
{
    public ProductsCollectionProxyStub GetProducts()
    {
        ProductsCollection pc = new ProductsCollection();
        pc.LoadAll();
        ProductsCollectionProxyStub pcs = new ProductsCollectionProxyStub(pc);
        return pcs;
    }

    public ProductsCollectionProxyStub GetProductsByCategory(int categoryId)
    {
        ProductsCollection pc = new ProductsCollection();
        pc.Query.Where(pc.Query.CategoryID.Equal(categoryId));
        pc.Load(pc.Query);

        ProductsCollectionProxyStub pcs = new ProductsCollectionProxyStub(pc);
        return pcs;
    }

    public CategoriesCollectionProxyStub GetCategories()
    {
        CategoriesCollection cc = new CategoriesCollection();
        cc.LoadAll();

        // strip out the OLE bitmaps
        for (int i = 0; i < cc.Count; i++)
        {
            cc[i].Picture = null;
        }

        CategoriesCollectionProxyStub ccs = new CategoriesCollectionProxyStub(cc);
        return ccs;
    }

}

Category-Filtered Products (Local Filtering Method)

Practically speaking, calling out to the server every time you want to filter the products is bad design unless the Products list contains a lot (hundreds of thousands) of records, in which case we should also drop the “Products: All” unfiltered option from the ListBox so that the user cannot make the mistake of loading all products which might take forever.

If the unfiltered source data is relatively small, as Northwind’s Products table actually is, then it would make more sense to load all products from the server and then filter against the category locally. This eliminates the HTTP-based callback to the server and makes the user’s experience much faster and more responsive.

LINQ-to-Objects is supported in Silverlight, and this includes LINQ-to-EntitySpaces-client-proxies. We can change the NorthwindClient_GetProductsCompleted and ViewSelector_SelectionChanged methods to use LINQ filtering rather than server-side filtering. In so doing, we can remove much of the code we just added and achieve the same results but executing much faster.

Products[] NorthwindProducts = null;
void NorthwindClient_GetProductsCompleted(object sender, Northwind.GetProductsCompletedEventArgs e)
{
    WaitText.Visibility = Visibility.Collapsed;
    NorthwindProducts = e.Result.Collection;
    ESDataGrid.ItemsSource = NorthwindProducts;
    ESDataGrid.Columns[ESDataGrid.Columns.Count - 1].Visibility = Visibility.Collapsed; // hide "esRowState"
}

private void ViewSelector_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    ListBoxItem lbi = ViewSelector.SelectedItem as ListBoxItem;
    if (lbi != null)
    {
        if (lbi.Content.ToString() == "Products: All")
        {
            ESDataGrid.ItemsSource = NorthwindProducts;
            ESDataGrid.Columns[ESDataGrid.Columns.Count - 1].Visibility = Visibility.Collapsed; // hide "esRowState"
            return;
        }
        string tag = lbi.Tag as string;
        if (tag != null && tag.StartsWith("CategoryID: "))
        {
            int catID = int.Parse(tag.Substring(12));
            ESDataGrid.ItemsSource =
                from p in NorthwindProducts
                where p.CategoryID == catID
                select p;
            ESDataGrid.Columns[ESDataGrid.Columns.Count - 1].Visibility = Visibility.Collapsed; // hide "esRowState"
        }
    }
}

We can now eliminate one of the INorthwind interfaces we just added, ..

  • GetProductsByCategory(int categoryId)

.. and we can eliminate some of the just-added client/server handling code in Silverlight:

  • LoadProductsByCategory(int categoryId)
  • Northwind_GetProductsByCategoryCompleted(...)

Changing Data

Let’s assume that the user is an administrator who is maintaining this list of products and needs to be able to make changes to product data on the fly. The bad news is that in the interest of focusing on EntitySpaces and less on Silverlight UI, I intended to refrain from creating a pop-up editor for editing a record. The good news is that the Silverlight DataGrid supports two-way databinding and inline edits, and in such case one would need no layout changes at all. I only need to add an event handler for DataGrid edits and pass the revised record up to the server via WCF.

We’ll bind to the CommittingCellEdit and CommittingRowEdit events. In CommittingCellEdit we’ll update the property that was edited, and in CommittingRowEdit we’ll pass the Product out to the server.

Page.xaml (modify):

<Controls:DataGrid AutoGenerateColumns="True" x:Name="ESDataGrid" Grid.Column="1"
CommittingCellEdit="ESDataGrid_CommittingCellEdit"
CommittingRowEdit="ESDataGrid_CommittingRowEdit"
/>

Page.xaml.cs (add):

private void ESDataGrid_CommittingCellEdit(object sender, DataGridCellCancelEventArgs e)
{
    Products p = (Products)e.Row.DataContext;
    string f = e.Column.Header.ToString();
    string v = ((TextBox)e.Element).Text;
    System.Reflection.PropertyInfo pi = p.GetType().GetProperty(f);
    if (pi.PropertyType == typeof(string))
    {
        pi.SetValue(p, v, null);
    }
    else if (pi.PropertyType == typeof(int) ||  pi.PropertyType == typeof (int?))
    {
        pi.SetValue(p, int.Parse(v), null);
    }
    else e.Cancel = true;
    if (!e.Cancel)
    {
        p.esRowState = "Modified";
    }
}

private void ESDataGrid_CommittingRowEdit(object sender, DataGridRowCancelEventArgs e)
{
    Products p = (Products)e.Row.DataContext;
    if (p.esRowState == "Modified")
    {
        ESNorthwind.UpdateProductAsync(p);
    }
}

This won’t compile until we revise our service in the Web project and then update the reference to it in the Silverlight project.

INorthwind.cs (add):

[OperationContract]
void UpdateProduct(ProductsProxyStub product);

AppCode/Northwind.cs (or Northwind.svc.cs, add):

public void UpdateProduct(ProductsProxyStub product)
{
    Products prod = (Products)product.GetEntity();
    prod.Save(); // wow, that was easy..
}

Silverlight project:

linqII_three

Now the Silverlight code should compile, and if you set breakpoints both on the server’s UpdateProduct() method as well as on the new event handler in Silverlight, you should be able to watch the data model as it gets updated and sent back to the server.

Recommitting back to the server was so easy (although the reflection bits in the CommitingCellEdit event handler needs some clean-up) it’s actually a little dangerous. We can’t go live with this change on a public demo without having a nightly automated process to restore the Northwind data or else it will become unusable.

But this tutorial should have still sufficed in getting you on your feet with fetching, filtering, and updating data between Silverlight and EntitySpaces. There is a lot of design-related knowledge to be had in Silverlight, and real-world business objects to be realized in EntitySpaces, but this was a basic data discussion, so the rest is up to you.

 


EntitySpaces

From mobile devices to large scale enterprise solutions in need of serious transaction support, EntitySpaces can meet your needs. Whether you’re writing an ASP.NET application with medium trust requirements, a Mono application, or a Windows.Forms application, the EntitySpaces architecture is there for you. EntitySpaces is provider independent, which means that you can run the same binary code against any of the supported databases. EntitySpaces is available in both C# and VB.NET. EntitySpaces uses no reflection, no XML files, and sports a tiny foot print of less than 200k. Pound for pound, EntitySpaces is one tough, dependable .NET architecture.

The EntitySpaces Team
--

EntitySpaces LLC
Persistence Layer and Business Objects for Microsoft .NET
http://www.entityspaces.net

posted on Friday, May 23, 2008 10:15:53 PM (Eastern Standard Time, UTC-05:00)  #   
 Sunday, May 04, 2008

EntitySpaces and Silverlight Demo

Part 1: Propagating EntitySpaces Data

kick it on DotNetKicks.com


Jon_Davis 
Written by Jon Davis
http://www.jondavis.net/


 

Introduction

I was asked to see how easy it would be to get EntitySpaces objects to work inside Silverlight, and to demonstrate my findings. This was a curiously interesting notion, since many have pondered whether Silverlight is a valuable runtime environment for LOB (line-of-business) applications. There are a number of reasons why Silverlight, at least in its Version2 flavor, would be very ideal for building client/server applications:

· Silverlight 2 comes with pre-built user controls ideal for business environments, such as the basic Textbox and more advanced data grid with editable cells

· Silverlight 2's user controls support two-way data binding, so that synchronization can be performed easily in either direction

· Silverlight 2's runtime is fast and fluid, using retained graphics vector-based rendering rather than redraw-at-invalidate "lazy" rendering. This results in a very responsive, clean, and professional user experience.

· XAML takes lessons learned with ASP.NET with the separation of design from programming. Properly implemented, a Silverlight application can be completely re-skinned, with its layout completely reorganized, without touching any of the user interface programming code.

· Silverlight 2 ships with built-in support for the language of your choice of: C#, VB.NET, IronRuby, IronPython, or JScript.NET. The .NET Framework itself inside of Silverlight is stripped down, but the core CLR is complete, and the target deployment environment does NOT need the .NET Framework installed with it.

· Silverlight 2 runs not only on the Windows platform and on Internet Explorer, but it also runs happily on the Macintosh and in other browsers such as Firefox and Safari. This means that Silverlight is not just Microsoft's answer to Flash for its vector graphics, it's also Microsoft's decade-long-awaited answer to Java applets, "Write once, run anywhere." An investment in Silverlight on Windows is automatically a free, bonus investment in the Mac and Firefox runtime environments.

· Silverlight 2 is also capable of communicating at runtime with web services over HTTP, making synchronization a natural programming experience without "hacking".

So here I began. My first thought was to take the EntitySpaces generated objects and put them in a shared library, then reference them in my Silverlight application. This turned out not to work as I hoped, for a few important reasons worth mentioning:

· Silverlight 2 can only reference assemblies (DLLs) that had been compiled against Silverlight's custom CLR. It cannot reference assemblies that were compiled against the normal .NET Framework (or against Mono).This is actually an opportunity for EntitySpaces, at least in its current form, because ES is built around code generation, so theoretically you could get the generated code to target Silverlight. Or so I thought, until I found...

· Silverlight 2 only "speaks" HTTP. It doesn't "speak" T-SQL, so you cannot use an ADO.NET connection, for example. There is no System.Data namespace, no ADO.NET support. While this seemed bizarre and even annoying at first, eventually I began to understand, besides for obvious security reasons, why System.Data was not present in Silverlight 2 in the current beta form: CRUD is CRUD, it doesn't matter if it's happening over ADO.NET on a SQL connection or over HTTP, as long as the job gets done.

Now I was beginning to procrastinate, because I made too many dumb assumptions. "So I guess I have to use System.Net.WebClient", I thought, "to make a manual connection to a web server, invoke a REST query I have to manually implement on the server, and then manually bring it back, parse it out, and in the end enjoy no benefit to using EntitySpaces at all?" When I finally sat down and researched what I needed to do, my jaw dropped. It’s nothing like that. In fact, it's almost silly how simple this is. The answer to the problem of the need for easy business object data synchronization lies in a wonderfully consistent new approach provisioned by Microsoft called WCF, or Windows Communications Foundation, now implemented at a basic level in Silverlight.

WCF support on Silverlight is limited to basicHttpBinding (SOAP 1.1). But that binding should suffice for most business scenarios.

The power of WCF is actually somewhat less about the underlying technology than it is about the code generation tools built into Visual Studio for building out the necessary client proxy stubs from defined interfaces at design-time. EntitySpaces' built-in WCF took me there 75% of the way, but the Silverlight SDK closed the gap very quickly by allowing me to reference my WCF service in just a couple clicks.

There was no coding of System.Net.WebClient. No manual conversion of XML to C# objects. Just beautiful EntitySpaces proxy stubs that enabled me to gather rich business object data from a server and perform quick and easy data binding against Silverlight with minimal effort.

So in this first part, I'll describe the first "baby step" of getting Silverlight to interoperate with a WCF service in order to pass valuable EntitySpaces objects into Silverlight with very little effort. Following that, in the next part I'll describe a simple scenario of fetching filtered data based on a WCF callback parameter that then takes advantage of EntitySpaces query filtering on the server, again with only a few more lines of code. Finally, if I have time, I'll continue to research and then demonstrate how to take advantage of the new client-side query support that is already available in ES 2008's latest build, adding even more flexibility to the programming user experience.

 

Run the Demo Here  ==> http://developer.entityspaces.net/ES2008/Demos/Silverlight/PartOne

Download the Source Here ==> http://www.developer.entityspaces.net/downloads/EntitySpacesSilverlightDemo.zip 


Setup

These are the prerequisites you need to walk through this demo:

· Visual Studio 2008

· EntitySpaces 2007

· Microsoft Silverlight Tools Beta 1 for Visual Studio 2008

· Northwind database
 

image

Figure 1 - Download the Silverlight tools for Visual Studio

 

Setting Up the Project and Generating Northwind Business Objects

If you’re not already familiar with EntitySpaces, we need to walk through some initial code generation so that we have some business objects to begin with.

Since nearly everything we’re doing is going to be done inside of Visual Studio, let’s start off by creating a new solution.

clip_image002[19]

Figure 2 - Start up Visual Studio and start by creating a new Project 
 

clip_image002[17]

Figure 3 - Create a Silverlight Application using .NET Framework 3.5 
 

clip_image002[1]

Figure 4 - When you create a Silverlight Application, Visual Studio prompts you to ask whether you want to create a new web to the solution or if you just want a local HTML test page. Choose to add a new Web. 
 

In my opinion, the data objects that get passed around in an application should exist in their own class library and then referenced into the application that uses them.

clip_image002[21] 

Figure 5 - Add a new project to the solution ... 

 

clip_image002[25]

Figure 6 - .. and choose Class Library as the project type. You can delete the resulting Class1 file. 

I’m going to use MyGeneration 1.3 for this demonstration. After preparing Northwind for SQL Server, I can then fire up MyGeneration.

clip_image014

Figure 7 - Open the template browser 

clip_image002[61]

Figure 8 - Expand EntitySpaces, then C#, then right-click Generated Classes Master (C#), and choose Execute. 

clip_image018

Figure 9 - I have a Visual Studio class library project set up, waiting for new code to be added to the physical directory so that I can add the files to the project after they are generated. 

clip_image020

Figure 10 - I'd like to give the Namespace a similar name as the database: NorthwindData  

clip_image022

Figure 11 - You can pick and choose your tables -- we'll use Products and Categories -- but might as well select them all. 

clip_image024

Figure 12 - On the third tab, we want to enable the WCF proxies, as highlighted in the image.  

clip_image026

Figure 13 - Click the OK button at the bottom to generate the objects  

clip_image002[27]

Figure 14 - After a moment, you may receive a confirmation message. If not, no worries, as long as your error log window isn’t filled up.  

Now I can close MyGeneration and go back to Visual Studio.

clip_image002[29]

My NorthwindData project doesn’t have anything in it yet, let’s add the newly generated files.

clip_image002[11]

Figure 15 - Click the "Show All Files" toolbar button in the Solution Explorer window.  

clip_image002[31]

Figure 16 - Now we can see our generated files. Right-click on the folder and choose "Include In Project".  

Before we can call this class library “ready”, we need to remember to reference the EntitySpaces dependency assemblies.

clip_image002[33]

clip_image002[35]

Figure 17 - You need EntitySpaces.Core and EntitySpaces.Interfaces .. 

clip_image002[37]

Figure 18 - .. as well as System.Runtime.Serialization and System.ServiceModel for WCF proxy support. 

Finally, we should also add the same references to the Silverlight_Web project, as well as EntitySpaces.Loader, EntitySpaces.LoaderMT, EntitySpaces.SqlClientProvider, EntitySpaces.Web, and EntitySpaces.Web.Design.

You will also need to add updates to your web.config for EntitySpaces to be able to read from (and later write to) the Northwind database. Here’s what I have ..

<configuration>
   <configSections>
      <sectionGroup name="EntitySpaces" type="EntitySpaces.Interfaces.esConfigSettings, EntitySpaces.Core">
      <section name="connectionInfo" type="EntitySpaces.Interfaces.esConfigSettings, EntitySpaces.Interfaces"
          allowLocation="true" allowDefinition="Everywhere" restartOnExternalChanges="true"/>
</sectionGroup>
...

...
</system.webServer>

<EntitySpaces>
    <connectionInfo default="SQL">
      <connections>
         <add name="SQL" providerMetadataKey="esDefault" sqlAccessType="DynamicSQL" provider="EntitySpaces.SqlClientProvider"
              providerClass="DataProvider"
              connectionString="Data Source=.\SQLEXPRESS;AttachDbFilename=|DataDirectory|\NORTHWND.MDF;Integrated Security=True;User Instance=True" databaseVersion="2005"/>
      </connections>
   </connectionInfo>
</EntitySpaces>

<runtime>
...


Discovering the Silverlight Tools

Before you got this far, you should have discovered that when you added the Silverlight Application, the Page.xaml file loaded right up to be edited. It might have looked like this:

clip_image042

Figure 19 - The XAML Editor view for a Silverlight Application 

This is a design canvas for a Silverlight application layout. Silverlight works like ASP.NET in that the design and the programming code are isolated from each other. There is a “code-behind” file, or a similar principle, just as with an .aspx file.

In the design (XAML) editor, there are two views on a split pane.

One view is the XAML text editor. Think of it as an HTML editor in a split pane view, such as in Microsoft Expression Web or in Dreamweaver. The only big difference here is that XAML isn’t HTML, obviously, and in fact XAML is much stricter and less forgiving of errors than HTML ever was. Even so, its power becomes evident when elements can be added to a view with minimal markup.

clip_image002[39]

Figure 20 - The XAML text editor pane.  

The other view is the Design view. Currently (at the time of this demo, the Silverlight Tools was only in Beta 1), this only works as a preview in Visual Studio. You can drag items from the toolbar to your layout, but only into the XAML text editor, not to the Design view. This isn’t the case if you have access to Blend 2.5, which at the time of this demo is currently in public Alpha (Technology Preview) and was not used for this demo at all. The design view does have some advantages, though. It provides a quick visual preview of the XAML text, and if you need to nitpick you can zoom in or out.

clip_image002[41]

Figure 21 - The Design pane.  

So let’s start adding some XAML and some logic to get, say, a data grid populated with the entire contents of the Northwind “Products” table, as provided by the EntitySpaces business objects we generated earlier.

Personally I don’t care for fixed dimensions before we even begin let’s just delete the Width and Height attributes on the <UserControl> XML node. This will make Silverlight fill the screen or else collapse to the largest object.

Next I want to add a data grid.

clip_image002[47]

Figure 22 - Drag a DataGrid control to the <Grid> content. 

I’ll add the attribute x:Name=”ESGrid”, which is the ID of the control on the page. The attribute AutogenerateColumns=”True” does what it suggests; when data is bound to the grid, the columns are automatically generated. (Without this setting, the grid would come up blank because I am not providing layout information for the columns.)

<Grid x:Name="LayoutRoot" Background="White">
    <my:DataGrid x:Name="ESGrid" AutoGenerateColumns="True"></my:DataGrid>
</Grid>

The next thing we should do is insert a “Please Wait” text block so that as we download the Products list. This is just for effect so that the user isn’t staring at a blank DataGrid or a blank screen before the data shows up. To make this effective, we should also hide the DataGrid until the data is ready.

<Grid x:Name="LayoutRoot" Background="White">
    <TextBlock Text="Please Wait .." x:Name="WaitText" VerticalAlignment="Center" HorizontalAlignment="Center" />
    <my:DataGrid x:Name="ESGrid" AutoGenerateColumns="True" Visibility="Collapsed" />
</Grid>

Now all our Silverlight app wants is some data. But before we can populate the DataGrid, we have to expose the data. So now we turn to our Silverlight_Web project.

Exposing the Data via A WCF Service

Open up the Property Pages for the Web project.

clip_image002[49]

The first thing that should come up is a list of References. Add a reference to the Northwind project
 

image

clip_image054

With Northwind added, we can now add a WCF service. Right-click on the Silverlight_Web project again and choose Add New Item…
 

clip_image056

.. and then choose WCF Service. We’ll name it “Northwind.svc”.
 

clip_image058

This generates four things:

  • An interface file defining the interface INorthwind
  • A Service (Northwind.svc) file that only acts as a stub for the code-behind C# file
  • A service implementation file (Northwind.svc.cs or App_Code/Northwind.cs) where the service code is actually going to be written, and finally
  • A set of entries in web.config that define

    • Your site’s serviceModel configuration settings such as the EndPoint configuration
    • Bindings to the Northwind service

In the Northwind service implementation, you’ll see a bogus DoWork method stub defined and exposed, like so:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;

// NOTE: If you change the class name "Northwind" here, you must also update the reference to
// "Northwind" in Web.config.

public class Northwind : INorthwind
{
    public void DoWork()
    {

    }
}

You can toss this out but before you do you need to go and modify INorthwind.

Notice that INorthwind attributes itself with the ServiceContractAttribute. This identifies the interface as a WCF service interface definition. Exposed methods are attributed with OperationContractAttribute.

[ServiceContract]
public interface INorthwind
{
    [OperationContract]
    void DoWork();
}

Before we can see the Northwind project’s stuff in Intellisense, we need to build both it and the Silverlight_Web project. Right-click on the Silverlight_Web project, choose Build Web Site, and the Northwind project will build first because it is a dependency. (This is also a good time to check for errors.)

Now let’s add some code.

In INorthwind, replace “DoWork()” with “GetProducts()”, and change “void” to “NorthwindData.ProductsCollectionProxyStub”. Then go back to the service implementation file and replace “DoWork()” with “GetProducts()” again and “void” to “NorthwindData.ProductsCollectionProxyStub”.

Next, we can give the method stub some functionality.

public NorthwindData.ProductsCollectionProxyStub GetProducts()
{
    NorthwindData.ProductsCollection products = new NorthwindData.ProductsCollection();
    products.LoadAll();

    NorthwindData.ProductsCollectionProxyStub proxy = new NorthwindData.ProductsCollectionProxyStub(products);
    return proxy;
}

This will fetch all products, wrap them in a WCF-ready proxy, and spit back the proxy.

Now let’s make sure that our WCF service is functioning. Hit the Debug button in Visual Studio (while the Silverlight_Web project is selected) and when the web page comes up, navigate to Northwind.svc. You may get a prompt asking if you want to enable debugging, just say yes.

clip_image060

clip_image062

clip_image002[51]

If you can see the “Northwind Service” page, indicating “You have created a service,” you’re almost ready to start binding your service to Silverlight.

Stop the debugger before continuing.

 

Initializing EntitySpaces

Before this service will successfully work with EntitySpaces at runtime, the EntitySpaces runtime needs to be initialized to point to a database.

Right-click on the Silverlight_Web project node in Solution Explorer and choose “Add New Item...”, then select “Global Application Class” (name it the default, Global.asax). When it opens up, add the following code in Application_Start():

protected void Application_Start(object sender, EventArgs e)
{
    EntitySpaces.Interfaces.esProviderFactory.Factory = new EntitySpaces.LoaderMT.esDataProviderFactory();
}


Binding the WCF Service to Silverlight

Silverlight “speaks” WCF, but only using basicHttpBinding (SOAP 1.1). We need to update the endpoint configuration on the service so that it uses basicHttpBinding rather than the default wsHttpBinding.

Open web.config in Silverlight_Web and change wsHttpBinding to basicHttpBinding.

<system.serviceModel>
<behaviors>
    ...
</behaviors>
<services>
    <service behaviorConfiguration="NorthwindBehavior" name="Northwind">
    <endpoint address="" binding="basicHttpBinding" contract="INorthwind">
    ...

We also need to create a temporary lock on the port that our development web server (Cassini) is using. When Web projects are created within Visual Studio, a small web server loads up in the background, with a visible icon in the Windows Notification Area (or “system tray”), and the port that it uses is random. If we lock it, though, we can force Silverlight’s WCF bindings to always point to the correct web address when it calls back to the web server. This is only for debugging purposes; when deploying to production, a URL will be used literally, on port 80, as with http://www.mydomain.com/myService

To lock the web server port so that we can get a fixed reference on Silverlight, click on the Silverlight_Web project node in the Solution Explorer tree and disable the “Use dynamic ports” option.

clip_image066

Now it’s time to bind your WCF service to Silverlight. Right-click on the Silverlight project node in the Solution Explorer and choose “Add Service Reference ...”

clip_image002[53]

Then select “Discover >” and choose “Services in Solution”. The Silverlight_Web project is displayed. Expand it until you see the Northwind service, then expand that until you see the INorthwind service contract. Select the INorthwind service contract, and set the Namespace to “NorthwindClient”.
 

clip_image002[55]

Once you click on “OK”, the required assemblies for WCF support, all pre-compiled against the Silverlight runtime, are brought into Silverlight automatically.

clip_image002[57]

Great, I have my bindings. Now it’s time to populate my grid.

If you expand the Page.xaml node in the Solution Explorer, you should see the Page’s “code-behind”-ish file. Open that up and see what’s inside.

// using stuff ;
namespace EntitySpacesSilverlightDemo_Silverlight
{
    public partial class Page : UserControl
    {
        public Page()
        {
            InitializeComponent();
        }
    }
}

So we already have a constructor here where we can fire off an invocation of the WCF service. Note: All System.Net.WebClient and WCF client calls are asynchronous, so you must plan for this ahead of time whenever programming callbacks with Silverlight.

After adding “using NorthwindClient;” to the top of the file, here’s my code for within the “class Page” block:

NorthwindClient.NorthwindClient ESNorthwind;

public Page()
{
    InitializeComponent();
    ESNorthwind = new NorthwindClient.NorthwindClient();
    ESNorthwind.GetProductsCompleted += new EventHandler<NorthwindClient.GetProductsCompletedEventArgs>(NorthwindClient_GetProductsCompleted);
    LoadAllProducts();
}

private void LoadAllProducts()
{
    WaitText.Visibility = Visibility.Visible;
    ESNorthwind.GetProductsAsync();
}

void NorthwindClient_GetProductsCompleted(object sender, NorthwindClient.GetProductsCompletedEventArgs e)
{
    WaitText.Visibility = Visibility.Collapsed;
    NorthwindClient.ProductsCollection pc = e.Result;
    ESGrid.ItemsSource = pc.Collection;
    ESGrid.Visibility = Visibility.Visible;
}

Hit F5 and run, and you should see something like this:

clip_image002[59]

This is what we were designing for. We have a data-bound DataGrid, where the data derives from the same EntitySpaces business objects I perhaps already invested in elsewhere on my site or in my application, and despite a learning curve for basic configuration for WCF interop, there was actually very little code that was written by hand.


Deployment to IIS

We’re not quite done yet. Now we have to look at deployment to a production server.

Silverlight’s binding to the services is hard-coded to the URL http://localhost:11434/Northwind.svc. Silverlight is then compiled and bundled directly into the web project that hosts the WCF service. We now have a chicken-or-egg situation; both projects depend on the other. Further, EntitySpaces is hosting this demo on a shared web host that uses IIS 7 which enables WCF services natively, but WCF on IIS 7 doesn’t support HTTP endpoints through web.config without a custom service factory.

Here’s a run-down on how we got this running at: http://developer.entityspaces.net/ES2008/Demos/Silverlight/PartOne/

  1. Upload the web project files to the web server via FTP and make sure that the directory paths correctly reflect the desired URLs.
  2. In Visual Studio, with the Solution still open, choose File -> Add -> Existing Web Site..., then select FTP on the left and fill out the necessary FTP information (hostname, username, password, directory).
    Be sure to enter the exact FTP directory path to the root of the web project.
  3. We now have two copies of the same web project loaded in our solution, the local copy and the FTP-based copy. Now we need to make some configuration changes in the FTP-based copy of the project in our solution.
    • Re-link the Silverlight project. Right-click on the Project node in the Solution Explorer, choose Properties Pages, and select Silverlight Links. (If anything there, remove it now so it is empty.) Click “Add...”, ensure that your Silverlight project is available in the drop-down list for “Project to link”, and keep the Destination folder set to “/”. Click Add and then click OK. You may get some warnings that the file(s) already exist, go ahead and choose to overwrite.
    • Restore the database connection. In web.config, change the connection string at configuration/EntitySpaces/connectionInfo/connections to point to a working instance of Northwind on SQL Server on the web host.
    • Add a Northwind service factory to the project. Open App_Code/Northwind.cs and make the following changes:

      // append to the "using" declarations at the top
      using System.ServiceModel.Activation;
      using System.Configuration;

      // append inside of namespace but before or after the Northwind class block
      public class NorthwindServiceHostFactory : ServiceHostFactory
      {
          protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
          {
              // Specify the exact URL of your web service
              Uri webServiceAddress = new Uri(ConfigurationManager.AppSettings["ServiceUri_Northwind"]);
              NorthwindServiceHost webServiceHost = new NorthwindServiceHost(serviceType, webServiceAddress);
              return webServiceHost;
          }
      }

      public class NorthwindServiceHost : ServiceHost
      {
          public NorthwindServiceHost(Type serviceType, params Uri[] baseAddresses)
              : base(serviceType, baseAddresses)
          { }

          protected override void ApplyConfiguration()
          {
              base.ApplyConfiguration();
          }
      }

    • Find the <appSettings> node of web.config (which might be collapsed as <appSettings/>, just change it to <appSettings><appSettings>) and add the following setting inside of it:

      <add key="ServiceUri_Northwind" value="http://developer.entityspaces.net/ES2008/Demos/Silverlight/PartOne/EntitySpacesSilverlightDemo_Silverlight_Web/Northwind.svc"/>
    • In web.config, under configuration/system.serviceModel/behaviors/serviceBehaviors, add:

      <behavior name="NorthwindFactoryBehavior" >
          <serviceMetadata httpGetEnabled="true" />
      </behavior>

    • Finally, under configuration/system.serviceModel/services, change

        <service behaviorConfiguration="NorthwindBehavior"  name="Northwind">

      .. to ..

        <service behaviorConfiguration="NorthwindFactoryBehavior" name="Northwind">

      .. and although I don’t think it’s used anyway with our factory, you might want to go ahead and change the <dns>localhost</dns> entry down in there to <dns>yourhost.com</dns>.

    • Build the solution. If build succeeds, ...
    • In the Silverlight project, expand the Service References node in the Solution Explorer and remove the NorthwindClient node by right-clicking it and choosing Delete. Then right-click on Service References and choose Add Service Reference. Click on Discover and choose Services in Solution. Select the FTP-based project. You may be asked to refine the URL, you will need to modify the default value to correctly reflect the accurate URL path to the Northwind.svc file on the live server.

    If you receive an error, open up a web browser and navigate directly to the URL of the Northwind.svc file. If it fails to show up, try to use the error message (Google is your friend) to drill down the problem. It may relate to something overlooked in the steps I described.

    1. Now right-click on the Silverlight project node in the Solution Explorer and build the project again.
    2. Finally, right-click on the web project node in the Solution Explorer and build the project again. This will pull the revised compiled Silverlight project into the hosted site.

    If you made it this far, all the hard stuff is over!! From here on out, it’s easy, blissful C# coding with EntitySpaces and a few XAML tweaks, no more configuration hassles.

    In Part 2 of this tutorial / demo, I’ll make the app a little bit more interactive, adding filters and the ability to make a few changes to the data. (It will be comparatively short compared to this part.) But now that you know how to link up ES objects to Silverlight over WCF, have fun and be safe!!



    EntitySpaces

    From mobile devices to large scale enterprise solutions in need of serious transaction support, EntitySpaces can meet your needs. Whether you’re writing an ASP.NET application with medium trust requirements, a Mono application, or a Windows.Forms application, the EntitySpaces architecture is there for you. EntitySpaces is provider independent, which means that you can run the same binary code against any of the supported databases. EntitySpaces is available in both C# and VB.NET. EntitySpaces uses no reflection, no XML files, and sports a tiny foot print of less than 200k. Pound for pound, EntitySpaces is one tough, dependable .NET architecture.

    The EntitySpaces Team
    --

    EntitySpaces LLC
    Persistence Layer and Business Objects for Microsoft .NET
    http://www.entityspaces.net

    posted on Sunday, May 04, 2008 7:59:06 PM (Eastern Standard Time, UTC-05:00)  #   
     Saturday, November 10, 2007

    Our next beta will sport "pure" WCF client side proxies. These proxies allow the client side of a WCF conversation to seemlessly  communicate with your heavier server proxies without the need for EntitySpaces on the client side. Our new client side proxies are very lightweight, strongly typed, and match your server side. The first scenario below showing EntitySpaces on both sides of the conversation is of course still possible, however, we have added the "EntitySpaces on the Server Only Model" which we will discuss in further detail now.

    For those interested in our new client side proxies there is a new template that you will need to run to create them. These generated proxies would be placed in a tiny class library and used by the client to communicate with your server.

    Your client side proxies will look like this, first, the collection class.

    Notice that the collection is a partial class. This will allow you to add functionality if needed. Also notice that there is no link to EntitySpaces whatsoever. You serialize from your Server Side Proxy and deserialize into your Client Side Proxies.  Now let's take a look at the entity class (which is also contained in the collection).

    Again the single entity client side proxy has no link to EntitySpaces yet you can deserialize your heavier server side proxies into these tiny lightweight proxies on the client side. Notice the partial class definition as well. The row state is maintained for you as well. All you need to do is modify your data on the client side and ship it back to the server.

    Here is a simulated converation between the server and client side proxies.

    In the above sample we are serializing from the server proxy into a string which simulates your packet being sent over the wire to the client. Next we deserialize into our lightweight client proxy, change the employee's FirstName, and then ship it back over the wire to the server to be saved. The nice thing is that when you deserialize on your server you merely grab the true Employee business object from the proxy and save it, it's so easy.

    EntitySpaces

    From mobile devices to large scale enterprise solutions in need of serious transaction support, EntitySpaces can meet your needs. Whether you’re writing an ASP.NET application with medium trust requirements, or a Windows.Forms application, the EntitySpaces architecture is there for you. EntitySpaces is provider independent, which means that you can run the same binary code against any of the supported databases. EntitySpaces is available in both C# and VB.NET. EntitySpaces uses no reflection, no XML files, and sports a tiny foot print of less than 200k. Pound for pound, EntitySpaces is one tough, dependable .NET architecture.

    The EntitySpaces Team
    --

    EntitySpaces LLC
    Persistence Layer and Business Objects for Microsoft .NET
    http://www.entityspaces.net

    posted on Saturday, November 10, 2007 10:56:44 AM (Eastern Standard Time, UTC-05:00)  #   
     Saturday, July 28, 2007
    We have written a webservices example using three different forms of the same service. These webservices allow you to retrieve and update data and are built upon the Microsoft SQL Northwind Employees table using EntitySpaces 2007.0.730.0 maintenance release which will be published this weekend (July 28th, 2007).  You can download both the WSConsumer application and the webservices (including source code) at the end of this article. The webservice is included so you can the see the source and run it locally if desired. The WSConsumer is setup to hit the services listed below on our site.

        Xml - http://www.entityspaces.net/webservices/csharp/ES_Xml.asmx

        XmlProxy - http://www.entityspaces.net/webservices/csharp/ES_XmlProxy.asmx

        Binary - http://www.entityspaces.net/webservices/csharp/ES_Binary.asmx

     

    The Xml Webservice

    The XML webservice is a basic SOAP example. It simply returns the Employees entity or the EmployeesCollection and everything is serialized accordingly. This type of webservice, the more traditional, doesn't require the webservice consumer to have any knowledge or link to any of your code.

    [WebMethod]
    public Employees GetEmployee(int i)
    {
        Employees emp = new Employees();
        if (emp.LoadByPrimaryKey(i))
            return emp;
        else
            return null;
    }

    [WebMethod]
    public EmployeesCollection GetAllEmployees()
    {
        EmployeesCollection emps = new EmployeesCollection();
        if (emps.LoadAll())
            return emps;
        else
            return null;
    }

    Most people are familiar with traditional webservices. If you are interested in saving data through your webservice all the necessary code is in the demo app. However, security issues when allowing users to update data are not covered, those are left up to the developer to consider.

    The XmlProxy Webservice (and WCF)

    This is by far the most interesting and powerful example. The EntitySpaces proxy classes can be used in webservice scenarios in which you have control over both the webservice and the consumer as this example requires the proxy to be on both sides of the equation. Additionally, the EntitySpaces proxy classes are perfect for use as your WCF wire format. When we generated the proxy classes for the demo we did not check the "Support WCF" checkbox. We only generated the proxy portion which can be used on it's own without WCF. Some of the features of the proxy classes are follows:

    • Perfect for WCF communications.
    • Very easy to use wrappers
    • Very lightweight format which can serialize only the dirty rows and dirty columns.
    • Manages Added, Modified, and Deleted state for you.
    • Transparent Save logic.

    If you're using EntitySpaces in WCF scenarios you can glean a lot from this section, it's shows exactly how your WCF packets will look. In this sample we need to manually serialize them into string form but in WCF scenarios you would just return the actual proxy's in your methods and the serialization is done for you. Take a look at our GetEmployee() method and notice the serialization so that we can return a raw string. Notice how we use the Proxy to wrap the real Employees object.

    [WebMethod]
    public string GetEmployee(int i)
    {
        Employees emp = new Employees();
        if (emp.LoadByPrimaryKey(i))
        {
            // The Generated Proxy class
            EmployeesProxyStub proxy = new EmployeesProxyStub(emp);

            // Manually Serialize into string form (we want to deserialize it on the other side)
            XmlSerializer sf = new XmlSerializer(typeof(EmployeesProxyStub));
            StringWriter sw = new StringWriter();
            sf.Serialize(sw, proxy);

            return sw.ToString();
        }
        else
            return null;
    }

     

    The output of our call to the GetEmployee() method above can be seen below.

    <?xml version="1.0" encoding="utf-16"?>
        <EmployeesProxyStub xmlns:xsi="
    " xmlns:xsd="">
        <EmployeeID>1</EmployeeID>
        <LastName>Davolio</LastName>
        <FirstName>Jimmy</FirstName>
        <Title>Sales Representative</Title>
        <TitleOfCourtesy>Ms.</TitleOfCourtesy>
        <BirthDate>1948-12-08T00:00:00</BirthDate>
        <HireDate>1992-05-01T00:00:00</HireDate>
        <Address>507 - 20th Ave. E.Apt. 2A</Address>
        <City>Seattlesss</City>
        <Region>WA</Region>
        <PostalCode>78890</PostalCode>
        <Country>USA</Country>
        <HomePhone>(206) 555-9857</HomePhone>
        <Extension>989</Extension>
        <Notes>Some notes by Rick</Notes>
        <ReportsTo>161</ReportsTo>
       
    <esRowState>Unchanged</esRowState>
    </EmployeesProxyStub>

     

    Notice the esRowState is "Unchanged". Now let's deserialize this in our client (in the demo the client is called WSConsumer). The code necessary to do this (taken from the WSConsumer application) is shown below.

    // Call the XmlProxy Service
    string data = wsProxyService.GetEmployee(1);

    XmlSerializer xs = new XmlSerializer(typeof(BusinessObjects.EmployeesProxyStub));
    StringReader sr = new StringReader(data);

    // Deserialize into our Proxy
    BusinessObjects.EmployeesProxyStub proxy =
    xs.Deserialize(sr) as BusinessObjects.EmployeesProxyStub;

    // Ask the proxy for the true Employees object
    Employees emp = proxy.Entity;

    Now let's modify the "emp" object we just deserialized and save it back through our service

    emp.FirstName = "Joe";
    emp.LastName = " EntitySpaces";

    // True here means dirty columns only (primary keys are of course always sent)
    BusinessObjects.EmployeesProxyStub proxy =
        new BusinessObjects.EmployeesProxyStub(this.emp, true);

    XmlSerializer sf = new XmlSerializer(typeof(BusinessObjects.EmployeesProxyStub));
    StringWriter sw = new StringWriter();
    sf.Serialize(sw, proxy);

    string packet = sw.ToString();

    wsProxyService.SaveEmployee(packet);

    Let's take a look at the packet sent back from the WSConsumer application to the webservice that was generated from the above code.

    <?xml version="1.0" encoding="utf-16"?>
        <EmployeesProxyStub xmlns:xsi="
    " xmlns:xsd="">
        <EmployeeID>1</EmployeeID>
        <LastName>EntitySpaces</LastName>
        <FirstName>Joe</FirstName> 
        <esRowState>Modified</esRowState>
    </EmployeesProxyStub>

    Notice that only the dirty columns are sent back and the esRowState is set to "Modified". We're almost at the end of this sequence, let's see how our XmlProxy webservice implements the SaveEmployee method.

    [WebMethod]
    public void SaveEmployee(string data)
    {

        // Deserialize into our Proxy
        XmlSerializer xs = new XmlSerializer(typeof(EmployeesProxyStub));
        StringReader sr = new StringReader(data);

        EmployeesProxyStub proxy = xs.Deserialize(sr) as EmployeesProxyStub;

        // Ask for the real Employee entity
        Employees emp = proxy.entity;
        if (emp != null)
        {
            // Simply Save it, the added, modified, deleted state is 100% managed by the proxy
            emp.Save();
        }
    }

    Could it be any easier? Although not shown here (but demonstrated in the WSConsumer application) you can modify and transmit entire collections just as easily. The XmlProxy approach is the best way to go in all scenarios in which you are in control of both ends of the equation. Nothing could be easier or faster.

    The Binary Webservice

    The binary webservice approach can also be used in two way communication scenario's and while it is very easy to use, like the XmlProxy approach, it is far more bulky and bloated, however, the data is somewhat more secure in that it is not human readable.

    [WebMethod]
    public byte[] GetEmployee(int id)
    {
        Employees obj = new Employees();
        if (obj.LoadByPrimaryKey(id))
        {
            BinaryFormatter bf = new BinaryFormatter();
            MemoryStream ms = new MemoryStream();
            bf.Serialize(ms, obj);

           return ms.ToArray();
        }
        else
            return null;
    }

    The packet transmitted will look something like this, only much larger.

    AAEAAAD/////AQAAAAAAAAAMAgAAAD1CdXNPYmosIFZlcnNpb249MS4wLjAuMCwgQ3
    VsdHVyZT1uZXV0cmFsLCBQdWJsaWNLZXlUb2tlbj1udWxsDAMAAABZRW50aXR5U3BhY2
    VzLkNvcmUsIFZlcnNpb249MjAwNy4wLjczMC4wLCBDdWx0dXJlPW5ldXRyYWwsIFB1Ymxp
    Y0tleVRva2VuPTFjMjY4MTk5ZmIyMWJmZTgMBAAAAElTeXN0ZW0sIFZlcnNpb249Mi4wLjA
    uMCwgQ3VsdHVyZT1uZXV0cmFsLCBQdWJsaWNLZXlUb2tlbj1iNzdhNWM1NjE5MzRlMDg5D
    AUAAABOU3lzdGVtLkRhdGEsIFZlcnNpb249Mi4wLjAuMCwgQ3VsdHVyZT1uZXV0cmFsLCBQ
    dWJsaWNLZXlUb2tlbj1iNzdhNWM1NjE5MzRlMDg5BQEAAAAZQnVzaW5lc3NPYmplY3RzLkV
    tcGxveWVlcw8AAAAfX0VtcGxveWVlc0NvbGxlY3Rpb25CeVJlcG9ydHNUbxpfVXBUb1RlcnJp
    dG9yaWVzQ29sbGVjdGlvbh5fRW1wbG95ZWVUZXJyaXRvcmllc0NvbGxlY3Rpb24qX0VtcGxv
    eWVlVGVycml0b3JpZXNDb2xsZWN0aW9uQnlFbXBsb3llZUlEHV9PcmRlcnNDb2xsZWN0aW9
    uQnlFbXBsb3llZUlEBXF1ZXJ5GV9VcFRvRW1wbG95ZWVzQnlSZXBvcnRzVG8PbW9kaWZpZ
    WRDb2x1bW5zA2Zrcxtlc0VtcGxveWVlcytQcm9wZXJ0eUNoY

    And that is not all that is transmitted for a just a single employee object. The reason this is so large is that everything is serialized in binary serialization, the underlying DataTable, it's columns, rows, everything.

     

    Summary

    We encourage you to download and run our WebServices demo, it comes with the v2007.0730 Trial assemblies necessary to run the WSConsumer against our hosted webservices, and shows you the packets as they go back and forth (except in the case of the Xml service, there is no easy way to intercept the soap packets). There re some big improvements (though minor changes in code) in the v2007.0730 release that greatly improve the EntitySpaces proxy classes.

    You can download the full source to both the WSConsumer and Webservices here ==> DOWNLOAD

    NOTE: There is an enforced two second delay between requests to cut down on the traffic for these demo services.

    EntitySpaces

    From mobile devices to large scale enterprise solutions in need of serious transaction support, EntitySpaces can meet your needs. Whether you’re writing an ASP.NET application with medium trust requirements, or a Windows.Forms application, the EntitySpaces architecture is there for you. EntitySpaces is provider independent, which means that you can run the same binary code against any of the supported databases. EntitySpaces is available in both C# and VB.NET. EntitySpaces uses no reflection, no XML files, and sports a tiny foot print of less than 200k. Pound for pound, EntitySpaces is one tough, dependable .NET architecture.

    The EntitySpaces Team
    --

    EntitySpaces LLC
    Persistence Layer and Business Objects for Microsoft .NET
    http://www.entityspaces.net

    posted on Saturday, July 28, 2007 11:08:59 AM (Eastern Standard Time, UTC-05:00)  #   
     Thursday, June 14, 2007

    Epoxy is a REST/POX webservice API that extends the EntitySpaces Architecture. If you are unfamiliar with what REST/POX is I would recommend checking out the excellent series over at Softies On Rails. This addition is meant to empower the creation of massively public http API's for any application that you develop with the EntitySpaces Architecture. Why would you want to expose your EntitySpaces objects via REST/POX and not just provide the defacto SOAP service API? Well, lets face it not everyone is living in the .NET world. Don't you want all your cool ruby coding friends to be able to consume your services easily? Of course you do, not to mention the 100's of other geeks that will write clients if only you allow them to do it in the language of their choice without jumping through a lot of hoops. For a more authoritative answer on when to use REST/POX instead of or in conjunction with SOAP, check out what Don Box has to say about it.

    So now that we all agree that this is indeed something very useful, lets get down to implementation. We chose to use the new WCF REST/POX facilities to create our services. Below is a sample of our Service Contract

    [ServiceContract]
    public interface IUniversalContract
    {
        [OperationContract(Action = "*", ReplyAction = "*")]
        Message ProcessMessage(Message input);
    }

    As you can see our interface consists of one Operation Contract which simply takes a Message as an input. The implementation of the interface is where the rubber hits the road. Below is a sample implementation of the above defined interface using the Employees class generated from the Northwind sample database.

    public Message ProcessMessage(Message request)
    {
        Message response = null;

        //The HTTP Method (e.g. GET) from the incoming HTTP request
        //can be found on the HttpRequestMessageProperty. The MessageProperty
        //is added by the HTTP Transport when the message is received.
        HttpRequestMessageProperty requestProperties =
            (HttpRequestMessageProperty)request.Properties[HttpRequestMessageProperty.Name];

        //Here we dispatch to different internal implementation methods
        //based on the incoming HTTP verb.
        if (requestProperties != null)
        {
            if (String.Equals("GET", requestProperties.Method,
                StringComparison.OrdinalIgnoreCase))
            {
                response = GetEmployee(request);
            }
            else if (String.Equals("PUT", requestProperties.Method,
                StringComparison.OrdinalIgnoreCase))
            {
                response = UpdateEmployee(request);
            }
            else if (String.Equals("POST", requestProperties.Method,
                StringComparison.OrdinalIgnoreCase))
            {
                response = AddEmployee(request);
            }
            else if (String.Equals("DELETE", requestProperties.Method,
                StringComparison.OrdinalIgnoreCase))
            {
                response = DeleteEmployee(request);
            }
            else
            {
                //This service doesn't implement handlers for other HTTP verbs (such as HEAD), so we
                //construct a response message and use the HttpResponseMessageProperty to
                //set the HTTP status code to 405 (Method Not Allowed) which indicates the client
                //used an HTTP verb not supported by the server.
                response = Message.CreateMessage(MessageVersion.None, String.Empty, String.Empty);

                HttpResponseMessageProperty responseProperty = new HttpResponseMessageProperty();
                responseProperty.StatusCode = HttpStatusCode.MethodNotAllowed;

                response.Properties.Add( HttpResponseMessageProperty.Name, responseProperty );
            }
        }
        else
        {
            throw new InvalidOperationException( "This service requires the HTTP transport" );
        }

        return response;
    }

    As you can see the above maps various http verbs to our CRUD methods for our objects. Using the new EntitySpaces WCF support the WCF serialization worked like a charm.

    Endpoints are very important in a REST'ful API, so its worth covering briefly how the WCF implementation handles this. For the context of this post my virtual directory locally is ServiceHost. The actual service file will be called employee.svc in this sample. This relates directly to the address you will use to interact with the API over HTTP.

    **Note if you want this to work you must have WCF installed, be using EntitySpaces Developer 0614 or higher and configure IIS for WCF REST/POX operations.

    In our sample to get a list of all employees you would hit

    http://localhost/servicehost/employees.svc/employees/

    this would produce the following XML

    <ArrayOfanyURI xmlns="http://schemas.microsoft.com/2003/10/Serialization/Arrays"
        xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
        <anyURI>http://scattorshot/servicehost/employees.svc/employees/1</anyURI>
        <anyURI>http://scattorshot/servicehost/employees.svc/employees/2</anyURI>
        <anyURI>http://scattorshot/servicehost/employees.svc/employees/3</anyURI>
    </ArrayOfanyURI>

    As you can see it provides you with a list or URI's for each employee in the list. You can also perform an HTTP POST to this address to add a new employee. You may retrieve a single employee with an EmployeeID of 1 by performing an HTTP GET to the below address

    http://localhost/servicehost/employees.svc/employees/1

    this would produce the following XML

    <Employees>
        <EmployeeID>1</EmployeeID>
        <LastName>Davolio</LastName>
        <FirstName>Nancy</FirstName>
        <Title>Sales Representative</Title>
        <TitleOfCourtesy>Ms.</TitleOfCourtesy>
        <BirthDate>1948-12-08T00:00:00</BirthDate>
        <HireDate>1992-05-01T00:00:00</HireDate>
        <Address>507 - 20th Ave. E. Apt. 2A</Address>
        <City>Seattle</City>
        <Region>WA</Region>
        <PostalCode>98122</PostalCode>
        <Country>USA</Country>
        <HomePhone>(206) 555-9857</HomePhone>
        <Extension>5467</Extension>
        <Photo>
            FRwvAAIAAn....the long base64 encoded string
        </Photo>
        <Notes>
            Education includes a BA in psychology from Colorado State University in 1970. 
            She also completed "The Art of the Cold Call."  Nancy is a member of Toastmasters International.
        </Notes>
        <ReportsTo>2</ReportsTo>
        <PhotoPath>http://accweb/emmployees/davolio.bmp</PhotoPath>
        <esRowState>Unchanged</esRowState>
    </Employees>

    After performing the HTTP GET and retrieving the desired employee resource you could modify it, and then perform an HTTP PUT to the same endpoint to update the employee. In order to delete an employee you would perform an HTTP DELETE to the above address to delete the resource. This is how your endpoints relate to your HTTP verbs, and is at the very heart of the REST style architecture.

    So now you not only have the option to expose your EntitySpaces objects via SOAP services but REST/POX as well. We will be providing the full source code for the sample services as well as a small Windows Form client to consume the service. Choice is always a good thing, and this is certainly no exception.

    EntitySpaces

    From mobile devices to large scale enterprise solutions in need of serious transaction support, EntitySpaces can meet your needs. Whether you’re writing an ASP.NET application with medium trust requirements, or a Windows.Forms application, the EntitySpaces architecture is there for you. EntitySpaces is provider independent, which means that you can run the same binary code against any of the supported databases.EntitySpaces is available in both C# and VB.NET. EntitySpaces uses no reflection, no XML files, and sports a tiny foot print of less than 200k. Pound for pound, EntitySpaces is one tough, dependable .NET architecture.

    The EntitySpaces Team
    --

    EntitySpaces LLC
    Persistence Layer and Business Objects for Microsoft .NET
    http://www.entityspaces.net

    EntitySpaces.RestPox.zip (93.44 KB)