简体   繁体   中英

Service Fabric Reliable Collections: serialization issue

Reliable Collection (Queue) values store some complex type SomeUnit .

I've marked it as [DataContract] and it's members as [DataMember] and added [KnownType(typeof(SomeUnit))] attribute on top of it also.

Looks like now SomeUnit serializes, but then deserialize exception is thrown:

Element 'urn:ServiceFabric.Communication:item' contains data from a type that maps to the name ' http://schemas.datacontract.org/2004/07/RP.Core:SomeUnit '. The deserializer has no knowledge of any type that maps to this name. Consider using a DataContractResolver if you are using DataContractSerializer or add the type corresponding to 'SomeUnit' 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 the serializer.

How can I solve it?

Since you are not showing any code we can only guess what's causing this.

This problem would occur if you would use a reliable queue with a generalized item type, for example a base class of SomeUnit .

// Using reliable collection with a base item type
IReliableQueue<BaseClass> myQueue = ...;

// Store derived item in the queue
SomeUnit myData = ...; // SomeUnit inherit from BaseClass
await myQueue.EnqueueAsync(txn, myData); // OK to store but won't deserialize!

The deserializer for that queue would know how to parse BaseClass but it won't be implicitly aware of your derived class SomeUnit .

You can fix that by applying a KnownTypeAttribute on the base class, thereby explicitly declaring the derived classes that the deserializer shall be aware of.

[DataContract]
[KnownType(typeof(SomeUnit))]
public class BaseClass
{
    ...
}

[DataContract]
public class SomeUnit : BaseClass
{
    ...
}

It's not possible to apply [KnownType] on an interface type. There are, however, some options for supporting this:

Option #1

Use a wrapper contract to declare known type(s).

[DataContract]
[KnownType(typeof(SomeUnit))]
public class Wrapper
{
    [DataMember]
    public IUnit Value { get; set; }
}

[DataContract]
public class SomeUnit : IUnit
{
    ...
}

Option #2

Specify known types to the DataContractSerializer constructor .

This will, however, require that you tell service fabric to use your custom serializer .

Option #3

Specify known types in a configuration file (app.config) as described here .

I know this is a bit of an old topic but after having this problem and not wanting to use the KnownType method (as it can get a bit messy if you have a lot of derived types) I was able to sucessfully get my messages serialized with the following use of reflection:

[DataContract]
[KnownType("GetKnownTypes")]
public abstract class Event
{
    [DataMember]
    public DateTime AtTime { get; private set; }

    public Event()
    {
        AtTime = DateTime.Now;
    }

    private static Type[] GetKnownTypes()
    {
        return typeof(Event).Assembly.GetTypes()
            .Where(x => x.IsSubclassOf(typeof(Event)))
            .ToArray();
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM