Служебная фабрика: 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, Func
2 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);
}
По сути, это устраняет необходимость совместного использования одного и того же интерфейса между службой и клиентом.
У меня просто была эта проблема, для меня это был случай пространства имен для интерфейса. Глупая ошибка. Я просто скопировал класс в другой проект без пространства имен.