2

Я использую надежную очередь в своем приложении для работы с сервисом Stateful. Когда я пытаюсь поставить в очередь элемент, который, метод Enqueue бросает исключениеОшибка последовательной передачи IReliableQueue Enqueue

код используется

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; } 
    } 

На myQueue.EnqueueAsync, его бросает исключение,

Тип 'TestService.CreateCommand' с именем контракта данных 'CreateCommand: http://schemas.datacontract.org/2004/07/TestService' is не ожидается. Подумайте об использовании DataContractResolver, если вы используете DataContractSerializer или добавляете любые типы, не известные статически в список известных типов - например, с помощью атрибута KnownTypeAttribute или путем добавления их в список известных типов, переданных в сериализатор .

Трассировка стека

на System.Runtime.Serialization.XmlObjectSerializerWriteContext.SerializeAndVerifyType (DataContract DataContract, XmlWriterDelegator XMLWriter, OBJ Object, Boolean verifyKnownType, RuntimeTypeHandle declaredTypeHandle, Тип declaredType) в системе .Runtime.Serialization.XmlObjectSerializerWriteContext.SerializeWithXsiTypeAtTopLevel (DataContract dataContract, XmlWriterDelegator xmlWriter, Object obj, R untimeTypeHandle originalDeclaredTypeHandle, Тип graphType) при System.Runtime.Serialization.DataContractSerializer.InternalWriteObjectContent (XmlWriterDelegator писателя, графа объекта, DataContractResolver dataContractResolver) при System.Runtime.Serialization.DataContractSerializer.InternalWriteObject (XmlWriterDelegator писатель, граф объектов, DataContractResolver dataContractResolver) на System.Runtime.Serialization.XmlObjectSerializer.WriteObjectHandleExceptions (XmlWriterDelegator писатель, граф объектов, DataContractResolver dataContractResolver) на System.Runtime.Serialization.XmlObjectSerializer.WriteObject (XmlDictionaryWriter писатель, граф объектов) в Microsoft.ServiceFabric.Replicator.DataContractStateS erializer 1.Write(T value, BinaryWriter binaryWriter) at System.Fabric.Store.TStore 5.GetValueBytes (TValue CurrentValue, TValue новое_значение) при System.Fabric.Store.TStore 5.<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.DistributedQueue 1.d__9.MoveNext() в System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess (Задача задачи) при System.Runtime .CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification (Task задача) в System.Runtime.CompilerServices.TaskAwaiter.GetResult()
на TestService.TestService.d__2.MoveNext() в D: \ Projects \ Local \ ReliableSerialization \ Application1 \ TestService \ TestService.cs: строка 51 at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess (Задача задача) в Система .Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification (Task задача) в Microsoft.ServiceFabric.Services.Runtime.StatefulServiceReplicaAdapter.d__f.MoveNext()

Когда я изменил код, чтобы использовать конкретный тип Он работал отлично.

в моем случае IReliableQueue<CreateCommand>

Он работал отлично.

мой код ссылки: https://github.com/Azure-Samples/service-fabric-dotnet-iot/blob/master/src/gateway/IoTProcessorManagement.Common/WorkManagement/WorkManager.cs

+0

«DataContractSerializer» сериализует конкретные типы, а не интерфейсы. Взгляните на [Сериализация списка интерфейсов с помощью DataContractSerializer] (https://stackoverflow.com/questions/14937097/serializing-a-list-of-interfaces-using-the-datacontractserializer). – dbc

ответ

3

Вы упускаете DataContract и DataMember атрибуты ваших типов, а также из-за реализации, как контракт данных сериализации вы не можете поставить атрибут DataContract на интерфейсах. Отсутствующие атрибуты контракта данных могут вызывать затруднения в отслеживании ошибок, потому что Service Fabric будет хранить ссылку на объект внутри и возвращать эту ссылку при удалении, но сериализует объект, когда он отправляет его второстепенным при фиксации. То, что вы увидите, когда вы забудете упомянутые атрибуты, состоит в том, что он будет работать иногда, но либо бросит на совершение транзакции, либо у вас будут, казалось бы, заполненные объекты до тех пор, пока служба не перезапустится (для обновления, если этот узел пошел вниз или по какой-либо другой причине), которая затем будет пустой (поля, отсутствующие в атрибутах, будут иметь значение null/default).

Чтобы воспользоваться преимуществами полиморфизма в Контрактах данных, вы можете использовать базовый класс и атрибут KnownType. Это будет выглядеть ваш пример с добавленной командой delete.

[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; } 
} 

отметить также, что известный атрибут типа поддерживает переходящий в функции программно открыть вложенные типы, но не использовать его, как он сломается апгрейд в нечетных и трудно диагностировать способы.