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

Имам запитване относно .Net Remoting, създаване на версии и създаване на активирани от клиента обекти.

Ето сценария:

Има 2 интерфейса, които се намират в собствена сборка „SharedTypes“: IServer и IAccount. IServer съдържа методи „GetStatus“, който връща низ, и „CreateAccount“, който връща тип IAccount. Това е регистрирано в GAC като v1.0.0.0.

Сървърното приложение препраща към SharedTypes и имплементира IServer и IAccount с конкретни класове, сървър и акаунт. Това са обекти MarshalByRefObject. Приложението Server маршалира класа Server като единичен обект.

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

Сега увеличавам версията на SharedTypes до v2.0.0.0 и се регистрирам в GAC, като премахвам старата версия v1.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