Tim Scott's Blog

July 13, 2008

RhinoCommons With MS MVC

Filed under: MS MVC, NHibernate — Tim Scott @ 10:34 pm

If you are building an MS MVC web app using Rhino Commons you will probably want to run it as a UnitOfWorkApplication, like so:

public class GlobalApplication : UnitOfWorkApplication

If you stop there it will not work. You will get “The resource cannot be found” error when you run it. Michael Hanney has found a workaround that solves this issue. Change your Application_Start method as follows:

public override void Application_Start(object sender, EventArgs e)
{
      base.Application_Start(sender, e);
      RegisterRoutes(RouteTable.Routes);
}
Advertisements

May 9, 2007

Remoting Using WCF and NHibernate

Filed under: C#, NHibernate, WCF — Tim Scott @ 5:00 am

By Tim Scott and Greg Banister

UPDATE – 3/5/09 – There is ongoing discussion in the community over remoting versus a service oriented architecture (SOA).  Almost two years after its publication this post continues to to get a lot of traffic, and so we feel it’s appropriate to add a small comment with our opinion on the matter.   We generally do not recommend remoting (sending domain objects over the wire), and would in almost all cases today, prefer an SOA approach. This article shows how we solved a specific technical problem in implementing a smart client application using a remoting architecture.   It has value on that basis.

Introduction

Windows Communication Foundation (WCF) is Microsoft’s unified messaging framework for building distributed applications. It is targeted at two types of applications:

  • distributed objects applications
  • service oriented applications (SOA)

Let’s define what we mean by each. “Distributed objects applications” are applications where client and service share common custom business object types and pass these objects back and forth over the wire. Conversely, SOAs typically hide business object types and pass lighter data transfer objects between client and service. Microsoft intends for WCF to supersede ASMX Web Services for SOA applications and .NET Remoting for distributed objects applications.

NHibernate is a port of Hibernate, the popular open source object‑relational mapper for Java. NHibernate has been gaining popularity in the .NET world, and we are avid users. As we will discuss in this article, it is possible to build a distributed objects application using WCF for messaging and NHibernate for object persistence. But to pull this off you have to know some tricks. As one wades into the WCF waters, one discovers that WCF has a lot of tricks. Articles and samples abound for WCF, but we could find none that address the issues that we faced. In this article we will share what we learned.

The Application

In early 2006 our team committed to create a “smart client” application using WCF for communications between a WinForms client and a Web service. Client and service would contain a common business domain assembly and would pass business objects back and forth. We also decided to use NHibernate for object persistence. This sounded great on paper, and after a little hacking we got it to work, or so we thought.

As we prepared to develop this application, we studied many WCF articles and “getting started” examples. We even created a “spike” application using WCF together with NHibernate. Based on all this research we made dozens of explicit configuration choices. We made many more choices by default.

The Problem

One such default choice was to use the DataContractSerializer. Only much later did we learn that Microsoft recommends the DataContractSerializer specifically because it assumes the client knows nothing of .NET types. This keeps coupling between client and service loose and so maximizes cross‑platform interoperability. The DataContractSerializer, we realized at long last, was the problem.

So why was it the problem? In a word, collections. Complex business objects typically have collections (e.g., Order has LineItems). One thing we love about NHibernate is how nicely it handles persistent collections. It provides various custom collection types that implement IList. We use the NHibernate Bag for all of our collections. Bag contains several properties that help NHibernate detect changes to collections and persist them properly. By using some clever versioning techniques, NHibernate always knows which members of collections to insert, update and delete any time you save its parent object.

As an aside, NHibnerate has other choices for collections besides Bag. There are Sets, Lists, Maps, Arrays, SortedSets, SortedMaps and IdBags, all of which, like Bag, inherit from NHibernate.Collection.PersistentCollection. This article addresses only the Bag type. We would guess the same problems and solutions apply to these other types as well; however, we have not used these types, so we cannot say for sure.

Back to the DataContractSerializer. It is capable of serializing custom objects. To accomplish this you simply decide which classes and class members to share and decorate them with DataContract attribute and DataMember attribute respectively. If the class members are not primitive types, you can further decorate your classes with KnownType attributes. This attribute tells WCF that both sender and receiver understand the custom type. Here is an example of a Company class decorated for WCF:

[DataContract]
[KnownType(typeof(NHibernate.Collection.Bag))]
[KnownType(typeof(NHibernate.Impl.CollectionEntry))]
[KnownType(typeof(CompanyRole))]
[KnownType(typeof(Person))]
public class Company : IEntity
{
    [DataMember]
    private Guid id;
    private string name;
    [DataMember]
    private IList employeesList;   

    public Guid Id
    {
        get { return id; }
    }   

    [DataMember]
    public string Name
    {
        get { return name; }
        set { name = value; }
    }   

    private IList EmployeesList
    {
        get
        {
            if (employeesList == null)
            {
                employeesList = new ArrayList();
            }
            return employeesList;
        }
        set { employeesList = value; }
    }   

    public Person[] Employees
    {
        get
        {
            Person[] employees = new Person[EmployeesList.Count];
            EmployeesList.CopyTo(employees, 0);
            return employees;
        }
    }   

    .
    .
    .   

}

When NHibernate fetches a Company from the database, employeesList will be a Bag of Persons. We assumed that by setting Bag as a known type, WCF would properly handle employeesList. However, even with the KnownType attribute, DataContractSerializer does not properly handle NHibernate Bags. The employeesList enters the serializer as a Bag and comes out the other end as an object array. Before serialization:

bag.jpg

After serialization:

objectgraph.jpg

The following test further illustrates the problem:

[TestMethod]
public void DataContractSerialization_will_change_the_type_of_a_Collection()
{
    Company company = GetCompanyFromNhibernate();   

    //company.EmployeesList made public for the purpose of this demo
    Assert.AreEqual(typeof(NHibernate.Collection.Bag), company.EmployeesList.GetType());   

    List<Type> knownTypes = new List<Type>();
    knownTypes.Add(typeof(Person));
    knownTypes.Add(typeof(NHibernate.Collection.Bag));
    knownTypes.Add(typeof(NHibernate.Impl.CollectionEntry));
    DataContractSerializer serializer = new
             DataContractSerializer(typeof(Company),knownTypes);   

    //serialize company to a memory stream
    Stream stream = new MemoryStream();
    serializer.WriteObject(stream,company);   

    //deserialize the memory stream back to a company
    stream.Position = 0;
    company = (Company) serializer.ReadObject(stream);   

    Assert.AreNotEqual(typeof(NHibernate.Collection.Bag), company.EmployeesList.GetType());
    Assert.AreEqual(typeof(object[]), company.EmployeesList.GetType());
}

One might expect that NHibernate to blow up when it encounters a persisted collection that has morphed into a simple object array. Not so! After a small hack that we created, NHibernate was able to persist updates and inserts just fine. However, it was not able to figure out deletions. Rather than deleting an item that had been removed from the collection, it simply de‑referenced it. That is, it updated the foreign key to its parent (in the preceding example, CompanyID) to NULL. These de-referenced items end up as orphaned records in the database. In our case, for certain collections, the result was hundreds of thousands of orphans.

The Solution

We recall vividly the moment we discovered this problem. Once we stopped sobbing uncontrollably, we pressed on and worked out a solution (sort of). We added a bunch of tricky code to our persistence layer to detect orphans and delete them from the database. We’ll not belabor the point by explaining this complex hack; suffice it to say it was both ugly and inefficient!

Then one day, almost by accident, we found the NetDataContractSerializer. Actually we had known about it, but we hadn’t grasped what it was. The NetDataContractSerializer, unlike its brother the DataContractSerializer, serializes .NET type information. To use it both client and server must understand .NET types. Cross‑platform interoperability is lost, which was fine in our case. Our client and service are by nature tightly coupled – they share a business domain.

So we gave the NetDataContractSerializer a try. To our delight it serialized NHibernate Bags just fine. Problem solved! The following test illustrates:

[TestMethod]
public void NetDataContractSerialization_will_Not_change_the_type_of_a_Collection()
{
    Company company = GetCompanyFromNhibernate();   

    //company.EmployeesList made public for the purpose of 
    //this demo
    Assert.AreEqual(typeof(NHibernate.Collection.Bag), company.EmployeesList.GetType());   

    NetDataContractSerializer serializer = new NetDataContractSerializer();   

    //serialize company to a memory stream
    Stream stream = new MemoryStream();
    serializer.Serialize(stream, company);   

    //deserialize the memory stream back to a company
    stream.Position = 0;
    company = (Company)serializer.Deserialize(stream);   

    Assert.AreEqual(typeof(NHibernate.Collection.Bag),company.EmployeesList.GetType());
    Assert.AreNotEqual(typeof(object[]), company.EmployeesList.GetType());
}

Soon we learned that NetDataContractSerializer solved other problems too. From the start we had wanted to pass generic types between client and service. We could not figure out how to do this using DataContractSerializer. Again, KnownType attributes did not seem to work. There might be some way to do it, but we did not find it. No matter, NetDataContractSerializer handles .NET generic types just fine.

We should mention that this application uses NHibernate 1.02. As of this writing, NHibernate has released version 1.2. We tested the application with NHibernate 1.2 before changing to the NetDataContractSerializer, and verified that it exhibits the same problem. We have not verified that the solution described here will work with NHibnerate 1.2, although we expect it will.

Using NetDataContractSerializer

Unfortunately you cannot simply flip a config switch and change serializers. You need to write some code. We created two classes, like so:

public class NetDataContractOperationBehavior : DataContractSerializerOperationBehavior
{
    public NetDataContractOperationBehavior(OperationDescription operation)
        : base(operation)
    {
    }   

    public NetDataContractOperationBehavior(OperationDescription operation, DataContractFormatAttribute dataContractFormatAttribute)
        : base(operation, dataContractFormatAttribute)
    {
    }   

    public override XmlObjectSerializer CreateSerializer(Type type, string name, string ns,
        IList<Type> knownTypes)
    {
        return new NetDataContractSerializer(name, ns);
    }   

    public override XmlObjectSerializer CreateSerializer(Type type, XmlDictionaryString name,
        XmlDictionaryString ns, IList<Type> knownTypes)
    {
        return new NetDataContractSerializer(name, ns);
    }
}   

public class UseNetDataContractSerializerAttribute : Attribute, IOperationBehavior
{
    public void AddBindingParameters(OperationDescription description, BindingParameterCollection parameters)
    {
    }   

    public void ApplyClientBehavior(OperationDescription description,
        System.ServiceModel.Dispatcher.ClientOperation proxy)
    {
        ReplaceDataContractSerializerOperationBehavior(description);
    }   

    public void ApplyDispatchBehavior(OperationDescription description,
        System.ServiceModel.Dispatcher.DispatchOperation dispatch)
    {
        ReplaceDataContractSerializerOperationBehavior(description);
    }   

    public void Validate(OperationDescription description)
    {
    }   

    private static void ReplaceDataContractSerializerOperationBehavior( OperationDescription description)
    {
        DataContractSerializerOperationBehavior dcsOperationBehavior =
        description.Behaviors.Find<DataContractSerializerOperationBehavior>();   

        if (dcsOperationBehavior != null)
        {
            description.Behaviors.Remove(dcsOperationBehavior);
            description.Behaviors.Add(new NetDataContractOperationBehavior(description));
        }
    }
}

Then in the service contract, to every method we added the UseNetDataContractSerializer attribute, like so:

[UseNetDataContractSerializer]
[OperationContractAttribute]
Company SaveCompany(CompanyUpdater companyUpdater);

NOTE: We derived the code to use NetDataContractSerializer from code examples we found on the web:

Conclusion

Our specific conclusion is, to serialize objects with NHibernate Bags using WCF, use the NetDataContractSerializer. A more general conclusion might be, if you want to distribute custom business objects between a .NET client and WCF service only, such as with a distributed objects application, use NetDataContractSerializer unless there is some compelling reason not to.

Blog at WordPress.com.