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

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

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

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

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

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

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

Приложение Server построено на этой версии, а клиент - нет.

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

Если я копирую v1.0.0.0 SharedTypes в каталог exe клиента, клиентское приложение в конечном итоге связывается с этим (после того, как поиск GAC был неудачным). Клиентское приложение запускается, и я могу успешно вызвать GetStatus для объекта IServer (через объект singleton). Однако, если я вызываю 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 приведет к тому же исключению?

3 ответа

Решение

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

Перекомпиляция клиента обязательна.

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

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

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

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

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

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