Using EntityBag with EntityFramework over WCF

I was going through the article published in MSDN by Daniel Simmons about the N-Tier Patterns https://msdn.microsoft.com/en-us/magazine/dd882522.aspx and came across the EntityBag implementation. So I went through couple of posts by Daniel on the EntityBag stuff to acquire the working knowledge and get started with EntityBag.  The project is hosted in codeplex

There were a couple of issues that I faced in getting it to run especially with changes in WCF and EF 3.5 sp1. So I thought of blogging about this to help get this working without much hassles.

Changes Needed In EntityBag

The first issue that you will face while compiling EntityBag project(Perseus) is the following error

  No overload for method 'GetEntityKey' takes '2' arguments C:\Users\bindeshv\Code\perseus-1.1 entitybag\Perseus\UtilityExtensionMethods.cs     

The above error happens because ObjectContext.GetEntityKey(entitySetName, object) was a beta 3 method which was removed. Though there is an extension method inside UtilityExtensionMethod.cs  for IRleatedEnd like this

      public static EntityKey GetEntityKey(thisIRelatedEnd relatedEnd)
{
Debug.Assert(relatedEnd.IsEntityReference());
TyperelationshipType = relatedEnd.GetType();
PropertyInfo pi = relationshipType.GetProperty("EntityKey");
return(EntityKey)pi.GetValue(relatedEnd, null);
}

But the actual code calling this method is on ObjectContext in our CreateOriginalValuesObject method like this

       EntityKey targetKey = context.GetEntityKey(fullEntitySetName, target);

Since the intent here was to get the entity key for the target object and then get its ObjectStateEntry and then clone the original entries in the source to target, I changed the code to like this 

 

           // EntityKey targetKey = context.GetEntityKey(target);
            ObjectStateEntry targetStateEntry = context.ObjectStateManager.GetObjectStateEntry(target);

Changes In WCF Service

To test the program I created the following service contract and tried exposing it over WsHttpbinding

    [ServiceContract]
   public interface INorthwind
  {
      [OperationContract]
       EntityBag<NorthwindModel.Customers> GetCustomerByID(string customerID);

      [OperationContract]
      void UpdateCustomer(EntityBag<NorthwindModel.Customers> customers);

      [OperationContract] //for test
      int Test(int p);


  }

But each time I tried getting this over to the client I will get a weird error on the client (took 2 days to figure this out)

An error occurred while receiving the HTTP response to https://binvij-admin2.fareast.corp.microsoft.com/NorthwindSVC/Northwind.svc. This could be due to the service endpoint binding not using the HTTP protocol. This could also be due to an HTTP request context being aborted by the server (possibly due to the service shutting down). See server logs for more details.

Server stack trace:

   at System.ServiceModel.Channels.HttpChannelUtilities.ProcessGetResponseWebException(WebException webException, HttpWebRequest request, HttpAbortReason abortReason)

   at System.ServiceModel.Channels.HttpChannelFactory.HttpRequestChannel.HttpChannelRequest.WaitForReply(TimeSpan timeout)

   at System.ServiceModel.Channels.RequestChannel.Request(Message message, TimeSpan timeout)

   at System.ServiceModel.Channels.ClientReliableChannelBinder`1.RequestClientReliableChannelBinder`1.OnRequest(TRequestChannel channel, Message message, TimeSpan timeout, MaskingMode maskingMode)

   at System.ServiceModel.Channels.ClientReliableChannelBinder`1.Request(Message message, TimeSpan timeout, MaskingMode maskingMode)

   at System.ServiceModel.Channels.ClientReliableChannelBinder`1.Request(Message message, TimeSpan timeout)

   at System.ServiceModel.Security.SecuritySessionClientSettings`1.SecurityRequestSessionChannel.Request(Message message, TimeSpan timeout)

   at System.ServiceModel.Dispatcher.RequestChannelBinder.Request(Message message, TimeSpan timeout)

   at System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs, TimeSpan timeout)

   at System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(IMethodCallMessage methodCall, ProxyOperationRuntime operation)

   at System.ServiceModel.Channels.ServiceChannelProxy.Invoke(IMessage message)

I implemented a WCF trace, more details here on enabling the trace , and saw the following logged in

<ExceptionType>System.ServiceModel.CommunicationException, System.ServiceModel, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</ExceptionType>

<Message>There was an error while trying to serialize parameter https://tempuri.org/:GetCustomerByIDResult. The InnerException message was 'Type 'NorthwindModel.Customers' with data contract name 'Customers:https://schemas.datacontract.org/2004/07/NorthwindModel' is not expected. 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.</ Message>

<StackTrace>at System.ServiceModel.Dispatcher.DataContractSerializerOperationFormatter.SerializeParameterPart(XmlDictionaryWriter writer, PartInfo part, Object graph) at System.ServiceModel.Dispatcher.DataContractSerializerOperationFormatter.SerializeParameter(XmlDictionaryWriter writer, PartInfo part, Object graph) at System.ServiceModel.Dispatcher.DataContractSerializerOperationFormatter.SerializeBody(XmlDictionaryWriter writer, MessageVersion version, String action, MessageDescription messageDescription, Object returnValue, Object[] parameters, Boolean isRequest) at System.ServiceModel.Dispatcher.OperationFormatter.SerializeBodyContents(XmlDictionaryWriter writer, MessageVersion version, Object[] parameters, Object returnValue, Boolean isRequest) at

 

The DataContractSerializer did not know about my Northwind.Customers type so I had to append my ServiceContract with

  [ServiceContract]
  [ServiceKnownType(typeof(NorthwindModel.Customers))] 
 
  public interface INorthwind
  {
      [OperationContract]
     
      EntityBag<NorthwindModel.Customers> GetCustomerByID(string customerID);
 Note: This is missing in the client test project for EntityBag in the  demo
 After this I got an error at the client end, though I could see the WCF service sending the EntityBag<Customers> successfully. Here is the error
 message 

The formatter threw an exception while trying to deserialize the message: There was an error while trying to deserialize parameter https://tempuri.org/:GetCustomerByIDResult. The InnerException message was 'Error in line 1 position 1808. Element 'https://schemas.microsoft.com/2003/10/Serialization/Arrays:anyType' contains data of the 'https://schemas.datacontract.org/2004/07/NorthwindModel:Customers' data contract. The deserializer has no knowledge of any type that maps to this contract. Add the type corresponding to 'Customers' to the list of known types - for example, by using the KnownTypeAttribute attribute or by adding it to the list of known types passed to DataContractSerializer.'. Please see InnerException for more details.

Server stack trace:

   at System.ServiceModel.Dispatcher.DataContractSerializerOperationFormatter.DeserializeParameterPart(XmlDictionaryReader reader, PartInfo part, Boolean isRequest)

   at System.ServiceModel.Dispatcher.DataContractSerializerOperationFormatter.DeserializeBody(XmlDictionaryReader reader, MessageVersion version, String action, MessageDescription messageDescription, Object[] parameters, Boolean isRequest)

   at System.ServiceModel.Dispatcher.OperationFormatter.DeserializeBodyContents(Message message, Object[] parameters, Boolean isRequest)

   at System.ServiceModel.Dispatcher.OperationFormatter.DeserializeReply(Message message, Object[] parameters)

   at System.ServiceModel.Dispatcher.ProxyOperationRuntime.AfterReply(ProxyRpc& rpc)

   at System.ServiceModel.Channels.ServiceChannel.HandleReply(ProxyOperationRuntime operation, ProxyRpc& rpc)

   at System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs, TimeSpan timeout)

   at System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(IMethodCallMessage methodCall, ProxyOperationRuntime operation)

   at System.ServiceModel.Channels.ServiceChannelProxy.Invoke(IMessage message)

 

We get this error due to an inappropriate setting of the buffer size for the binding in use. Check this link for more details
https://social.msdn.microsoft.com/Forums/en-US/wcf/thread/e0bac3a6-f758-499b-9cab-32435b9887ea

The fix is to increase the messagesize for both the client and the service in the config file

          <wsHttpBinding>
         <binding name="AllowLargeMessages" maxReceivedMessageSize="512000" maxBufferPoolSize="512000" >
           <readerQuotas maxArrayLength="5000000" />
         </binding>
        </wsHttpBinding>

 

And this should help getting your EntityBag serialized and Deserialized.

Hope this helps !