System.InvalidCastException при создании объекта, активируемого клиентом, с использованием более старой версии интерфейса, определенного в сборке со строгим именем.

У меня есть вопрос о .Net Remoting, управлении версиями и создании объектов, активируемых клиентом.

Вот сценарий:

Есть 2 интерфейса, находящихся в собственной сборке «SharedTypes»: IServer и IAccount. IServer содержит методы «GetStatus», возвращающие строку, и «CreateAccount», возвращающие тип IAccount. Это зарегистрировано в GAC как v1.0.0.0.

Серверное приложение ссылается на SharedTypes и реализует IServer и IAccount с конкретными классами Server и Account. Это объекты MarshalByRefObject. Приложение Server маршалирует класс Server как одноэлементный объект.

Клиентское приложение ссылается на SharedTypes и успешно подключается к удаленному объекту сервера через интерфейс IServer. Здесь я могу успешно вызвать GetStatus и CreateAccount (которые возвращают объект, активированный клиентом). Пока все в порядке.

Теперь я увеличиваю версию SharedTypes до версии 2.0.0.0 и регистрируюсь в GAC, удаляя старую версию версии 1.0.0.0.

Серверное приложение создано для этой версии, а клиент — нет.

Теперь, когда я запускаю клиентское приложение, оно, как и ожидалось, будет жаловаться на System.IO.FileNotFoundException, т. е. не может найти v1.0.0.0 SharedTypes в GAC.

Если я скопирую v1.0.0.0 SharedTypes в каталог exe клиента, клиентское приложение в конечном итоге привязывается к этому (после неудачного поиска GAC). Клиентское приложение запускается, и я могу успешно вызвать GetStatus для объекта IServer (через одноэлементный объект). Однако, если я вызываю CreateAccount, который должен возвращать активированный клиентом объект, я получаю следующее исключение:

System.InvalidCastException: Return argument has an invalid type.
   at System.Runtime.Remoting.Proxies.RealProxy.ValidateReturnArg(Object arg, Type paramType)
   at System.Runtime.Remoting.Proxies.RealProxy.PropagateOutParameters(IMessage msg, Object[] outArgs, Object returnValue)
   at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type)
   at SharedTypes.IServer.GetAccount()

Мой вопрос заключается в том, почему вызов GetStatus на одноэлементном объекте, активированном сервером, от клиента (который использует v1.0.0.0) не вызывает это исключение, тогда как создание активированного клиентом объекта с помощью CreateAccount делает? Поскольку оба типа создаются на сервере, я бы подумал, что вызов GetStatus приведет к одному и тому же исключению?


person Jeb    schedule 12.08.2011    source источник


Ответы (3)


CLR обычно гарантирует, что в процесс может быть загружена только одна конкретная версия сборки. В данном сценарии это не работает, поскольку работают две копии среды CLR: одна на сервере, а другая на клиенте. Итак, теперь инфраструктура удаленного взаимодействия должна обеспечить совместимость объектов удаленного типа. Что делает с апломбом в вашем случае. Не уверен, что понял вопрос, заданный в вашем последнем предложении, но сервер иначе не знает о версии сборки, загруженной в клиент.

Требуется перекомпиляция клиента.

person Hans Passant    schedule 12.08.2011
comment
Спасибо, Ганс, я отредактировал вопрос, надеюсь, он более понятен. Мы избегаем перекомпиляции клиента, используя сборки политик издателя, которые прекрасно решают эту проблему. Однако мне любопытно узнать, почему этот конкретный сценарий приводит к тому, что клиент, который ссылается на более старую версию сборки со строгим именем, может вызвать метод на активированном сервером удаленном объекте в серверном приложении, ссылающемся на новую версию сборки со строгим именем. успешно, но не может создать активируемый клиентом объект. - person Jeb; 12.08.2011

Это может не отвечать на ваш конкретный вопрос, но это единственный вопрос, который я могу найти в StackOverflow относительно необъяснимого InvalidCastException с сообщением «Аргумент возврата имеет недопустимый тип», связанный с одноэлементным сервером.

В моем случае мой клиент начал с запроса синглтона сервера (т. е. объекта, который был передан RemotingServices.Marshal() на сервере), то в процессе его обработки произошло получение ссылки на этот синглтон через другие методы и получили это исключение.

Я работал над этим, создав два удаленных прокси-сервера для объекта верхнего уровня, один для использования в качестве синглтона, а другой для внутреннего использования в других контекстах. Возможно, это связано с тем, что в ходе обработки клиента он пытался получить ссылку на этот одноэлементный объект через тип, отличный от того, который был передан в RemotingServices.Marshal(). Я все еще не уверен. Но наличие двух удаленных прокси-серверов для одного и того же локального объекта, один для удаленного одноэлементного взаимодействия, а другой для всех других внутренних целей, решило проблему. К счастью, у меня есть архитектура удаленного прокси без сохранения состояния, поэтому наличие двух для одного и того же локального объекта не вызвало никаких проблем.

Изменить: позже я нашел более простое решение, которое не требует двух прокси-серверов — вызовите RemotingServices.Marshal() с фактическим типом прокси в параметре 3 вместо типа интерфейса.

person ulatekh    schedule 24.06.2015

Для меня клиентское приложение было на .net 4.0, а серверное приложение работало на 4.5. Установка .net framework 4.5 на клиентский компьютер устранила эту проблему.

person Usman    schedule 17.08.2015