UnitOfWork2 WCF Serialization

Posts   
 
    
saggett
User
Posts: 50
Joined: 12-Nov-2007
# Posted on: 12-Nov-2007 17:22:55   

When using UnitOfWork2 as a parameter of a method on a WCF Service (having added it as a known type by attributing the Service Contract), I receive the following error:

System.ServiceModel.CommunicationException occurred Message="There was an error while trying to serialize parameter http://tempuri.org/:unitOfWork. The InnerException message was 'Type 'System.Collections.Generic.List1[[SD.LLBLGen.Pro.ORMSupportClasses.UnitOfWorkCallBackElement2, SD.LLBLGen.Pro.ORMSupportClasses.NET20, Version=2.5.0.0, Culture=neutral, PublicKeyToken=ca73b74ba4e3ff27]]' with data contract name 'ArrayOfUnitOfWorkCallBackElement2:http://schemas.datacontract.org/2004/07/SD.LLBLGen.Pro.ORMSupportClasses' 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." Source="mscorlib" StackTrace: Server stack trace: 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.SerializeParameters(XmlDictionaryWriter writer, PartInfo[] parts, Object[] parameters) 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 System.ServiceModel.Dispatcher.OperationFormatter.OperationFormatterMessage. OperationFormatterBodyWriter.OnWriteBodyContents(XmlDictionaryWriter writer) at System.ServiceModel.Channels.BodyWriter.WriteBodyContents(XmlDictionaryWriter writer) at System.ServiceModel.Channels.BodyWriterMessage.OnWriteBodyContents(XmlDictionaryWriter writer) at System.ServiceModel.Channels.Message.WriteBodyContents(XmlDictionaryWriter writer) at System.ServiceModel.Security.SecurityAppliedMessage.WriteBodyToSignThenEncryptWithFragments(Stream stream, Boolean includeComments, String[] inclusivePrefixes, EncryptedData encryptedData, SymmetricAlgorithm algorithm, XmlDictionaryWriter writer) at System.ServiceModel.Security.WSSecurityOneDotZeroSendSecurityHeader.ApplyBodySecurity(XmlDictionaryWriter writer, IPrefixGenerator prefixGenerator) at System.ServiceModel.Security.SecurityAppliedMessage.OnWriteMessage(XmlDictionaryWriter writer) at System.ServiceModel.Channels.Message.WriteMessage(XmlDictionaryWriter writer) at System.ServiceModel.Channels.BufferedMessageWriter.WriteMessage(Message message, BufferManager bufferManager, Int32 initialOffset, Int32 maxSizeQuota) at System.ServiceModel.Channels.TextMessageEncoderFactory.TextMessageEncoder.WriteMessage(Message message, Int32 maxMessageSize, BufferManager bufferManager, Int32 messageOffset) at System.ServiceModel.Channels.MessageEncoder.WriteMessage(Message message, Int32 maxMessageSize, BufferManager bufferManager) at System.ServiceModel.Channels.HttpOutput.SerializeBufferedMessage(Message message) at System.ServiceModel.Channels.HttpOutput.Send(TimeSpan timeout) at System.ServiceModel.Channels.HttpChannelFactory.HttpRequestChannel.HttpChannelRequest.SendRequest(Message message, TimeSpan timeout) at System.ServiceModel.Channels.RequestChannel.Request(Message message, TimeSpan timeout) at System.ServiceModel.Channels.ClientReliableChannelBinder1.RequestClientReliableChannelBinder1. OnRequest(TRequestChannel channel, Message message, TimeSpan timeout, MaskingMode maskingMode) at System.ServiceModel.Channels.ClientReliableChannelBinder1.Request(Message message, TimeSpan timeout, MaskingMode maskingMode) at System.ServiceModel.Channels.ClientReliableChannelBinder1.Request(Message message, TimeSpan timeout) at System.ServiceModel.Security.SecuritySessionClientSettings1.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.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs) at System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(IMethodCallMessage methodCall, ProxyOperationRuntime operation) at System.ServiceModel.Channels.ServiceChannelProxy.Invoke(IMessage message) Exception rethrown at [0]: at System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg) at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type) at UnivarQ.ServiceContracts.IUnivarQWork.CommitEntityUnitOfWork(UnitOfWork2 unitOfWork, EntityType rootEntityType) at UnivarQ.ClientUILib.ServiceProxy.ClientUnivarQWork.CommitUnitOfWork(UnitOfWork2 unitOfWork, EntityType rootEntityType) in C:\Projects\LatestU\UI\ClientUILib\ServiceProxy\ClientUnivarQWork.cs:line 115

The Service Contract Interface already includes the following attributes:

[ServiceKnownType(typeof(UnitOfWorkElement2))]
[ServiceKnownType(typeof(UnitOfWorkCollectionElement2))]
[ServiceKnownType(typeof(List<UnitOfWorkElement2>))]
[ServiceKnownType(typeof(List<UnitOfWorkCollectionElement2>))]
[ServiceKnownType(typeof(Dictionary<System.Guid, UnitOfWorkElement2>))]
[ServiceKnownType(typeof(Dictionary<System.Guid, UnitOfWorkCollectionElement2>))]

which were all required for the DataContractSerializer to serialize a UnitOfWork2 object. However, when attempting to add the attribute: [ServiceKnownType(typeof(List<SD.LLBLGen.Pro.ORMSupportClasses.UnitOfWorkCallBackElement2>))]

I receive the compiler error 'SD.LLBLGen.Pro.ORMSupportClasses.UnitOfWorkCallBackElement2' is inaccessible due to its protection level'.

Is there any resolution for my problem, other than not using UnitOfWork2 as a parameter in a WCF service? I assumed that the UnitOfWork2 class would be serializable by the DataContractSerializer, given that the documentation states that the class is xml serializable.

goose avatar
goose
User
Posts: 392
Joined: 06-Aug-2007
# Posted on: 12-Nov-2007 18:01:59   

Hi, this thread contains a similar question, let me know if you need further help.

http://llblgen.com/TinyForum/Messages.aspx?ThreadID=11493

saggett
User
Posts: 50
Joined: 12-Nov-2007
# Posted on: 12-Nov-2007 18:05:01   

I read that thread, but there didn't seem to be a fix for the problem. Should I take it then that the UnitOfWork2 cannot be serialized, contrary to what it says in the documentation, and that there are no plans to change this?

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39588
Joined: 17-Aug-2003
# Posted on: 12-Nov-2007 18:27:17   

Serialization isn't always the same: UnitOfWork2 is serializable over remoting, but not over XML. WCF uses XML if you're not using remoting over WCF. So if you want to send UnitOfWork2 objects, you either have to use remoting, or use messages instead of sending a unitofwork2.

We're thinking about making UnitOfWork2 objects XML serializable (that's something else than remoting serializable wink ) in a future version, as it shouldn't be that much work (the beef is already done as entitycollections and entities are already serializable over XML)

Frans Bouma | Lead developer LLBLGen Pro
saggett
User
Posts: 50
Joined: 12-Nov-2007
# Posted on: 13-Nov-2007 11:30:35   

Would utilizing NetDataContractSerializer rather than DataContractSerializer do the trick, given that NetDataContractSerializer seems to be Microsoft's WCF version of Remoting?

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39588
Joined: 17-Aug-2003
# Posted on: 13-Nov-2007 18:28:55   

saggett wrote:

Would utilizing NetDataContractSerializer rather than DataContractSerializer do the trick, given that NetDataContractSerializer seems to be Microsoft's WCF version of Remoting?

I don't think that will give remoting, as it still serializes to XML. Remoting under WCF is IMHO not really 'there', WCF is pure XML, remoting over WCF therefore isn't possible, you simply have to use remoting using the normal proven way to setup remoting. The WCF + remoting docs suggest either migration to WCF (Which imho implies XML) or side-by-side.

Frans Bouma | Lead developer LLBLGen Pro
mihies avatar
mihies
User
Posts: 800
Joined: 29-Jan-2006
# Posted on: 21-Dec-2008 18:22:17   

Otis wrote:

Serialization isn't always the same: UnitOfWork2 is serializable over remoting, but not over XML. WCF uses XML if you're not using remoting over WCF. So if you want to send UnitOfWork2 objects, you either have to use remoting, or use messages instead of sending a unitofwork2.

We're thinking about making UnitOfWork2 objects XML serializable (that's something else than remoting serializable wink ) in a future version, as it shouldn't be that much work (the beef is already done as entitycollections and entities are already serializable over XML)

Was it ever implemented?

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39588
Joined: 17-Aug-2003
# Posted on: 21-Dec-2008 18:57:35   

No unfortunately not, as we ran into a problem with deserializing predicates and relationships in update with filter saves: it is required to deserialize fields in that case, however the UoW has no access to any factory when doing so.

We'll implemented in v3 even if it requires changes to the system. Also planned is a way to diff the elements to serialize so the data is really compact.

Frans Bouma | Lead developer LLBLGen Pro
mihies avatar
mihies
User
Posts: 800
Joined: 29-Jan-2006
# Posted on: 21-Dec-2008 19:49:43   

Otis wrote:

No unfortunately not, as we ran into a problem with deserializing predicates and relationships in update with filter saves: it is required to deserialize fields in that case, however the UoW has no access to any factory when doing so.

We'll implemented in v3 even if it requires changes to the system. Also planned is a way to diff the elements to serialize so the data is really compact.

Interesting. Right now I am thinking about how to efficiently transfer graphs forth and back (main transport is WCF). One way would be to use DTO. When fetching I'd create (fetch from database) entity graph on server side, project it to DTO graph (it shouldn't be difficult) that'd travel over the wire. The DTO graphs should be used to recreate entity graph on client side.

When saving, I'd collect all dirty entities from that graph and create DTO objects from them (perhaps by using UoW - I'd have the right order of operation). I'd send DTO over the wire, on server create all entities in correct order and save them in a transaction.

But if you are going to make the save step "out of the box" then the DTOs aren't really required anymore since the fetch scenario doesn't really require them.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39588
Joined: 17-Aug-2003
# Posted on: 23-Dec-2008 11:19:09   

mihies wrote:

Otis wrote:

No unfortunately not, as we ran into a problem with deserializing predicates and relationships in update with filter saves: it is required to deserialize fields in that case, however the UoW has no access to any factory when doing so.

We'll implemented in v3 even if it requires changes to the system. Also planned is a way to diff the elements to serialize so the data is really compact.

Interesting. Right now I am thinking about how to efficiently transfer graphs forth and back (main transport is WCF). One way would be to use DTO. When fetching I'd create (fetch from database) entity graph on server side, project it to DTO graph (it shouldn't be difficult) that'd travel over the wire. The DTO graphs should be used to recreate entity graph on client side.

When saving, I'd collect all dirty entities from that graph and create DTO objects from them (perhaps by using UoW - I'd have the right order of operation). I'd send DTO over the wire, on server create all entities in correct order and save them in a transaction.

But if you are going to make the save step "out of the box" then the DTOs aren't really required anymore since the fetch scenario doesn't really require them.

Actually, one only needs to send the EntityFields2 objects, and then only the values which are changed (or set to a value), and graph information, and it should be possible to re-build the graph on the other side, though this does require logic which isn't there yet. simple_smile .

You have to collect all dirty entities + all entities which receive an FK value from a PK side. for example employee E will now work for a new department D. E isn't 'dirty' though after D is saved, E's WorksForDepartmentId will be changed (with the new id for D) and E will be dirty. This is done for you by the action queue producer methods in ObjectGraphUtils. The entities produced by these routines are the ones to sent. At the moment, the reference hierarchies aren't cut off properly for serialization. So if these queues are serialized, the entities they reference are still serialized too, while that shouldn't be done.

Frans Bouma | Lead developer LLBLGen Pro
mihies avatar
mihies
User
Posts: 800
Joined: 29-Jan-2006
# Posted on: 23-Dec-2008 11:47:41   

Actually, one only needs to send the EntityFields2 objects, and then only the values which are changed (or set to a value), and graph information, and it should be possible to re-build the graph on the other side, though this does require logic which isn't there yet. Regular Smiley.

Yep, that's all its needed.

You have to collect all dirty entities + all entities which receive an FK value from a PK side. for example employee E will now work for a new department D. E isn't 'dirty' though after D is saved, E's WorksForDepartmentId will be changed (with the new id for D) and E will be dirty. This is done for you by the action queue producer methods in ObjectGraphUtils. The entities produced by these routines are the ones to sent. At the moment, the reference hierarchies aren't cut off properly for serialization. So if these queues are serialized, the entities they reference are still serialized too, while that shouldn't be done.

That's actually one of the two problems. Currently I am using some sort of remoting and UoW thus too much data is sent unnecessarily. So, if you create a WCF serializable UoW and a good pruning algorithm that would be just awesome. simple_smile

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39588
Joined: 17-Aug-2003
# Posted on: 28-Dec-2008 11:46:54   

mihies wrote:

You have to collect all dirty entities + all entities which receive an FK value from a PK side. for example employee E will now work for a new department D. E isn't 'dirty' though after D is saved, E's WorksForDepartmentId will be changed (with the new id for D) and E will be dirty. This is done for you by the action queue producer methods in ObjectGraphUtils. The entities produced by these routines are the ones to sent. At the moment, the reference hierarchies aren't cut off properly for serialization. So if these queues are serialized, the entities they reference are still serialized too, while that shouldn't be done.

That's actually one of the two problems. Currently I am using some sort of remoting and UoW thus too much data is sent unnecessarily. So, if you create a WCF serializable UoW and a good pruning algorithm that would be just awesome. simple_smile

simple_smile Let's see how far I can get with v3's attempt at this problem. simple_smile

Frans Bouma | Lead developer LLBLGen Pro
mihies avatar
mihies
User
Posts: 800
Joined: 29-Jan-2006
# Posted on: 28-Dec-2008 11:58:44   

Otis wrote:

simple_smile Let's see how far I can get with v3's attempt at this problem. simple_smile

I don't doubt it will be just perfect. simple_smile And then the n-tier capability will be unleashed!