How can I replace the property while serializing the DataContractSerializer?

I have a working ChangeTrackingList implementation that works great, but I want to "filter" its content when it is sent from the client back to the server so that it only includes the changes. Getting changes is easy as my list provides a GetChanges method just for this purpose. How can I interrupt the DataContractSerializer and replace List.GetChanges () where List was used?

More details: Consider a parent / child relationship where I have one parent with multiple children, each with a parent reference, like Customer / Orders. Serializing the entire child list to the client application is fine as I need to show all orders. However, I don't want to return all orders to the server when I save only the changes.

Complexity: I've already looked at the ISerializable implementation and my own GetObjectData implementation, which wouldn't be very difficult if it wasn't for the fact that I also need to store object references. If I find a DataContractSerializer on my graph and enable PreserveObjectReferences (either by adding behavior or explicitly through the constructor), I get a very nice graph without duplication, but it wants to include my entire ChangeTrackingList. If I implement ISerializable, I can manually write my ChangeTrackingList and only include changes, but those child objects will no longer know their parent references.

Clarification: This is a very simplified example to illustrate the problem. I am not looking for alternative solutions to this particular problem. My real problem is not with customers, orders or line items. Alternative solutions to the Customer / Order problem are not the answers I am looking for.

I'm quite simply looking for a way to serialize only the "interesting" parts of an object graph. The mechanism for identifying and filtering to "interesting" is already done, I just need a way to replace these parts on the object graph during serialization.

Another example: Let's say we have a Person object, underneath is a collection of Phone entities. Phones are invalid without a parent, and the business rules state that Face is invalid on at least one phone. I can't just save the person and then the phones in two separate calls because each call will be an error. I have to save them as a schedule in one call. Later, if I update Person to change my address, which is stored on Person, and also add a new phone number, I need to send the updated person as well as changes to the phone list. I don't want to ship the original phone because it hasn't changed.

This is a contrived example again, but another is very similar to a real life problem. I have parents who are invalid with at least one child and no child is valid without a parent.

Update: It looks like the "substitution" I want to perform can be done using the DataContractSurrogate class. I've seen several examples of this and they are relatively simple, but that's because their examples are too. They usually fall under the category "Swap EmployeeSurrogate for Employee", where "Employee" is some class not related to serialization. In my case, things get weirder because the class I want to replace is a generic type.

Thus, a simpler version of the problem might be like this. Let's say I have a completely non-serializable class MyList. (And before anyone suggests it, replacing the MyList class is also not a valid solution. Remember, this is just an example.) I want to set up a DataContractSurrogate so that whenever MyList appears on my object graph, I want to convert this simple array for serialization.

Does THIS sound like a valid direction for anyone? Has anyone ever tried to surrogate a generic type? Am I insane even for this?

+2


source to share


4 answers


Ok, finally I have something that works and would like to share an answer. Unfortunately, I cannot just share the code as it was written at the time of the client.

The key to the solution is the DataContractSurrogate class, which you can read here and here . Typically you would use this to provide a rack for a non-serializable class in an object graph.

To illustrate, imagine that we have a class called MyList, which is not serializable. We need to create a surrogate class to act as its "stand". The naming conventions here are pretty awful, as the one called MyListSurrogate is really more of a factory, and the one called MyListSurrogated is what I consider to be an actual surrogate. In any case, the "surrogate" class provides a simple array or List and is labeled DataContract. This is the class that will go through the wire. The MyListSurrogate class implements IDataContractSurrogate and implements four important methods.

The GetDataContractType method returns the stand-in type specified for the entity type. When the type is set to MyList, it must return MyListSurrogated. Any other type should simply return the original type. This method involves some messy thinking and so I will include the code for this rather than just explaining it.

public Type GetDataContractType(Type type)
{
    if(type.IsGenericType 
        && (type.GetGenericTypeDefinition() == typeof(MyList<>)))
    {
        var itemType = type.GetGenericArguments()[0];
        var result = typeof(MyListSurrogated<>)
            .GetGenericTypeDefinition().MakeGenericType(itemType);
        return result;
    }
    return type;
}

      



Likewise, GetObjectToSerialize turns a MyList instance into a MyListSurrogated instance. And GetDeserializedObject turns the MyListSurrogated instance back into a MyList instance. IDataContractSurrogate includes several other methods that we won't be using, and I just returned null for most of them.

An instance of the MyListSurrogate class can then be passed to the DataContractSerializer constructor, and it will be called during serialization and deserialization to replace types as needed on the fly. Unfortunately, there is no easy way to specify a surrogate class via configuration, so unless you create your own DataContractSerializer, you will have to implement the DataContractSerializerOperationBehavior, which you can read about here . The application behavior is the same as described here below .

To use surrogates, you will have to deal with some known type problems. In my example, the type MyListSurrogated must be added to the list of known types for the service you are trying to call. You can do it manually or in my case as part of the code generation template.

Last thing. My initial goal was to create a ChangeTrackingList that will transfer all content from server to client, but only client to server changes. It's really easy once the surrogate has been mapped. My ChangeTrackingList has a boolean property called "IncludeOriginalsWhenSerializing" which is true by default. The surrogate class method "GetObjectToSerialize" examines this flag to decide whether to copy the original items to the stand or not. At the other end of the line, the GetDeserializedObject method recreates the original ChangeTrackingList and then sets the flag to false during deserialization. So, a list with flag set to true goes at one end and the other end with flag set to false. It's simple,it's automatic, it's finished.

0


source


You are approaching this the wrong way. You want to have one set of serialization methods when you send the list to the client and a different set of actions when you send it to the server.

You must have explicit code on the client that will call GetChanges and then send this truncated list back to the server.




Since you seem to have one root with only part of the children changed, and you only want to send back those children that have changed, you will need to create a new type that has a list of children you want to change, not the whole graph objects.

In other words, you need to create List<T>

(or some other appropriate container) and serialize THAT back to the server. The point is that you will not have a fully rehydrated object, but only children that have been changed.

If you want a fully rehydrated object, you must send back the entire graphic object independently.

0


source


I was struggling with a similar problem. I think @casperOne is on the right track. The server-to-client message must contain the complete object graph in one large chunk. But changes to the complete object graph must be done with small helper methods that exchange a limited subset of data.

For example, you get the complete Customer / Orders graph using the ReadFullCustomer () operation, and then navigate to the server using the AddOrder () method, passing only the order information required for this task. Let your server domain logic handle the save bit of the parent record according to its children.

Rory Primrose has an article on Draft WCF Service Contract that talks about short and chat interfaces. In your case, it sounds like you want a combination of both: a short read operation with chatty modification operations.


EDIT: I'm not sure I fully understood your scenario, @Mel. It sounds like you want to change the object graph during serialization. The only way I know this is to ensure that the classes in your graphics implement IXmlSerializable

and deploy your own methods WriteXml

and ReadXml

. The DataContractSerializer understands the classes it implements IXmlSerializable

, so it might be worth trying.

0


source


The solution is to recognize that you have two different graphs: a graph of the original objects and a graph of changes. They are not of the same type. The original will have an object of type Customer, and the other will have an object of type ChangedCustomer. A customer might have a collection of orders, but a ChangedCustomer will have a collection of ChangedOrders, etc.

The GetChanges operation will return a list of ChangedCustomer, not a list of customers.

0


source







All Articles