2

I am using a Reliable Queue in my Stateful service fabric application. When I am trying to Enqueue an Item which, the Enqueue method is throwing an exception

the code used is

 protected override async Task RunAsync(CancellationToken cancellationToken)
        {
            ICommand myItem = new CreateCommand()
            {
                Data = "Sample Data",
                Id = Guid.NewGuid(),
                TenentName = "SampleTenant"
            };
            var myQueue = await this.StateManager.GetOrAddAsync<IReliableQueue<ICommand>>("CommandQueue");
            using (var tx = StateManager.CreateTransaction())
            {
                await myQueue.EnqueueAsync(tx, myItem, TimeSpan.FromSeconds(4), cancellationToken);
                await tx.CommitAsync();
            }
            using (var tx = StateManager.CreateTransaction())
            {
                var dq = await myQueue.TryDequeueAsync(tx);
                await tx.CommitAsync();
            }
        }
    }

    public interface ICommand
    {
        Guid Id { get; set; }
        string TenentName { get; set; }
    }


    public class CreateCommand : ICommand
    {
        public Guid Id { get; set; }
        public string TenentName { get; set; }
        public string Data { get; set; }
    }

On myQueue.EnqueueAsync, its is throwing an exception

Type 'TestService.CreateCommand' with data contract name 'CreateCommand:http://schemas.datacontract.org/2004/07/TestService' is not expected. Consider using a DataContractResolver if you are using DataContractSerializer 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 the serializer.

Stack Trace

at System.Runtime.Serialization.XmlObjectSerializerWriteContext.SerializeAndVerifyType(DataContract dataContract, XmlWriterDelegator xmlWriter, Object obj, Boolean verifyKnownType, RuntimeTypeHandle declaredTypeHandle, Type declaredType) at System.Runtime.Serialization.XmlObjectSerializerWriteContext.SerializeWithXsiTypeAtTopLevel(DataContract dataContract, XmlWriterDelegator xmlWriter, Object obj, RuntimeTypeHandle originalDeclaredTypeHandle, Type graphType) at System.Runtime.Serialization.DataContractSerializer.InternalWriteObjectContent(XmlWriterDelegator writer, Object graph, DataContractResolver dataContractResolver) at System.Runtime.Serialization.DataContractSerializer.InternalWriteObject(XmlWriterDelegator writer, Object graph, DataContractResolver dataContractResolver) at System.Runtime.Serialization.XmlObjectSerializer.WriteObjectHandleExceptions(XmlWriterDelegator writer, Object graph, DataContractResolver dataContractResolver) at System.Runtime.Serialization.XmlObjectSerializer.WriteObject(XmlDictionaryWriter writer, Object graph) at Microsoft.ServiceFabric.Replicator.DataContractStateSerializer1.Write(T value, BinaryWriter binaryWriter) at System.Fabric.Store.TStore5.GetValueBytes(TValue currentValue, TValue newValue) at System.Fabric.Store.TStore5.<AddAsync>d__4.MoveNext() at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Microsoft.ServiceFabric.Data.Collections.DistributedQueue1.d__9.MoveNext() at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
at TestService.TestService.d__2.MoveNext() in D:\Projects\Local\ReliableSerialization\Application1\TestService\TestService.cs:line 51 at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Microsoft.ServiceFabric.Services.Runtime.StatefulServiceReplicaAdapter.d__f.MoveNext()

When I changed the code to use a Concrete type It worked fine.

in my case IReliableQueue<CreateCommand>

It worked fine.

my code reference : https://github.com/Azure-Samples/service-fabric-dotnet-iot/blob/master/src/gateway/IoTProcessorManagement.Common/WorkManagement/WorkManager.cs

Binu Vijayan
  • 803
  • 1
  • 9
  • 24
  • `DataContractSerializer` serializes concrete types not interfaces. Take a look at [Serializing a List of Interfaces using the DataContractSerializer](https://stackoverflow.com/questions/14937097/serializing-a-list-of-interfaces-using-the-datacontractserializer). – dbc Sep 06 '16 at 07:48

1 Answers1

3

You're missing DataContract and DataMember attributes on your types, and because of the implementation of how data contract serialization works you cannot put the DataContract attribute on interfaces. Missing data contract attributes can cause hard to track down bugs because Service Fabric will store a reference to the object internally and return that reference when you dequeue, but serialize the object when it sends it to the secondaries on commit. What you'll see when you forget said attributes is that it'll appear to work sometimes, but will either throw on committing a transaction or you'll have seemingly fine populated objects until the service restarts (for an upgrade, if that node went down, or some other reason) which will then be empty (the fields missing the attributes will be null/default).

To take advantage of polymorphism in Data Contracts you can use a base class and the KnownType attribute. This is what your example would look like with an added delete command.

[DataContract]
[KnownType(typeof(CreateCommand))]
[KnownType(typeof(DeleteCommand))]
public class BaseCommand
{
    [DataMember]
    public Guid Id { get; set; }

    [DataMember]
    public string TenentName { get; set; }
}

[DataContract]
public class CreateCommand : BaseCommand
{
    [DataMember]
    public string Data { get; set; }
}

[DataContract]
public class DeleteCommand : BaseCommand
{
    [DataMember]
    public string SomeOtherData { get; set; }
}

Also note that the known type attribute supports passing in a function to programmatically discover sub types, but DO NOT use it as it will break upgradeability in odd and hard to diagnose ways.

pdylanross
  • 265
  • 3
  • 7