# Saturday, August 07, 2010

Consuming OData from Silverlight 4 can be a very frustrating experience for people like me who are just now joining the Silverlight party.

When it doesn't work, it can fail silently and/or with incorrectly worded warnings.

This is a perfect example of where a blog post can hopefully fill in some of the gaps left by the Microsoft documentation.

First, let's setup a very simple Silverlight 4 application against the odata.org Northwind service.

Note: You may need to install the Silverlight 4 Tools for VS 2010 for this sample.
 http://www.bing.com/search?q=Microsoft+Silverlight+4+Tools+for+Visual+Studio+2010

In Visual Studio 2010:

File -> New -> Project
 Visual C# -> Silverlight -> Silverlight Application -> OK
  New Silverlight Application wizard
   Uncheck "Host the Silverlight application in a new website"
   Silverlight Version: Silverlight 4
   OK

Right Click References in Solution Explorer -> Add Service Reference...
 Address: http://services.odata.org/Northwind/Northwind.svc/
 Namespace: RemoteNorthwindServiceReference
 OK

From the toolbox, drag and drop a DataGrid from the "Common Silverlight Controls" section onto MainPage.xaml
 Change the AutoGenerateColumns property to True
 Change the Margin to 0,0,0,0
 Change the Width to 400

In MainPage.xaml.cs:
 Add the following using statement:

  using System.Data.Services.Client;

 Add the following class variable:

  private DataServiceCollection<RemoteNorthwindServiceReference.Shipper> _shippers;

 Add the following to the bottom of the MainPage constructor method:

  RemoteNorthwindServiceReference.NorthwindEntities remoteNorthwindService =
    new RemoteNorthwindServiceReference.NorthwindEntities(
      new Uri("http://services.odata.org/Northwind/Northwind.svc/"));

  _shippers = new DataServiceCollection<RemoteNorthwindServiceReference.Shipper>();
  _shippers.LoadCompleted +=
new EventHandler<LoadCompletedEventArgs>(_shippers_LoadCompleted);

  var query = from shippers in remoteNorthwindService.Shippers select shippers;
  _shippers.LoadAsync(query);

 Add the following new method:

  private void _shippers_LoadCompleted(object sender, LoadCompletedEventArgs e)
  {
    if (_shippers.Continuation != null)
    {
      _shippers.LoadNextPartialSetAsync();
    }
    else
    {
      this.dataGrid1.ItemsSource = _shippers;
      this.dataGrid1.UpdateLayout();
    }
  }

Debug -> Start Without Debugging
 You should get a dialog box titled "Silverlight Project" that says:
  "The Silverlight project you are about to debug uses web services.  Calls to the web service will fail unless the Silverlight project is hosted in and launched from the same web project that contains the web services.  Do you want to debug anyway?"
 Click "Yes"

This application runs fine.  The warning dialog message was obviously inaccurate and misleading.

I wish I would have understood what this warning dialog was trying to say, but since the remote OData service was working, I had no real choice but to ignore the dialog, which would haunt me when trying to use the same application to call a local OData service.

I believe this dialog is trying to warn you that Silverlight has special constraints when calling web services, but it's still not clear to me what those are.  Proceed cautiously.

Here is one way to successfully call a local OData webservice from Silverlight 4 in Visual Studio 10:

File -> New -> Project
 Visual C# -> Silverlight -> Silverlight Application -> OK
  New Silverlight Application wizard
   OK (Accept the defaults: ASP.NET Web Application Project, Silverlight 4, etc.)

Right Click SilverlightApplication<number>.Web in Solution Explorer -> Add New Item...
 Visual C# -> Data -> ADO.NET Entity Data Model -> Add
  Entity Data Model Wizard
   Next (Generate from database - this assumes you have Northwind running locally)
   New Connection... -> Point to your local Northwind Database Server -> Next
   Select the Tables check box (to select all tables) -> Finish

Right Click SilverlightApplication<number>.Web in Solution Explorer -> Add New Item...
 Visual C# -> Web -> WCF Data Service -> Add

In WcfDataService1.cs:
 Replace " /* TODO: put your data source class name here */ " with "NorthwindEntities" (no quotes)
 Add the following line to the InitializeService method:

  config.SetEntitySetAccessRule("*", EntitySetRights.AllRead);

Debug -> Start Without Debugging

Right Click References under SilverlightApplication<number> in Solution Explorer -> Add Service Reference...
 Discover -> Services In Solution (note the port number of the discovered service, you will need it below)
 Namespace: LocalNorthwindServiceReference
 OK

Stop the browser that was started above

Follow the same basic steps as the first example above to modify MainPage except:
 Replace RemoteNorthwindServiceReference with LocalNorthwindServiceReference
 Replace "http://services.odata.org/Northwind/Northwind.svc/" with "http://localhost:<port number noted above>/WcfDataService1.svc/"

Debug -> Start Without Debugging

This application runs fine (without the warning dialog this time).

It can be ridiculously difficult to get a local OData service working with Silverlight 4 if you don't carefully dance around the project setup issues.
The warning dialog when you start debugging is nearly useless and the app will return no data with no apparent errors if you setup the project incorrectly.

Some references I found useful while building this sample:

 MSDN: How to: Create the Northwind Data Service (WCF Data Services/Silverlight)
 http://msdn.microsoft.com/en-us/library/cc838239(VS.95).aspx

 Audrey PETIT's blog: Use OData data with WCF Data Services and Silverlight 4
 http://msmvps.com/blogs/audrey/archive/2010/06/10/odata-use-odata-data-with-wcf-data-services-and-silverlight-4.aspx

 Darrel Miller's Bizcoder blog: World’s simplest OData service
 http://www.bizcoder.com/index.php/2010/03/26/worlds-simplest-odata-service/

Once you deploy your OData service to a real web hosting environment, you'll likely need to do some special setup to access your OData service from Silverlight 4 (clientaccesspolicy.xml / crossdomain.xml):

 Making a Service Available Across Domain Boundaries
 http://msdn.microsoft.com/en-us/library/cc197955(VS.95).aspx

Tremendous thanks go out to Scott Davis of Ignition Point Solutions for pointing me to the flawed project setup as the reason I couldn't get this working initially.

Saturday, August 07, 2010 12:49:17 PM (GMT Daylight Time, UTC+01:00)  #    Disclaimer  |  Comments [1]  | 
# Sunday, August 01, 2010

This post is an update to a post I made last September (~10 months ago):

http://www.capprime.com/software_development_weblog/PermaLink,guid,162facaa-a3b9-4dc5-b770-657e27e887ad.aspx

The biggest roadblock I ran into when writing that blog post is that ActiveWriter doesn't work well with the Northwind database which had significant ripple effects throughout the sample.

That inspired me to spend quite a bit of time over the last 10 months to make the Castle ActiveRecord code generation story better.

So here is an updated sample using the free version of the Agility for ORMs Castle ActiveRecord code generator in place of ActiveWriter.

--

This is a quick guide to getting up and running with NHibernate and Linq quickly.

We are going to assume our database already exists.  We are going to assume that database is Northwind, and we are going to assume that we are doing database driven design (as opposed to domain driven design).  Northwind setup is described below.

We are going to use Visual Studio 2008 with Service Pack 1 and SQL Server 2008 Express.  (Note: A web application variant of this should work with Visual Web Developer 2008 Express Edition resulting in a completely free development stack.  This sample should also work fine in Visual Studio 2010, but you'll need to change the project to target the .NET Full Profile instead of the .NET Client Profile.)

Step 1:

Download and install the Northwind database.

Jeff Atwood provides approximate instructions here (ask specific questions in the comments if you get stuck):

http://www.codinghorror.com/blog/archives/000434.html

Follow the path for installing the binary files from the command line.  I have tested that the SQL 2005 instructions work on SQL 2008.


Step 2:

Next Download Castle ActiveRecord.  The download link is available from here:

http://www.castleproject.org/castle/projects.html

At the time of writing, the current version is "2.1.2 (2010-01-31)".

Unzip it and remember where you put it, you'll need that info in step 3.


Step 3:

Start Visual Studio 2008

We will create a new console application project:

File -> New -> Project
 Visual C# -> Windows -> Console Application
  ConsoleApplication1 -> OK

We need to add the appropriate NHibernate & Castle Active Record references:

Solution Explorer
 Right Click ConsoleApplication1 -> References -> Add
  Browse Tab
   Go to your Castle ActiveRecord download location and add:
    Castle.ActiveRecord.dll
    Castle.ActiveRecord.Linq.dll
    Castle.DynamicProxy2.dll
    NHibernate.dll
    NHibernate.Linq.dll
   Click OK

And add a reference to System.Configuration as well:

Solution Explorer
 Right Click ConsoleApplication1 -> References -> Add
  .NET Tab
   System.Configuration
  Click OK


Step 4:

Download the free (aka Convention Only) version of the Agility for ORMs Castle ActiveRecord code generator by registering and then downloading here:

http://www.agilityfororms.com/Apps/Register.aspx

The current version is 1.0.0.4.


Step 5:

Run the AFO Castle ActiveRecord code generator, specifying the correct connection string for the Northwind database you setup in Step 1 above.

Note the output directory where the generated files went, you'll need that info in step 6.


Step 6:

Add the generated files to the console application project.

Solution Explorer
 Right Click ConsoleApplication1 -> Add -> New Folder -> DataLayer
 Right Click DataLayer -> Add -> Existing Item...
  Add all of the .cs files generated in Step 5.


Step 7:

We will now add an Application Configuration File to the project and put the Northwind Connection String into it:

Solution Explorer
 Right Click ConsoleApplication1 -> Add -> New Item...
  Visual C# Items -> General -> Application Configuration File
  Add

Modify the file to look like this and use your specific DB connection info:

--
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <connectionStrings>
    <add name="Northwind" connectionString="Data Source=.\SQLExpress;Initial Catalog=Northwind;Trusted_Connection=True;"/>
  </connectionStrings>
</configuration>
--


Step 8:

Open the main "Program.cs" class and add the following new method:

        private static void InitializeNHibernateActiveRecord()
        {
            string connectionString = System.Configuration.ConfigurationManager.ConnectionStrings["Northwind"].ToString();
            InPlaceConfigurationSource configuration = InPlaceConfigurationSource.Build(DatabaseType.MsSqlServer2008, connectionString);

            ActiveRecordStarter.Initialize(System.Reflection.Assembly.GetExecutingAssembly(), configuration);
        }

Add the following code to the Main Method:

        static void Main(string[] args)
        {
            try
            {
                InitializeNHibernateActiveRecord();

                using (new SessionScope())
                {
                    var queryToExecute = from oneProduct in ActiveRecordLinq.AsQueryable<Product>()
                                         select oneProduct;

                    foreach (Product oneProduct in queryToExecute.Take(5).ToList())
                    {
                        Console.WriteLine("ProductID=[" + oneProduct.ProductID + "] ProductName=[" + oneProduct.ProductName + "] Supplier CompanyName=[" + oneProduct.Supplier.CompanyName + "]");
                    }
                }
            }
            catch (Exception oneException)
            {
                Console.WriteLine("oneException=[" + oneException + "]");
                throw; // you can remove this if you'd rather the program exit "more normally"
            }
        }

Add the following using statements at the top of Program.cs:

using Castle.ActiveRecord;
using Castle.ActiveRecord.Framework.Config;
using Castle.ActiveRecord.Linq;

using Model.Northwind;


Step 9:

Copy the NHibernate.ByteCode.Castle.dll file from your Castle Active Record download unzip directory to your projects bin\Debug\ folder.


Step 10:

Run the application:

Ctrl-F5 (Debug -> Start Without Debugging)

And you should see the following output:

--
ProductID=[1] ProductName=[Chai] Supplier CompanyName=[Exotic Liquids]
ProductID=[2] ProductName=[Chang] Supplier CompanyName=[Exotic Liquids]
ProductID=[3] ProductName=[Aniseed Syrup] Supplier CompanyName=[Exotic Liquids]
ProductID=[4] ProductName=[Chef Anton's Cajun Seasoning] Supplier CompanyName=[New Orleans Cajun Delights]
ProductID=[5] ProductName=[Chef Anton's Gumbo Mix] Supplier CompanyName=[New Orleans Cajun Delights]
Press any key to continue . . .
--

We have successfully executed a join query through Linq.


Please note:

You'll notice that the Agility for ORMs Castle ActiveRecord code generator only generated 5 class files in addition to a readme.txt file (there are 13 tables in Northwind which would otherwise result in 11 class files).  Northwind is not a convention based database due to use of assignable keys, surrogate keys, and composite keys.  The free version of the AFO code generator only works with convention oriented database tables and only 5 of the tables in Northwind follow conventions.  The readme.txt file explains why the other tables were not generated.

The commercial version ($30) of the AFO code generator will properly generate code for all 13 tables in Northwind and is intended to support the entire feature set of Castle ActiveRecord including things like Composite Keys, which aren't currently supported by ActiveWriter.


Now that you have the basic NHibernate Linq infrastructure in place, there are plenty of Linq examples and sample code available elsewhere.

Enjoy!

Sunday, August 01, 2010 12:24:48 PM (GMT Daylight Time, UTC+01:00)  #    Disclaimer  |  Comments [0]  |