Служебная фабрика: System.ArgumentException: не найден интерфейс с этим идентификатором -488762776

Я работаю над простым кластером Service Fabric, где я хочу вызывать службу без сохранения состояния из веб-API AspNet Core 2.0 без учета состояния.

Первым делом я создал библиотеку классов.Net Standard 2.0 с простым интерфейсом и DTO:

public interface IMicroService : IService
{
    Task<MyDto> GetMahDto(int id);
}

public class MyDto
{
    public string Name { get; set; }
}

Затем я создал пакет nuget и добавил его в качестве зависимости как к моему веб-API (проект MyServiceApi), так и к службе без сохранения состояния (MyService).

Слушатель службы определяется как:

        protected override IEnumerable<ServiceInstanceListener> CreateServiceInstanceListeners()
    {
        var fabricListener = new ServiceInstanceListener((context) =>
        {
            var fabricRemotingListener = new FabricTransportServiceRemotingListener(
            serviceContext: context,
            serviceRemotingMessageHandler: null,
            remotingListenerSettings: new Microsoft.ServiceFabric.Services.Remoting.FabricTransport.Runtime.FabricTransportRemotingListenerSettings()
            {
                EndpointResourceName = "myendpoint"
            },
            serializationProvider: null);


            return fabricRemotingListener;
        },
       "mylistener");

        return new ServiceInstanceListener[] { fabricListener };
    }

и реализация интерфейса просто:

public Task<MyDto> GetMahDto(int id)
    {
        return Task.FromResult(new MyDto() { Name=$"Hallo from TheService in FabricSandbox3 project, id: {id}" });
    }

В MyServiceApi у меня есть метод контроллера, который выглядит следующим образом:

[HttpGet]
    public async Task<MyDto> Get()
    {

        var svc = ServiceProxy.Create<ContractsStandard.IMicroService>(new Uri("fabric:/FabricSandbox4/TheService"), listenerName: "mylistener");

        return await svc.GetMahDto(23);
    }

Когда я запускаю отладчик, я могу войти в метод контроллера MyServiceApi, но он выдает следующее исключение:

System.AggregateException: 'Произошла одна или несколько ошибок. (Исключение System.ArgumentException не было обработано в службе и не может быть сериализовано для передачи клиенту. Подробная информация об удаленном исключении: System.ArgumentException: не найден интерфейс с этим идентификатором -488762776 в Microsoft.ServiceFabric.Services.Remoting.V2.ServiceRemotingMessageSerializersManager.GetInterfaceDetails(Int32 interfaceId) в Microsoft.ServiceFabric.Services.Remoting.V2.ServiceRemotingMessageSerializersManager.CreateSerializers(Int32 interfaceId) в System.Collections.Concurrent.Concurrentictionary 2.GetOrAdd(TKey key, Func2 valueFactory) в Microsoft.ServiceFabric.Services.Remoting.V2.ServiceRemotingMessageSerializersManager.GetRequestBodySerializer(Int32 interfaceId) в Microsoft.ServiceFabric.Services.Remoting.V2.FabricTransport.Runtime.Wessage Модель.Services.Remoting.V2.FabricTransport.Runtime.FabricTransportMessageHandler.d__7.MoveNext())"

Я не нашел ничего в моих диагностических событиях, которое выглядит многообещающим, и единственное упоминание о похожих проблемах (на этом сайте и в других местах), которые я видел, обычно касалось типов в разных сборках, что не является проблемой, учитывая, что общий интерфейс содержится в пакете nuget.

Есть идеи, где я могу пойти не так?

3 ответа

Решение

Смотрите в нижней части этого ответа, чтобы перейти к окончательному решению.

Я нашел ответ, который является частично удовлетворительным, однако я не совсем знаю, почему он работает. Это была чистая случайность, когда я наткнулся на это - я работал над способом беспрепятственной передачи заголовков с запросами фабрики, и именно так я и попал в эту ситуацию.

Ссылка на код слушателя выше, и обратите внимание на эту строку, в частности:

 serviceRemotingMessageHandler: null,

Чтобы это работало, я создал собственную реализацию IServiceRemotingMessageHandler (обратите внимание, что он просто делегирует вызов базовому типу):

class TestRemotingDispatcher : ServiceRemotingMessageDispatcher, IServiceRemotingMessageHandler
{

    public TestRemotingDispatcher(
        ServiceContext serviceContext, IService serviceImplementation, IServiceRemotingMessageBodyFactory serviceRemotingMessageBodyFactory = null) :
        base(serviceContext, serviceImplementation, serviceRemotingMessageBodyFactory)
    {

    }

    public override void HandleOneWayMessage(IServiceRemotingRequestMessage requestMessage)
    {
        base.HandleOneWayMessage(requestMessage);
    }

    public override Task<IServiceRemotingResponseMessageBody> HandleRequestResponseAsync(
        ServiceRemotingDispatchHeaders requestMessageDispatchHeaders, IServiceRemotingRequestMessageBody requestMessageBody, CancellationToken cancellationToken)
    {

        return base.HandleRequestResponseAsync(requestMessageDispatchHeaders, requestMessageBody, cancellationToken);
    }

    public override Task<IServiceRemotingResponseMessage> HandleRequestResponseAsync(IServiceRemotingRequestContext requestContext, IServiceRemotingRequestMessage requestMessage)
    {

        return base.HandleRequestResponseAsync(requestContext, requestMessage);
    }
}

Тогда слушатель становится:

var fabricListener = new ServiceInstanceListener((context) =>
        {
        var fabricRemotingListener = new FabricTransportServiceRemotingListener(
        serviceContext: context,
        serviceRemotingMessageHandler: new TestRemotingDispatcher(context, this),
            remotingListenerSettings: new Microsoft.ServiceFabric.Services.Remoting.FabricTransport.Runtime.FabricTransportRemotingListenerSettings()
            {
                EndpointResourceName = "mngendpoint"
            },
            serializationProvider: null);


            return fabricRemotingListener;
        },
      "mnglistener");

Это работает как ожидалось. Если я выясню почему, я обновлю.

Редактировать: не выяснил, почему работает явное назначение serviceRemotingMessageHandler, но показанный выше подтип ServiceRemotingMessageDispatcher не требуется. Это будет работать, назначив встроенную реализацию:

 serviceRemotingMessageHandler: new ServiceRemotingMessageDispatcher(context, serviceInstance)

Последнее объяснение

Все это сводится к моей ошибке - заметьте, я сначала назначил null для serviceRemotingMessageHandler, что неверно. После просмотра исходного кода, есть конструктор, который создаст для вас диспетчер. Присваивая null так, как я это сделал, диспетчер внутри слушателя не существовал.

 public FabricTransportServiceRemotingListener(
        ServiceContext serviceContext,
        IService serviceImplementation,
        FabricTransportRemotingListenerSettings remotingListenerSettings = null,
        IServiceRemotingMessageSerializationProvider serializationProvider = null)
        : this(
              serviceContext,
              new ServiceRemotingMessageDispatcher(
                serviceContext,
                serviceImplementation,
                GetMessageBodyFactory(serializationProvider, remotingListenerSettings)),
              remotingListenerSettings,
              serializationProvider)
    {
    }

Сообщение об исключении определенно отправило меня по неверному пути, но, по крайней мере, есть хорошее объяснение (вызванной самим собой) проблемы.

Однажды я столкнулся с той же проблемой и не смог ее решить иначе, чем с помощью метода NonIServiceProxy:

public async Task<MyDto> Get()
{

    var svc = ServiceProxy.CreateNonIServiceProxy<ContractsStandard.IMicroService>(new Uri("fabric:/FabricSandbox4/TheService"), listenerName: "mylistener");

    return await svc.GetMahDto(23);
}

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

У меня просто была эта проблема, для меня это был случай пространства имен для интерфейса. Глупая ошибка. Я просто скопировал класс в другой проект без пространства имен.

Другие вопросы по тегам