Software Architecture – Design Problem – MS Visual Studio 2010 + .Net 4.0 + WCF + NHibernate + Silverlight

Environment: MS Visual Studio 2010 + .Net 4.0 + WCF + NHibernate + Silverlight

This post assumes that you have initial knowledge of WCF, NHibernate, Silverlight.

See the sample code for this solution working here.
Rename file as up.zip for extracting.

Sample Project:

Entities Relation / Tables relation is like:

Region >> Country >> City >> Area

So each region has many countries, Each country has many cities. Each City has many regions.
A typical Silverlight solution is created with 2 projects (One for silverlight(Client side project), other for ASP .Net Web project (server side ) having a WCF Service for data transfer from server side to client side.

Problem Definition:

Lets suppose you want to get list of City with lazy=”false” and fetch=”join”
for all entities So we will get following queries in SQL Profiler:

SELECT this_.CityID as CityID3_2_, this_.CountryID as CountryID3_2_, this_.CityCode as CityCode3_2_, this_.Description as Descript4_3_2_, this_.Active as Active3_2_, country2_.CountryID as CountryID1_0_, country2_.CountryCode as CountryC2_1_0_, country2_.Description as Descript3_1_0_, country2_.Active as Active1_0_, country2_.RegionId as RegionId1_0_, regions3_.RegionID as RegionID0_1_, regions3_.RegionCode as RegionCode0_1_, regions3_.Description as Descript3_0_1_, regions3_.Active as Active0_1_ FROM GeneralCityMaster this_ left outer join GeneralCountryMaster country2_ on this_.CountryID=country2_.CountryID left outer join GeneralRegionMaster regions3_ on country2_.RegionId=regions3_.RegionID

Again get list of City with lazy=”false” and fetch=”select”
for all entities So we will get following queries in SQL Profiler:

SELECT this_.CityID as CityID3_0_, this_.CountryID as CountryID3_0_, this_.CityCode as CityCode3_0_, this_.Description as Descript4_3_0_, this_.Active as Active3_0_ FROM GeneralCityMaster this_

For each country in list, there will be an select from country table as:

exec sp_executesql N'SELECT country0_.CountryID as CountryID1_0_, country0_.CountryCode as CountryC2_1_0_, country0_.Description as Descript3_1_0_, country0_.Active as Active1_0_, country0_.RegionId as RegionId1_0_ FROM GeneralCountryMaster country0_ WHERE country0_.CountryID=@p0',N'@p0 int',@p0=1

For each region in list, there will be an select from region table as:

exec sp_executesql N'SELECT regions0_.RegionID as RegionID0_0_, regions0_.RegionCode as RegionCode0_0_, regions0_.Description as Descript3_0_0_, regions0_.Active as Active0_0_ FROM GeneralRegionMaster regions0_ WHERE regions0_.RegionID=@p0',N'@p0 int',@p0=21

Two Problems:
There are 2 problems with above implementation.

Lazy load is false, so on server side that is ASP .Net / WCF Project, if we need some processing to be done, it is loading all data, no lazy initialization helps us.

Secondly, in both above cases data is joined from all the corresponding tables even if it is not required. Suppose we want to get data of cities only but unfortunately data of country and region is also fetched and transfer of such a heavy data across the WCF wire is very heavy activity.

If you will change lazy=”true” for entities, then WCF will pass following error.

{System.Net.WebException: The remote server returned an error: NotFound.
   at System.Net.Browser.BrowserHttpWebRequest.InternalEndGetResponse(IAsyncResult asyncResult)
   at System.Net.Browser.BrowserHttpWebRequest.<>c__DisplayClass5.<EndGetResponse>b__4(Object sendState)
   at System.Net.Browser.AsyncHelper.<>c__DisplayClass2.<BeginOnUI>b__0(Object sendState)}

In order to see the real problem, configure WCF Log as explained
as explained at here

On looking into



Exeption will look like as:

There was an error while trying to serialize parameter http://tempuri.org/:GetCitiesResult. The InnerException message was 'Type 'CountryProxy' with data contract name 'CountryProxy:http://schemas.datacontract.org/2004/07/' is not expected. Consider using a DataContractResolver or add any types not known statically to the list of known types - for example, by using the KnownTypeAttribute attribute or by adding them to the list of known types passed to DataContractSerializer.'.  Please see InnerException for more details.

Stack trace will look like as:

System.ServiceModel.Dispatcher.DataContractSerializerOperationFormatter.SerializeParameterPart(XmlDictionaryWriter writer, PartInfo part, Object graph)
System.ServiceModel.Dispatcher.DataContractSerializerOperationFormatter.SerializeParameter(XmlDictionaryWriter writer, PartInfo part, Object graph)
System.ServiceModel.Dispatcher.DataContractSerializerOperationFormatter.SerializeBody(XmlDictionaryWriter writer, MessageVersion version, String action, MessageDescription messageDescription, Object returnValue, Object[] parameters, Boolean isRequest)
System.ServiceModel.Dispatcher.OperationFormatter.SerializeBodyContents(XmlDictionaryWriter writer, MessageVersion version, Object[] parameters, Object returnValue, Boolean isRequest)
System.ServiceModel.Dispatcher.OperationFormatter.OperationFormatterMessage.OperationFormatterBodyWriter.OnWriteBodyContents(XmlDictionaryWriter writer)
System.ServiceModel.Channels.BodyWriter.WriteBodyContents(XmlDictionaryWriter writer)
System.ServiceModel.Channels.BodyWriterMessage.OnBodyToString(XmlDictionaryWriter writer)
System.ServiceModel.Channels.Message.ToString(XmlDictionaryWriter writer)
System.ServiceModel.Diagnostics.MessageLogTraceRecord.WriteTo(XmlWriter writer)
System.ServiceModel.Diagnostics.MessageLogger.LogInternal(MessageLogTraceRecord record)
System.ServiceModel.Diagnostics.MessageLogger.LogMessageImpl(Message&amp; message, XmlReader reader, MessageLoggingSource source)
System.ServiceModel.Diagnostics.MessageLogger.LogMessage(Message&amp; message, XmlReader reader, MessageLoggingSource source)
System.ServiceModel.Dispatcher.DispatchOperationRuntime.SerializeOutputs(MessageRpc&amp; rpc)
System.ServiceModel.Dispatcher.DispatchOperationRuntime.InvokeBegin(MessageRpc&amp; rpc)
System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage5(MessageRpc&amp; rpc)
System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage41(MessageRpc&amp; rpc)
System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage4(MessageRpc&amp; rpc)
System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage31(MessageRpc&amp; rpc)
System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage3(MessageRpc&amp; rpc)
System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage2(MessageRpc&amp; rpc)
System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage11(MessageRpc&amp; rpc)
System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage1(MessageRpc&amp; rpc)
System.ServiceModel.Dispatcher.MessageRpc.Process(Boolean isOperationContextSet)
System.ServiceModel.Dispatcher.ChannelHandler.DispatchAndReleasePump(RequestContext request, Boolean cleanThread, OperationContext currentOperationContext)
System.ServiceModel.Dispatcher.ChannelHandler.HandleRequest(RequestContext request, OperationContext currentOperationContext)
System.ServiceModel.Dispatcher.ChannelHandler.AsyncMessagePump(IAsyncResult result)
System.ServiceModel.Dispatcher.ChannelHandler.OnAsyncReceiveComplete(IAsyncResult result)
System.Runtime.Fx.AsyncThunk.UnhandledExceptionFrame(IAsyncResult result)
System.Runtime.AsyncResult.Complete(Boolean completedSynchronously)
System.Runtime.InputQueue`1.AsyncQueueReader.Set(Item item)
System.Runtime.InputQueue`1.EnqueueAndDispatch(Item item, Boolean canDispatchOnThisThread)
System.Runtime.InputQueue`1.EnqueueAndDispatch(T item, Action dequeuedCallback, Boolean canDispatchOnThisThread)
System.ServiceModel.Channels.SingletonChannelAcceptor`3.Enqueue(QueueItemType item, Action dequeuedCallback, Boolean canDispatchOnThisThread)
System.ServiceModel.Channels.HttpChannelListener.HttpContextReceived(HttpRequestContext context, Action callback)
System.ServiceModel.Activation.HostedHttpTransportManager.HttpContextReceived(HostedHttpRequestAsyncResult result)
System.ServiceModel.Activation.HostedHttpRequestAsyncResult.HandleRequest()
System.ServiceModel.Activation.HostedHttpRequestAsyncResult.BeginRequest()
System.ServiceModel.Activation.HostedHttpRequestAsyncResult.OnBeginRequest(Object state)
System.Runtime.IOThreadScheduler.ScheduledOverlapped.IOCallback(UInt32 errorCode, UInt32 numBytes, NativeOverlapped* nativeOverlapped)
System.Runtime.Fx.IOCompletionThunk.UnhandledExceptionFrame(UInt32 error, UInt32 bytesRead, NativeOverlapped* nativeOverlapped)
System.Threading._IOCompletionCallback.PerformIOCompletionCallback(UInt32 errorCode, UInt32 numBytes, NativeOverlapped* pOVERLAP)

This exception reflects that when WCF serializer tries to load countries, it fails because of lazy loading being true as country will be a proxy object, so it will pass the exception.

Solution:
After reviewing the suggestions provided by Sowmy Srinivasan’s Blog at:
http://blogs.msdn.com/b/sowmy/archive/2006/03/26/561188.aspx

and discussion on Johan Danforth’s Blog at:
http://weblogs.asp.net/jdanforth/archive/2008/12/22/nhibernate-and-wcf-is-not-a-perfect-match.aspx

I have introduced 3 new classes as:

1. HibernateDataContractSurrogate which implements IDataContractSurrogate which replaces proxy object with forcefully created object by the DataContractSerializer of WCF during serialization.

MSDN Reference:
http://msdn.microsoft.com/en-us/library/ms574969.aspx

2. ReferencePreservingDataContractSerializerOperationBehavior
Which is derived from DataContractSerializerOperationBehavior will extend the functionality provided by DataContract behavior.

3. ReferencePreservingDataContractFormatAttribute which provide attribute that will apply the behavior created above with name
ReferencePreservingDataContractSerializerOperationBehavior

So in short we have modified the behavior of data contract by using IDataContractSurrogate to replace proxy object with forcefully created object.

After this above change of introducing 3 classes, old exception of countryproxy will disappear, but a new exception will be there as below.

There was an error while trying to serialize parameter http://tempuri.org/:GetCitiesResult. The InnerException message was 'Type 'WCFNHibernateSilverLightSample.Web.City' with data contract name 'City:http://schemas.datacontract.org/2004/07/WCFNHibernateSilverLightSample.Web' is not expected. Consider using a DataContractResolver or add any types not known statically to the list of known types - for example, by using the KnownTypeAttribute attribute or by adding them to the list of known types passed to DataContractSerializer.'.  Please see InnerException for more details.

Trace as:

System.ServiceModel.Dispatcher.DataContractSerializerOperationFormatter.SerializeParameterPart(XmlDictionaryWriter writer, PartInfo part, Object graph)
System.ServiceModel.Dispatcher.DataContractSerializerOperationFormatter.SerializeParameter(XmlDictionaryWriter writer, PartInfo part, Object graph)
System.ServiceModel.Dispatcher.DataContractSerializerOperationFormatter.SerializeBody(XmlDictionaryWriter writer, MessageVersion version, String action, MessageDescription messageDescription, Object returnValue, Object[] parameters, Boolean isRequest)
System.ServiceModel.Dispatcher.OperationFormatter.SerializeBodyContents(XmlDictionaryWriter writer, MessageVersion version, Object[] parameters, Object returnValue, Boolean isRequest)
System.ServiceModel.Dispatcher.OperationFormatter.OperationFormatterMessage.OperationFormatterBodyWriter.OnWriteBodyContents(XmlDictionaryWriter writer)
System.ServiceModel.Channels.BodyWriter.WriteBodyContents(XmlDictionaryWriter writer)
System.ServiceModel.Channels.BodyWriterMessage.OnBodyToString(XmlDictionaryWriter writer)
System.ServiceModel.Channels.Message.ToString(XmlDictionaryWriter writer)
System.ServiceModel.Diagnostics.MessageLogTraceRecord.WriteTo(XmlWriter writer)
System.ServiceModel.Diagnostics.MessageLogger.LogInternal(MessageLogTraceRecord record)
System.ServiceModel.Diagnostics.MessageLogger.LogMessageImpl(Message&amp; message, XmlReader reader, MessageLoggingSource source)
System.ServiceModel.Diagnostics.MessageLogger.LogMessage(Message&amp; message, XmlReader reader, MessageLoggingSource source)
System.ServiceModel.Dispatcher.DispatchOperationRuntime.SerializeOutputs(MessageRpc&amp; rpc)
System.ServiceModel.Dispatcher.DispatchOperationRuntime.InvokeBegin(MessageRpc&amp; rpc)
System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage5(MessageRpc&amp; rpc)
System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage41(MessageRpc&amp; rpc)
System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage4(MessageRpc&amp; rpc)
System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage31(MessageRpc&amp; rpc)
System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage3(MessageRpc&amp; rpc)
System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage2(MessageRpc&amp; rpc)
System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage11(MessageRpc&amp; rpc)
System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage1(MessageRpc&amp; rpc)
System.ServiceModel.Dispatcher.MessageRpc.Process(Boolean isOperationContextSet)
System.ServiceModel.Dispatcher.ChannelHandler.DispatchAndReleasePump(RequestContext request, Boolean cleanThread, OperationContext currentOperationContext)
System.ServiceModel.Dispatcher.ChannelHandler.HandleRequest(RequestContext request, OperationContext currentOperationContext)
System.ServiceModel.Dispatcher.ChannelHandler.AsyncMessagePump(IAsyncResult result)
System.ServiceModel.Dispatcher.ChannelHandler.OnAsyncReceiveComplete(IAsyncResult result)
System.Runtime.Fx.AsyncThunk.UnhandledExceptionFrame(IAsyncResult result)
System.Runtime.AsyncResult.Complete(Boolean completedSynchronously)
System.Runtime.InputQueue`1.AsyncQueueReader.Set(Item item)
System.Runtime.InputQueue`1.EnqueueAndDispatch(Item item, Boolean canDispatchOnThisThread)
System.Runtime.InputQueue`1.EnqueueAndDispatch(T item, Action dequeuedCallback, Boolean canDispatchOnThisThread)
System.ServiceModel.Channels.SingletonChannelAcceptor`3.Enqueue(QueueItemType item, Action dequeuedCallback, Boolean canDispatchOnThisThread)
System.ServiceModel.Channels.HttpChannelListener.HttpContextReceived(HttpRequestContext context, Action callback)
System.ServiceModel.Activation.HostedHttpTransportManager.HttpContextReceived(HostedHttpRequestAsyncResult result)
System.ServiceModel.Activation.HostedHttpRequestAsyncResult.HandleRequest()
System.ServiceModel.Activation.HostedHttpRequestAsyncResult.BeginRequest()
System.ServiceModel.Activation.HostedHttpRequestAsyncResult.OnBeginRequest(Object state)
System.Runtime.IOThreadScheduler.ScheduledOverlapped.IOCallback(UInt32 errorCode, UInt32 numBytes, NativeOverlapped* nativeOverlapped)
System.Runtime.Fx.IOCompletionThunk.UnhandledExceptionFrame(UInt32 error, UInt32 bytesRead, NativeOverlapped* nativeOverlapped)
System.Threading._IOCompletionCallback.PerformIOCompletionCallback(UInt32 errorCode, UInt32 numBytes, NativeOverlapped* pOVERLAP)

Solution for the above exception is provided at:
http://msdn.microsoft.com/en-us/library/system.servicemodel.serviceknowntypeattribute.aspx

Introduce ServiceKnownType attribute in service contract with a helper class,
So the Service will look like as:

// NOTE: You can use the "Rename" command on the "Refactor" menu to change the interface name "IGetDataService" in both code and config file together.
    [ServiceContract]
    [ServiceKnownType("GetKnownTypes", typeof(Helper))]
    public interface IGetDataService
    {

        [OperationContract]        
        [ReferencePreservingDataContractFormat]
        IList<City> GetCities();
    }



// This class has the method named GetKnownTypes that returns a generic IEnumerable.
    static class Helper
    {
        public static IEnumerable<Type> GetKnownTypes(ICustomAttributeProvider provider)
        {
            System.Collections.Generic.List<System.Type> knownTypes =
                new System.Collections.Generic.List<System.Type>();
            // Add any types to include here.
            knownTypes.Add(typeof(City));
            knownTypes.Add(typeof(Country));
            knownTypes.Add(typeof(Regions));
            knownTypes.Add(typeof(AreaMaster));
            
            return knownTypes;
        }
    }

So now the first problem has been solved. So now in business classes we can use objects with lazy loading working, only required object will be initialized. We have lazy loading true in OR Mapping, so on Server side “WCFNHibernateSilverLightSample.Web” project will access the object model with fully operational benefits of lazy loading.

See the sample code for this solution working here.

But second problem still exists. To solve the second problem, DTO (Data Transfer Objects) are used. DTO object are simple entity objects have the only those properties that needs to be transferred via WCF wire, So OR Mapping Nhibernate object will be different from DTO objects. Once the data is fetched in OR Mapping Nhibernate object, a transformation is done to DTO object and light weight DTO objects are passed with WCF to client end silverlight project.

I will do one more post to demonstrate this second problem in next post.

Both problems must be solved in a Good designed WCF/Sliverlight/Nhibernate project.

Happy Coding.

Advertisements

2 Responses to Software Architecture – Design Problem – MS Visual Studio 2010 + .Net 4.0 + WCF + NHibernate + Silverlight

  1. […] Software Architecture – Design Problem Part 2 – DTO- MS Visual Studio 2010 – Net 4-0 – WCF – NHibernate – Silverlight This is second post with reference to Design Architecture discussion on Project based on NHibernate + WCF+Sliverlight. See the first Post last post here. […]

  2. Asking questions are actually nice thing if you are not understanding
    something entirely, except this paragraph provides nice understanding yet.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: