NHibernate 101: Silverlight 4 with NHibernate 2.n

clock February 1, 2010 05:44 by author jamesstill

In this intro I'll create a vanilla Silverlight 4 application, hosted in a WCF Service, and wired up to SQL Server 2008 with the open source NHibernate ORM tool. I'm not going to dive into the weeds here. I just want to show the basics.

Prerequisites:

* Visual Studio 2010 Beta 2
* Silverlight 4 Tools for VS 2010
* NHibernate 2.n
* SQL Server 2008

In Visual Studio create a new project using the Silverlight Application project template. When prompted, choose to host the project in a new web site with the default ASP.NET Web Application Project type. I'll call it "ConcertMusic.Web" because we're going to fetch a list of classical music instruments from a database table and display them in a SL DataGrid. Do not enable .NET RIA Services.

After the project template unfolds right-click the ConcertMusic.Web and create a WCF Service using the Silverlight-enabled WCF Service template. I'm not going to get into WCF here but you'll want to create a contract interface, configure the service behavior and bindings, and perhaps add a ClientAccessPolicy.xml file. Here's my service contract:

   1:  namespace ConcertMusic.Web
   2:  {
   3:      [ServiceContract(Namespace="http://ConcertMusic.Web/2010/07")]
   4:      public interface IConcertMusicService
   5:      {
   6:          [OperationContract]
   7:          IList<Instrument> GetInstruments();
   8:      }
   9:  }

Now let's configure NHiberate. Add references to the required NHibernate assemblies: NHibernate.dll, log4net.dll, Iesi.collections.dll, and Antlr3.Runtime.dll. (Optionally, include Castle or Spring assemblies if you want to support lazy loading. I'm not going to get into that here.)

In Web.config add the plumbing to configure NHibernate:

   1:  <configuration>
   2:   
   3:    <configSections>
   4:      <section name="hibernate-configuration" type="NHibernate.Cfg.ConfigurationSectionHandler, NHibernate" />
   5:    </configSections>
   6:   
   7:    <hibernate-configuration xmlns="urn:nhibernate-configuration-2.2">
   8:      <session-factory>
   9:        <property name="connection.provider">NHibernate.Connection.DriverConnectionProvider</property>
  10:        <property name="connection.driver_class">NHibernate.Driver.SqlClientDriver</property>
  11:        <property name="connection.connection_string">Data Source=SERVER_INSTANCE;Initial Catalog=DatabaseName;Integrated Security=SSPI</property>
  12:        <property name="dialect">NHibernate.Dialect.MsSql2005Dialect</property>
  13:        <property name="show_sql">false</property>
  14:        <property name="proxyfactory.factory_class">NHibernate.ByteCode.Castle.ProxyFactoryFactory, NHibernate.ByteCode.Castle</property>
  15:      </session-factory>
  16:    </hibernate-configuration>

Ok so we've got the NHibernate assemblies referenced and we've configured it the project. Let's start at the database and work our way forward. First, we've got a table of musical instruments in the database:

   1:  CREATE TABLE [dbo].[Instrument](
   2:      [ID] [int] IDENTITY(1,1) NOT NULL,
   3:      [Name] [nvarchar](50) NULL,
   4:      [Description] [nvarchar](120) NULL,
   5:   CONSTRAINT [PK_Instrument] PRIMARY KEY CLUSTERED 
   6:  (
   7:      [ID] ASC
   8:  )WITH 
   9:  (PAD_INDEX  = OFF, 
  10:  STATISTICS_NORECOMPUTE  = OFF, 
  11:  IGNORE_DUP_KEY = OFF, 
  12:  ALLOW_ROW_LOCKS  = ON, 
  13:  ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
  14:  ) ON [PRIMARY]
  15:   
  16:  GO

We need to do two things to map the table to a plain old CLR object (POCO) in our application. We have to create the POCO class and then we have to create a mappping file that tells NHibernate how to create an instance of that class from the data store. Here's our simple POCO class:

   1:  [DataContract]
   2:  public class Instrument
   3:  {
   4:      public Instrument() { }
   5:   
   6:      [DataMember]
   7:      public virtual int ID { get; set; }
   8:   
   9:      [DataMember]
  10:      public virtual string Name { get; set; }
  11:   
  12:      [DataMember]
  13:      public virtual string Description { get; set; }
  14:  }

Notice that we're using auto-implemented properties introduced in C# 3.0. Also, the class is implemented as a DataContract with DataMember properties. This is necessary if you want to bind an IList collection of Instrument objects to a Silverlight control. If you don't use DataMember attributes, then Silverlight will attempt to bind to the IL-generated backing fields instead and you'll get skinny blank rows in your DataGrid.

Ok, now let's create the NHibernate mapping file. It can live in the same place as the POCO or you can put your mapping files in a separate folder to keep them all together. It's up to you. The naming convention is [ClassName].hbm.xml:

   1:  <?xml version="1.0" encoding="utf-8"?>
   2:  <hibernate-mapping assembly="ConcertMusic.Web" xmlns="urn:nhibernate-mapping-2.2">
   3:    <class name="ConcertMusic.DomainModel.Instrument, ConcertMusic.DomainModel" table="Instrument" lazy="false" >
   4:      <id name="ID" type="Int32" column="ID">
   5:        <generator class="identity" />
   6:      </id>
   7:      <property name="Name" column="Name" />
   8:      <property name="Description" column="Description" />
   9:    </class>
  10:  </hibernate-mapping>

I'm not going into a long explanation of this file. Read the NHibernate docs for a detailed specification. For now I'll just say that ID is described as a synthetic key (identity column), and Name and Description are mapped one-to-one to identical column names in the table. Pretty straightforward. Very important! Change the build action of this xml file to make it an embedded resource. It needs to be compiled into the assembly in order by NHibernate to find it at runtime.

NHibernate uses an ISessionFactory to talk to the database. Just like IDbConnections in ADO.NET, these are very expensive to set up so you don't want to set them up and tear them down with every transaction to the database. The best practice is to create them once and then hold onto them during the lifetime of the application. Here I'm using a Singleton pattern to wrap the factory in a helper class:

   1:  public class NHibernateHelper
   2:  {
   3:      public static readonly ISessionFactory SessionFactory;
   4:   
   5:      static NHibernateHelper()
   6:      {
   7:          Configuration cfg = new Configuration().AddAssembly("ConcertMusic.DomainModel");
   8:          SessionFactory = cfg.Configure().BuildSessionFactory();
   9:      }
  10:   
  11:      public static ISession OpenSession()
  12:      {
  13:          return SessionFactory.OpenSession();
  14:      }
  15:  }

In line 7 I'm adding the assembly ConcertMusic.DomainModel to the NHibernate configuration. This is to tell NHibernate where my [ClassName].hbm.xml mapping files live. I created a separate project for my POCO classes and NHibernate mapping files. Change this to Silverlight1.Web or whatever assembly name where your mapping files were added.

One last plumbing task to complete. We need to implement the OperationContract so that Silverlight can call and fetch a list of musical instruments. I keep my web methods very thin but for illustration purposes only I'll just code it right there:

   1:  public class ConcertMusicService : IConcertMusicService
   2:  {
   3:      public IList<Instrument> GetInstruments()
   4:      {
   5:          IList<Instrument> list;
   6:          using (ISession session = NHibernateHelper.OpenSession())
   7:          {
   8:              IQuery query = session.CreateQuery("FROM Instrument");
   9:              list = query.List<Instrument>();
  10:          }
  11:          return list;    
  12:      }
  13:  }

Do not do this in a production application. Get that data access plumbing out of your web method and into a data layer somewhere. That makes it easy to write unit tests that call the data layer too. But this is fine for our purposes. Run your unit tests and make sure it's all working. Then wire up the Silverlight MainPage.xaml to consume the data. Drag a DataGrid onto your MainPage.xaml. It should register the System.Windows.Controls.Data assembly and stub out a DataGrid for you:

   1:  <data:DataGrid x:Name="InstrumentDataGrid" AutoGenerateColumns="False" Height="400" Width="200">
   2:      <data:DataGrid.Columns>
   3:          <data:DataGridTextColumn Header="ID" Binding="{Binding ID}" />
   4:          <data:DataGridTextColumn Header="Name" Binding="{Binding Name}" />
   5:      </data:DataGrid.Columns>
   6:  </data:DataGrid>

Create a service reference to the WCF service in your Silverlight app. Then in the code behind make an async call to the web service and bind the results to the grid:

   1:  public partial class MainPage : UserControl
   2:  {
   3:      public MainPage()
   4:      {
   5:          InitializeComponent();
   6:          Loaded += new RoutedEventHandler(MainPage_Loaded);
   7:      }
   8:   
   9:      void MainPage_Loaded(object sender, RoutedEventArgs e)
  10:      {
  11:          ConcertMusicServiceClient service = new ConcertMusicServiceClient();
  12:          service.GetInstrumentsCompleted += 
  13:              new EventHandler<GetInstrumentsCompletedEventArgs>(Service_GetInstrumentsCompleted);
  14:          service.GetInstrumentsAsync();
  15:      }
  16:   
  17:      void Service_GetInstrumentsCompleted(object sender, GetInstrumentsCompletedEventArgs e)
  18:      {
  19:          InstrumentDataGrid.ItemsSource = e.Result;  
  20:      } 
  21:  }

That's all there is to it.

Currently rated 2.0 by 1 people

  • Currently 2/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5


WCF 4

clock November 19, 2009 06:23 by author jamesstill

You no longer need to describe in detail all of the configuration for endpoints, bindings and protocols. For example, if you wipe out your web.config then basicHttp protocol is used by default. Also, and this is obscure, by default WCF 4 will act as a protocol bridge. If the client request is SOAP 1.1 but the back end service is SOAP 1.2 the bridge will transform the message automatically, which is transparent both to the client and service. In effect then the client can be an old skool app speaking SOAP 1.1 in http and the back end service can be SOAP 1.2 in tcp and no additional endpoints need to be configured. You do need to create a RoutingService to support routing behavior.

Something called "ETW" (also built into Azure AppFabric) is the new direction for instrumentation for IIS, ASP.NET, and WPF as well as apps running in the Azure cloud. For the purposes of WCF 4, it will be possible to instrument the whole call stack and pinpoint exactly which service and contract implementation is the bottleneck.

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5


Microsoft PDC09 Keynote Address

clock November 17, 2009 05:09 by author jamesstill

Microsoft announced the new bits at PDC keynote today. The theme? Cloud, cloud, and more cloud. The Azure platform goes into production on Jan 1. In beta through the next several months are new cloud platform tools: AppFabric (previously code named "Dublin") and Endpoint. From what I understand AppFabric provides a unified hosting environment for WCF and Workflow, SQL Azure database caching, and can be configured right in IIS. Endpoint seems to be a discovery service for web services all over the world. You can search it and get all the information you need to consume that data in your application. It probably uses the REST protocol. Microsoft showed a pretty cool demo with the NASA web service that involved showing 3D landscapes of pictures taken by the Mars Rover. You could do a serious mashup in a .NET app with Endpoint.

The CIO of the U.S. government gave a little pep talk about cloud computing. He said the Obama Administration is committed to the cloud and is actively migrating to it now. Honestly, will Multnomah County really spend $3.5 million to build a new DC? Next year, Azure will offer totally configurable VMs in any flavor you want. We could run all of the county DC virtualized on Azure. New apps would plug right in to the cloud environment and legacy stuff could be put on configurable virtual servers in WinOS 2003, 2008, whatever.

Oh and Visual Studio 2010 with .NET Framework 4.0 will be released sometime in Q1 or Q2. The beta is out now and there was some cool stuff in it. You can drag a code pane over to your other monitor, debugging is beefed up, and unit tests write themselves at run time. Other stuff but it went pretty fast.

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5


IIS 7 Non-HTTP Protocol Support

clock July 19, 2009 05:08 by author jamesstill

More fun with WCF. Out of the box IIS 7 supports the HTTP protocol only. To configure named pipes, TCP, or MSMQ you must use the IIS configuration tool appcmd.exe located in %windir%\system32\inetsrv. (The command-line tool updates the %windir%\system32\inetsrv\config\applicationHost.config file.) For example, if tcp runs over port 808 and my web server's windir is c:\windows then the command to enable tcp is:

c:\windows\system32\inetsrv\appcmd.exe set site "Default Web Site" -+bindings.[protocol='net.tcp',bindingInformation='808:*']

One last detail: each WCF service hosted in its own application (virtual) directory beneath the Default Web Site must also be explicitly enabled for non-HTTP protocol support:

c:\windows\system32\inetsrv\appcmd.exe set app "Default Web Site/[app dir]" /enabledProtocols:http,net.tcp

Now that the tcp protocol is supported in IIS 7 over port 808, a client endpoint is available for netTcpBinding.

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5


Windows Vista Port 8000

clock July 4, 2009 06:25 by author jamesstill

Port 8000 is not open in Windows Vista except when running as a system administrator. And so with User Account Control (UAC) enabled, you cannot navigate to http://localhost:8000 or run self-hosted WCF Services without opening the port. To open for your user account, open a command prompt (running as Administrator) and invoke the netsh utility:

      netsh http add urlacl url=http://+:8000/ user=MYMACHINE\UserName

 

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5


About

SquareWidget LLC is an Oregon-based software development company that specializes in handcrafted .NET software solutions.

Search

Archive

Categories


Sign in