Sending NHibernate entities over the WCF wire

38 commentsWritten on January 3rd, 2008 by
Categories: NHibernate, WCF

Note: i no longer recommend this solution for reasons that i've talked about in more detail here.

Last year, Tim Scott posted this very good article on how to distribute NHiberate entities through WCF services. In it, he mentions this:

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.

I created a small sample that uses his solution with NHibernate 1.2 and my Northwind example and in this post, we'll walk through the sample. But first, a little bit of background information. By default, WCF services use the DataContractSerializer to serialize/deserialize types. But when you're using NHibernate, you're most likely using some of the persistent collection types and proxies. Some people just want to use the same types serverside and clientside. For instance, retrieving a Customer object with his Orders collection through a service, manipulating one of the Order objects clientside, perhaps remove an order from the collection, send the object graph back to the server, attach it to an nhibernate session, persist the whole thing and be done with it. I'm still somewhat undecided as to whether or not this is a good way of doing things but that's beside the point of this post. Default WCF services do not make this possible but you can make it work rather easily. WCF includes the NetDataContractSerializer which differs from the normal DataContractSerializer in that it includes CLR type information in the serialized data. This makes the above scenario possible. You do lose all interoperability with other platforms, and your clients need the same types you use server side.

First of all, this is the service contract:

    [ServiceContract]

    public interface ICustomerService

    {

        [UseNetDataContractSerializer]

        [OperationContract]

        IList<Customer> GetCustomersWithTheirOrders();

 

        [UseNetDataContractSerializer]

        [OperationContract]

        void PersistCustomer(Customer customer);

    }

The [UseNetDataContractSerializer] is not a standard WCF attribute, but is a part of Tim Scott's solution. You can find the implementation of the attribute in his post, or in my code which you can download at the bottom of this post. It basically comes down to this: the serialization behavior of operations decorated with this attribute is modified to use the NetDataContractSerializer instead of the default DataContractSerializer.

The implementation of the service contract looks like this:

    public class CustomerService : ICustomerService

    {

        private static readonly ISessionFactory _sessionFactory;

 

        static CustomerService()

        {

            try

            {

                Configuration configuration = new Configuration().AddAssembly("Northwind.Domain");

                _sessionFactory = configuration.BuildSessionFactory();

            }

            catch (Exception e)

            {

                Console.Write(e);

                throw;

            }

        }

 

        public IList<Customer> GetCustomersWithTheirOrders()

        {

            using (ISession session = _sessionFactory.OpenSession())

            {

                return session.CreateCriteria(typeof(Customer))

                    .SetFetchMode("Orders", FetchMode.Join)

                    .SetResultTransformer(CriteriaUtil.DistinctRootEntity)

                    .List<Customer>();

            }

        }

 

        public void PersistCustomer(Customer customer)

        {

            using (ISession session = _sessionFactory.OpenSession())

            {

                using (ITransaction transaction = session.BeginTransaction())

                {

                    session.SaveOrUpdate(customer);

                    session.Flush();

                    transaction.Commit();

                }

            }

        }

    }

First of all, this is just an example, that's why the ISessionFactory is created within the service implementation, in a real system i wouldn't do this. Anyway, the GetCustomersWithTheirOrders method returns a list of all Customers, with their Orders. An Order contains references to an Employee and a Shipper. The Employee and Shipper will not be retrieved from the database, but NHibernate will initialize them with proxy objects to enable lazy-loading. Obviously, the lazy-loading won't work once you're outside of the scope of the NHibernate session, but it's important to note that there will be proxies in our object graph.

At first i had decorated my entity classes with the [DataContract] and [DataMember] attributes but that really messed up the deserialization of the proxies. Now my Entity classes are only decorated with the [Serializable] attribute. NetDataContractSerializer should work in both cases, but i only got it working properly when they were [Serializable].

Right, so now we have the service contract and the implementation, it's time to host it. My solution contains an example of a console host as well as a service hosted in IIS. For this post, i'll just go over the console host and console client. You can find the IIS example (which is practically identical anyway) in the downloadable solution.

So, in the console host project, i have the following in my app.config file:

  <system.serviceModel>

    <services>

      <service name="ServiceImplementation.CustomerService" behaviorConfiguration="customerServiceBehavior">

        <endpoint contract="ServiceInterface.ICustomerService" binding="wsHttpBinding"

                  bindingConfiguration="customerServiceBinding" />

        <endpoint contract="IMetadataExchange" binding="mexHttpBinding" address="mex" />

        <host>

          <baseAddresses>

            <add baseAddress="http://localhost:8000/CustomerService"/>

          </baseAddresses>

        </host>

      </service>

    </services>

 

    <bindings>

      <wsHttpBinding>

        <binding name="customerServiceBinding" maxReceivedMessageSize="2147483647" />

      </wsHttpBinding>

    </bindings>

 

    <behaviors>

      <serviceBehaviors>

        <behavior name="customerServiceBehavior" >

          <serviceMetadata httpGetEnabled="true" httpGetUrl=""/>

        </behavior>

      </serviceBehaviors>

    </behaviors>

 

  </system.serviceModel>

The service is then started like this:

        private static void Main(string[] args)

        {

            using (ServiceHost host = new ServiceHost(typeof(CustomerService)))

            {

                host.Open();

 

                Console.WriteLine("press ENTER to quit");

                Console.ReadLine();

            }

        }

As you can see, nothing special here... it's pretty much your typical self-hosting WCF example. Except that the maximum message size has been increased since we'll be sending large object graphs over the wire.

The client configuration looks like this:

  <system.serviceModel>

    <bindings>

      <wsHttpBinding>

        <binding name="customerServiceBinding" maxReceivedMessageSize="2147483647" />

      </wsHttpBinding>

    </bindings>

 

    <client>

      <endpoint address="http://localhost:8000/CustomerService" binding="wsHttpBinding" name="CustomerServiceEndPoint"

                bindingConfiguration="customerServiceBinding" contract="ServiceInterface.ICustomerService" />

    </client>

 

  </system.serviceModel>

And the client uses the service like this:

        private static void Main(string[] args)

        {

            ChannelFactory<ICustomerService> factory = new ChannelFactory<ICustomerService>("CustomerServiceEndPoint");

            ICustomerService proxy = factory.CreateChannel();

            IList<Customer> customers = proxy.GetCustomersWithTheirOrders();

        }

I didn't write a test for this to prove it works, but if you step through it and you explore the returned object graph using the debugger, you'll see that everything is of the correct type... even the proxies make it through correctly. Manipulating the graph and using the PersistCustomer method also works, but it's not in this example anymore because i didn't wanna pollute my database every time i ran it to test it.

I did have problems when i was hosting the service both in the console and through IIS at the same time... some requests would then fail to deserialize the returned graph properly. If you don't run both hosts at the same time, it works. I have no idea why this caused issues, but after wasting a few hours trying to figure it out, i just gave up. You can reproduce it by running the ServiceConsoleHost, ServiceClient and ServiceWebClient projects in the solution. Sometimes that just works as well, so you may have to try a few times to get it to fail. So if anyone can shed some light on that issue, i'd be most interested in hearing about it :)

You can download the solution here. Note that this is a Visual Studio 2008 solution...

Tags: ,

  • Luc

    Hi thx for the example.

    Is it the same serializtion problem with webservice and nhibernate. Will this solution work also with webservices

    thx

    Luc

  • http://ralinx.wordpress.com Davy Brion

    No this is a WCF-specific solution so this won’t work with classic ASP.NET webservices

  • Miguel

    Do you have the examples in a Visual Studio 2005 project format????

  • http://ralinx.wordpress.com Davy Brion

    Sorry, i only have Visual Studio 2008 around these days

  • Chris Pisani

    Is there a way to extend the NHibernate session from the service to the client to enable lazy loading for the client? Passing the proxied object to the client seems undesirable since it would throw an exception if the client tried to access the proxied object/property.

  • http://ralinx.wordpress.com Davy Brion

    I don’t think that’s possible but i’m not sure… I don’t think it would be a good idea though. You’re probably better off fetching all the data you need for your use case and sending it back to the client in one go. Otherwise, you could be looking at crossing the wire for every lazy loaded property you access which would almost certainly cause performance problems. And if you access those properties in your use case, you know in advance that you’ll need that data so you might as well retrieve it up front.

  • http://void.by/ VoiD

    Thanks!

  • http://orand.blogspot.com Oran

    This solution promises to support lazy loading of NHibernate entities across the wire using WCF.
    http://slagd.com/?p=4

  • http://ralinx.wordpress.com Davy Brion

    Well, as i mentioned before I really don’t think that’s a good idea… crossing the wire too much can be really expensive.

  • http://sachabarber.net sacha

    This looks to be exactly what I am after, but when I open the attached project in VS2008, I get the following error on the ServiceWebHost project.

    System.Runtime.InteropServices.ComException.

    Any idea what could be causing this. Would it be possible to email me the project again?

    I would be extremely grateful.

  • http://ralinx.wordpress.com Davy Brion

    the ServiceWebHost project hardly has code, and it certainly doesn’t use Interop directly… maybe there is a problem with your IIS installation/configuration? Can you host other WCF services without problems in IIS?

  • http://recipher.co.uk/ Johnny Hall

    “First of all, this is just an example, that’s why the ISessionFactory is created within the service implementation, in a real system i wouldn’t do this.”

    Out of interest, how would/do you do this? I’m having trouble right now deciding how best to accomplish this, particularly with regard to IoC using Windsor.

    Cheers.

  • http://ralinx.wordpress.com Davy Brion

    i’d go for a SessionProvider kind of class which would be responsible for configuring and initializing the ISessionFactory and for handing out ISessions to consumers. I wouldn’t use an IoC container to create the ISessionFactory because you’ll probably never use another implementation than the one NHibernate gives you anyway.

  • http://recipher.co.uk/ Johnny Hall

    Thanks. That’s how I’m doing it now, more or less but I can think of at least one situation where I don’t want to hand out standard ISession objects – unit testing the service layer.

    In that situation, I want something like an InMemorySessionFactory. That’s my big problem with WCF and NH at the moment – because the SessionFactory is so expensive – it needs to be created only the once, which in WCF means being a static. In this instance, how do you override the default implementation that the service uses?

    It beats me.

  • http://ralinx.wordpress.com Davy Brion

    the NHibernate SessionFactory is completely thread-safe so there’s not really a problem with it being a static instance behind a service layer.

    As for the unit testing, have you considered using an in-memory database to run your tests against? I’m not sure but i think NHibernate has support for SQLite.

  • Pingback: Links Today (2008-04-07)

  • Pingback: links for 2008-04-08 « dstelow notes…

  • http://giammin.blogspot.com giammin

    Is NetDataContractSerializer compatible with Compact Framework?

  • http://davybrion.com Davy Brion

    http://msdn.microsoft.com/en-us/library/system.runtime.serialization.netdatacontractserializer.aspx

    the compact framework isn’t listed among the supported .NET frameworks

  • N

    Hi there,

    I need to serialize / deserialize object to cml and since I am using nHibernate I have to use NetDataContractSerializer/DataContractSerializer.

    I used the above code and put [UseNetDataContractSerializer] on the getters of the IList (which so far failed to serialize or deserialized as array when using UseNetDataContractSerializer. The serialize works OK now but when I deserialize I get the following exception:

    System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. —> NHibernate.LazyInitializationException: Could not initialize proxy – no Session..

    Any ideas? Not Really sure what to do.

    Thanks
    N

  • http://davybrion.com Davy Brion

    i’m not sure i follow what you’re trying to do but i think you just need to serialize/deserialize your nhibernate objects, without going through WCF services, right?

    The [UseNetDataContractSerializer] is a custom attribute which modifies the behavior of a WCF OperationDescription. If you’re not using WCF, simply putting this attribute on your getters isn’t going to make a difference. Unless you modified the code of the attribute to do something completely different.

    If you want to use the NetDataContractSerializer directly to serialize/deserialize your classes, you don’t need to use any attributes on the members of your classes. Just tag the classes themselves with [Serializable] and use the NetDataContractSerializer like you would normally use the XmlSerializer.

    You can find more information on the NetDataContractSerializer here:
    http://msdn.microsoft.com/en-us/library/system.runtime.serialization.netdatacontractserializer.aspx

  • N

    Hi,

    Yes.. I am only trying to serialize and deserialize objects but nHibernate keeps messing things.

    Now i only have [Serializable] attributes on the classes (no DataContract, DataMember, UseNetDataContractSerializer) and I am still getting the following error when I deserialize:

    System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. —> NHibernate.LazyInitializationException: Could not initialize proxy – no Session..

    Also i am not that sure that the object is serialized correctly as when I look at the xml it does not contain a value of a string property the object instance had.

    BTW – when I tried to follow the example from msdn using IExtensibleDataObject I got an error that the nHibernate proxies do not implement IExtensibleDataObject.

    Do you have any idea how I can simply serialize/deserialize without nHibernate messing everything (and with IList deserialized into Ilist and not arrays).

    Many thanks!!

    N

  • http://davybrion.com Davy Brion

    well, if you try to access a proxied object or proxied collection outside of the scope of the session, then it’s normal to get that exception. When you deserialize the object, it’s a different instance than the original one so it’s not connected to the session.

    Are you accessing any proxied objects or proxied collections within your object in code that could be touched during deserialization? Depending on how the NetDataContractSerializer sets the data (through setters for instance), it could trigger code that would access contained proxies which could cause that exception.

  • N

    I don’t touch the proxies…they are accessed during serializtion – it doesn’t get to the deseralization.

    When I use the sample from MSDN with IExtensibleDataObject i cannot serialize and get an error that the nHibernate proxies do not implement IExtensibleDataObject.

    When I use NetDataContractSerializer as is without adding anything (except [Serializable]) then it is serializes but I get the session error when deserialize.

    the only thing that worked was DataContractSerializer but then Ilist was deserialized as arrays.

    So I am quite lost. I don’t know what to do to get it to work….I am actually thinking of using Linq cos I cannot make the nHibernate to work along with serialization…

    Any ideas? The IDataContractSurrogate made DataContractSerializer work… Is there anything simlilar for NetDataContractSerializer?

  • N

    In my previous post – ignore the first line – I meant to say that I am not touching the proxies…

  • http://davybrion.com Davy Brion

    no idea about the IDataContractSurrogate… or IExtensibleDataObject for that matter

    like i said, the only thing i can think of is code in your objects that’s triggered during the deserialization which accesses the proxies. If that’s not it, i can’t help you i guess…

  • N

    I agree.. this code is triggered.. but I just don’t know how to workaround it and also how to workaround the problem with the session (as the nHibernate will alway require valid session..) …

    Thanks anyway :)

  • http://notruf.swissphone.com Andres

    Hello

    I dowloaded the sample and compiled it.

    The domain and nHibernate sections work, but when running the WCF client ( Web or Console ) I get the following Exception

    {“Object of type ‘Iesi.Collections.Generic.HashedSet`1[Northwind.Domain.Entities.Employee]‘ cannot be converted to type ‘Iesi.Collections.Generic.ISet`1[Northwind.Domain.Entities.Order]‘.”}

    Like it is trying to assign an employee object to an Order.

    what can be going wrong ?

  • http://notruf.swissphone.com Andres

    Al litle more detail,

    the exception occurs when calling

    IList customers = proxy.GetCustomersWithTheirOrders();

    I debug insîde the Service and it read the information right. ( 1 Order with 2 Lines )

    The exception type is System.ArgumentException

    The stack Trace is as follows

    “\r\nServer stack trace: \r\n at System.RuntimeType.CheckValue(Object value, Binder binder, CultureInfo culture, BindingFlags invokeAttr)\r\n at System.Reflection.RtFieldInfo.InternalSetValue(Object obj, Object value, BindingFlags invokeAttr, Binder binder, CultureInfo culture, Boolean doVisibilityCheck, Boolean doCheckConsistency)\r\n at System.Reflection.RtFieldInfo.InternalSetValue(Object obj, Object value, BindingFlags invokeAttr, Binder binder, CultureInfo culture, Boolean doVisibilityCheck)\r\n at System.Runtime.Serialization.FormatterServices.SerializationSetValue(MemberInfo fi, Object target, Object value)\r\n at System.Runtime.Serialization.FormatterServices.PopulateObjectMembers(Object obj, MemberInfo[] members, Object[] data)\r\n at Castle.DynamicProxy.Serialization.ProxyObjectReference.RecreateClassProxy(SerializationInfo info, StreamingContext context)\r\n at Castle.DynamicProxy.Serialization.ProxyObjectReference.RecreateProxy(SerializationInfo info, StreamingContext context)\r\n at Castle.DynamicProxy.Serialization.ProxyObjectReference..ctor(SerializationInfo info, StreamingContext context)\r\n at ReadProxyObjectReferenceFromXml(XmlReaderDelegator , XmlObjectSerializerReadContext , XmlDictionaryString[] , XmlDictionaryString[] )\r\n at System.Runtime.Serialization.ClassDataContract.ReadXmlValue(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext context)\r\n at System.Runtime.Serialization.XmlObjectSerializerReadContext.ReadDataContractValue(DataContract dataContract, XmlReaderDelegator reader)\r\n at System.Runtime.Serialization.XmlObjectSerializerReadContextComplex.InternalDeserializeInSharedTypeMode(XmlReaderDelegator xmlReader, Int32 declaredTypeID, Type declaredType, String name, String ns)\r\n at System.Runtime.Serialization.XmlObjectSerializerReadContextComplex.InternalDeserialize(XmlReaderDelegator xmlReader, Int32 declaredTypeID, RuntimeTypeHandle declaredTypeHandle, String name, String ns)\r\n at ReadOrderFromXml(XmlReaderDelegator , XmlObjectSerializerReadContext , XmlDictionaryString[] , XmlDictionaryString[] )\r\n at System.Runtime.Serialization.ClassDataContract.ReadXmlValue(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext context)\r\n at System.Runtime.Serialization.XmlObjectSerializerReadContext.ReadDataContractValue(DataContract dataContract, XmlReaderDelegator reader)\r\n at System.Runtime.Serialization.XmlObjectSerializerReadContextComplex.InternalDeserializeInSharedTypeMode(XmlReaderDelegator xmlReader, Int32 declaredTypeID, Type declaredType, String name, String ns)\r\n at System.Runtime.Serialization.XmlObjectSerializerReadContextComplex.InternalDeserialize(XmlReaderDelegator xmlReader, Int32 declaredTypeID, RuntimeTypeHandle declaredTypeHandle, String name, String ns)\r\n at ReadArrayOfanyTypeFromXml(XmlReaderDelegator , XmlObjectSerializerReadContext , XmlDictionaryString , XmlDictionaryString , CollectionDataContract )\r\n at System.Runtime.Serialization.CollectionDataContract.ReadXmlValue(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext context)\r\n at System.Runtime.Serialization.XmlObjectSerializerReadContext.ReadDataContractValue(DataContract dataContract, XmlReaderDelegator reader)\r\n at System.Runtime.Serialization.XmlObjectSerializerReadContextComplex.InternalDeserializeInSharedTypeMode(XmlReaderDelegator xmlReader, Int32 declaredTypeID, Type declaredType, String name, String ns)\r\n at System.Runtime.Serialization.XmlObjectSerializerReadContextComplex.InternalDeserialize(XmlReaderDelegator xmlReader, Type declaredType, String name, String ns)\r\n at System.Runtime.Serialization.XmlObjectSerializerReadContext.ReadSerializationInfo(XmlReaderDelegator xmlReader, Type type)\r\n at ReadArrayOfKeyValueOfanyTypeanyTypeFromXml(XmlReaderDelegator , XmlObjectSerializerReadContext , XmlDictionaryString[] , XmlDictionaryString[] )\r\n at System.Runtime.Serialization.ClassDataContract.ReadXmlValue(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext context)\r\n at System.Runtime.Serialization.XmlObjectSerializerReadContext.ReadDataContractValue(DataContract dataContract, XmlReaderDelegator reader)\r\n at System.Runtime.Serialization.XmlObjectSerializerReadContextComplex.InternalDeserializeInSharedTypeMode(XmlReaderDelegator xmlReader, Int32 declaredTypeID, Type declaredType, String name, String ns)\r\n at System.Runtime.Serialization.XmlObjectSerializerReadContextComplex.InternalDeserialize(XmlReaderDelegator xmlReader, Int32 declaredTypeID, RuntimeTypeHandle declaredTypeHandle, String name, String ns)\r\n at ReadCollectionEntryFromXml(XmlReaderDelegator , XmlObjectSerializerReadContext , XmlDictionaryString[] , XmlDictionaryString[] )\r\n at System.Runtime.Serialization.ClassDataContract.ReadXmlValue(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext context)\r\n at System.Runtime.Serialization.XmlObjectSerializerReadContext.ReadDataContractValue(DataContract dataContract, XmlReaderDelegator reader)\r\n at System.Runtime.Serialization.XmlObjectSerializerReadContextComplex.InternalDeserializeInSharedTypeMode(XmlReaderDelegator xmlReader, Int32 declaredTypeID, Type declaredType, String name, String ns)\r\n at System.Runtime.Serialization.XmlObjectSerializerReadContextComplex.InternalDeserialize(XmlReaderDelegator xmlReader, Int32 declaredTypeID, RuntimeTypeHandle declaredTypeHandle, String name, String ns)\r\n at ReadPersistentGenericSetOfOrderSwFpc1QfFromXml(XmlReaderDelegator , XmlObjectSerializerReadContext , XmlDictionaryString[] , XmlDictionaryString[] )\r\n at System.Runtime.Serialization.ClassDataContract.ReadXmlValue(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext context)\r\n at System.Runtime.Serialization.XmlObjectSerializerReadContext.ReadDataContractValue(DataContract dataContract, XmlReaderDelegator reader)\r\n at System.Runtime.Serialization.XmlObjectSerializerReadContextComplex.InternalDeserializeInSharedTypeMode(XmlReaderDelegator xmlReader, Int32 declaredTypeID, Type declaredType, String name, String ns)\r\n at System.Runtime.Serialization.XmlObjectSerializerReadContextComplex.InternalDeserialize(XmlReaderDelegator xmlReader, Int32 declaredTypeID, RuntimeTypeHandle declaredTypeHandle, String name, String ns)\r\n at ReadCustomerFromXml(XmlReaderDelegator , XmlObjectSerializerReadContext , XmlDictionaryString[] , XmlDictionaryString[] )\r\n at System.Runtime.Serialization.ClassDataContract.ReadXmlValue(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext context)\r\n at System.Runtime.Serialization.XmlObjectSerializerReadContext.ReadDataContractValue(DataContract dataContract, XmlReaderDelegator reader)\r\n at System.Runtime.Serialization.XmlObjectSerializerReadContextComplex.InternalDeserializeInSharedTypeMode(XmlReaderDelegator xmlReader, Int32 declaredTypeID, Type declaredType, String name, String ns)\r\n at System.Runtime.Serialization.XmlObjectSerializerReadContextComplex.InternalDeserialize(XmlReaderDelegator xmlReader, Int32 declaredTypeID, RuntimeTypeHandle declaredTypeHandle, String name, String ns)\r\n at ReadArrayOfCustomerFromXml(XmlReaderDelegator , XmlObjectSerializerReadContext , XmlDictionaryString , XmlDictionaryString , CollectionDataContract )\r\n at System.Runtime.Serialization.CollectionDataContract.ReadXmlValue(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext context)\r\n at System.Runtime.Serialization.XmlObjectSerializerReadContext.ReadDataContractValue(DataContract dataContract, XmlReaderDelegator reader)\r\n at System.Runtime.Serialization.XmlObjectSerializerReadContextComplex.InternalDeserializeInSharedTypeMode(XmlReaderDelegator xmlReader, Int32 declaredTypeID, Type declaredType, String name, String ns)\r\n at System.Runtime.Serialization.XmlObjectSerializerReadContextComplex.InternalDeserialize(XmlReaderDelegator xmlReader, Int32 declaredTypeID, RuntimeTypeHandle declaredTypeHandle, String name, String ns)\r\n at ReadArrayOfCustomerFromXml(XmlReaderDelegator , XmlObjectSerializerReadContext , XmlDictionaryString[] , XmlDictionaryString[] )\r\n at System.Runtime.Serialization.ClassDataContract.ReadXmlValue(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext context)\r\n at System.Runtime.Serialization.XmlObjectSerializerReadContext.ReadDataContractValue(DataContract dataContract, XmlReaderDelegator reader)\r\n at System.Runtime.Serialization.XmlObjectSerializerReadContextComplex.InternalDeserializeInSharedTypeMode(XmlReaderDelegator xmlReader, Int32 declaredTypeID, Type declaredType, String name, String ns)\r\n at System.Runtime.Serialization.XmlObjectSerializerReadContextComplex.InternalDeserialize(XmlReaderDelegator xmlReader, Type declaredType, String name, String ns)\r\n at System.Runtime.Serialization.NetDataContractSerializer.InternalReadObject(XmlReaderDelegator xmlReader, Boolean verifyObjectName)\r\n at System.Runtime.Serialization.XmlObjectSerializer.ReadObjectHandleExceptions(XmlReaderDelegator reader, Boolean verifyObjectName)\r\n at System.Runtime.Serialization.NetDataContractSerializer.ReadObject(XmlDictionaryReader reader, Boolean verifyObjectName)\r\n at System.ServiceModel.Dispatcher.DataContractSerializerOperationFormatter.DeserializeParameterPart(XmlDictionaryReader reader, PartInfo part, Boolean isRequest)\r\n at System.ServiceModel.Dispatcher.DataContractSerializerOperationFormatter.DeserializeParameter(XmlDictionaryReader reader, PartInfo part, Boolean isRequest)\r\n at System.ServiceModel.Dispatcher.DataContractSerializerOperationFormatter.DeserializeBody(XmlDictionaryReader reader, MessageVersion version, String action, MessageDescription messageDescription, Object[] parameters, Boolean isRequest)\r\n at System.ServiceModel.Dispatcher.OperationFormatter.DeserializeBodyContents(Message message, Object[] parameters, Boolean isRequest)\r\n at System.ServiceModel.Dispatcher.OperationFormatter.DeserializeReply(Message message, Object[] parameters)\r\n at System.ServiceModel.Dispatcher.ProxyOperationRuntime.AfterReply(ProxyRpc& rpc)\r\n at System.ServiceModel.Channels.ServiceChannel.HandleReply(ProxyOperationRuntime operation, ProxyRpc& rpc)\r\n at System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs, TimeSpan timeout)\r\n at System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs)\r\n at System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(IMethodCallMessage methodCall, ProxyOperationRuntime operation)\r\n at System.ServiceModel.Channels.ServiceChannelProxy.Invoke(IMessage message)\r\n\r\nException rethrown at [0]: \r\n at System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg)\r\n at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type)\r\n at ServiceInterface.ICustomerService.GetCustomersWithTheirOrders()\r\n at ServiceClient.Program.Main(String[] args) in C:\\Projects\\NHibernate\\WCF\\NhibernateAndWcf\\ServiceClient\\Program.cs:line 16\r\n at System.AppDomain._nExecuteAssembly(Assembly assembly, String[] args)\r\n at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)\r\n at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()\r\n at System.Threading.ThreadHelper.ThreadStart_Context(Object state)\r\n at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)\r\n at System.Threading.ThreadHelper.ThreadStart()”

  • http://davybrion.com Davy Brion

    Andres,

    i got the same exception sometimes, but i think i only got it when i was hosting the service both through IIS and the Console host… not sure though

    either way, this entire approach of sending detached NHibernate entities over WCF is something i no longer recommend… i prefer to keep my NHibernate entities inside the service layer, and send specific DTO’s over WCF

  • Sam

    Davy > I’m thinking about using DTO’s too because I’m getting totally frustrated with sending my entities over the wire. I have issues with proxy classes and also NH specific types which are not serialized by WCF.

    Anyway I wanted to know how you manage to persist your DTO’s to the database with NH ? I want to do the following :

    Load business model entity with NHibernate
    Convert business model entity to dto (by just keeping the properties I need)
    Client modifies dto and send it back to the server
    Server convert dto back to business model entity
    NH persists business model entity to database.

    But there’s a problem : Since the dto is a smaller version of the business model entity, that means when I re-create my entity on the server side, it’s not going to be a full-feature entity, so how NHibernate is going to figure out what to save ? It’s probably going to interpret that as many properties have changed (when in fact they haven’t) and persists wrong data in the DB.

    You insight would be appreciated.

  • http://davybrion.com Davy Brion

    when you get back into your service layer, just retrieve the object from the database again and modify the properties you need/want to update

  • Sam

    Somebody else gave me the same advice. It seems to be the way to go. It costs a SELECT but I guess it’s not too much of an issue, and NHibernate makes it real easy anyway.

    Thanks a lot !

  • Gaurav

    Hi,

    I am trying to access the WCF service having following interface and implementation:

    [ServiceContract(Namespace="http://SMS.com/OPS")]
    public interface IOrderProcessingService
    {
    [UseNetDataContractSerializer]
    [OperationContract]
    Customer GetAnyCustomer();

    [UseNetDataContractSerializer]
    [OperationContract]
    Customer GetCustomerByName(string customerName);
    }

    public class OrderProcessingService : IOrderProcessingService
    {
    public Customer GetAnyCustomer()
    {
    ICustomerRepository repository = new CustomerRepository();
    return repository.GetByName(“Tom”);
    }

    public Customer GetCustomerByName(string customerName)
    {
    ICustomerRepository repository = new CustomerRepository();
    return repository.GetByName(customerName);

    }
    }

    CustomerRepository class is accessing the database using NHibernate.

    Now the problem is that the method GetAnyCustomer() is working fine. But when the service client is accessing the method GetCustomerByName, the following exception is thrown, indicating that there is some problem in deserializing the given string parameter customerName. The exception details are as follows:

    WorkingWithGraph.Tests.OrderProcessingServiceTest.GetCustomerByNameTest : System.ServiceModel.FaultException : The formatter threw an exception while trying to deserialize the message: There was an error while trying to deserialize parameter http://SMS.com/OPS:customerName. The InnerException message was ‘Error in line 1 position 281. XML ‘Element’ ‘http://SMS.com/OPS:customerName‘ does not contain expected attribute ‘http://schemas.microsoft.com/2003/10/Serialization/:Type'. The deserializer has no knowledge of which type to deserialize. Check that the type being serialized has the same contract as the type being deserialized.’. Please see InnerException for more details.

    And what does it meant by : XML ‘Element’ ‘http://SMS.com/OPS:customerName‘ does not contain expected attribute ‘http://schemas.microsoft.com/2003/10/Serialization/:Type

    I have also tried encapsulating the customerName both in a DataContract and in a MessageContract and used these contracts as parameter types, but not to avail anything.

    Please help me.

    Thanks in advance.
    Gaurav

  • staticage

    Nice! Thanks a lot.

  • Ganesh

    Hello,
    I made silverlight 3 + Silverlight enabled WCF service + NHibenate application.
    I write two contract methods. First contract methods works fine .. but when I call second method it gives me exception that “The remote server returned an error: NotFound.” in referance.cs fil..
    why this happened ..
    pls help me.. I spend a lot time to solve this problem ..

  • Datta Jadhav

    Thanks a lot.

    bcz i was afraid to use nhibernate with wcf before reading this article.
    after reading this article it worked perfect for me.

    now it is so easy to transfer nhibernate entities over wcf wire to client.

    Thank you once again.

    Datta

  • Fahad

    Dear Davy ,

    Thanks for such a great article .I am using visual studio 2010 with oracle and nhibernate 2.0 .The same thing started to happen while I was implementing. Then I found out you’re blog , the problem is that I did everything as you mentioned , step by step.My WCF service calls the methods and gives me back the data , but I don’t know that as soon as the WCF call end and the call comes back to the proxy at my client , it doesn’t returns anything …I tried many things but it didn’t worked .
    At my Client :
    //My Proxy
    CustomerServiceClient cl = new CustomerServiceClient();
    //Opening the proxy
    cl.Open();
    //Calling the Method via WCF service which is decorated by
    // UseNetDataContractSerializer and I didnt used DataContract and
    //DataMemeber for my entity i simply used Serializable attribute.

    object[] cust = cl.GetCustomersWithTheirOrders();

    Now the GetCustomersWithTheirOrders() is working perfectly fine and bringing the collection
    untill it is at the service layer.But when it crosses the service layer and comes to my WebApplication , it is not returning anything , instead it shows empty colection.

    I am not getting why is the data being missed while it is fully populated at the WCF service layer.Is it some kind of deserialization problem ? although i used youre implementation as you had mentioned.

    I am desperately waiting for you’re or anyone’s reply regarding this query.