Mef: «Не удается сериализовать» при попытке загрузить ApiController из другого домена приложения - PullRequest
0 голосов
/ 29 апреля 2018

Я пытаюсь найти способ обновить веб-API Asp.net (.Net Framework 4.5) во время выполнения (без перезапуска основного домена приложения), добавив новый ApiController (загруженный другой службой).

Я пытался использовать Mef и смог загрузить новый ApiController в текущий домен приложения, но я застрял при попытке обновить существующий плагин (сборка уже добавлена ​​в домен приложения, поэтому я не могу добавить новый). Поэтому я решил загрузить плагин, содержащий ApiController, в отдельный домен приложения и использовать MarshalByRefObject для загрузки его из основного домена приложения, но оказалось, что ApiConroller не может быть сериализован.

Вы знаете, как я мог сериализовать это? Вы знаете альтернативу?

Edit:

Мне удалось загрузить разные версии сборки (в одном домене приложения), если сборка подписана, но она не соответствует моим требованиям.

1 Ответ

0 голосов
/ 02 июня 2018

Я не использовал MEF (потому что реализовать его функциональность с нуля так же легко, в отличие от MAF), но у меня есть некоторый опыт работы с голыми доменами приложений.

Трудно сказать многое, не видя ваш код, но из того, что вы написали, мне кажется, что вы путаете некоторые вещи.

Как вы, наверное, знаете, и вы уже указали, вы не можете обновить уже загруженную сборку. Загрузка другой версии (с другой подписью) означает, что у вас загружены две разные сборки. Типы внутри них будут иметь разные строгие имена. На самом деле вы можете справиться с этим, если хотите. Единственный способ выгрузить сборку - выгрузить домен приложения, в котором она находится.

Моя проблема с этим предложением:

... загрузить плагин, содержащий ApiController, в отдельном домене приложения и используйте MarshalByRefObject для загрузки из основного домена приложения

Определение типа (класса) + данные кода и экземпляра - две разные вещи. Загрузка сборки в домен приложения означает, что вы загружаете определение типа и код. Сериализация появляется, когда вы хотите перенести данные экземпляра через границы домена приложения. Вы не можете загрузить определение типа и код из другого домена приложения, как вы написали (на самом деле вы могли бы, но я сомневаюсь, что вам нужно). Чтобы иметь возможность передавать данные экземпляра, обе стороны должны знать об определении типа передаваемого экземпляра. Сериализация и передача в этом случае управляются средой удаленного взаимодействия .net. У вас есть два варианта: либо переместить все данные экземпляра и сериализовать их все время, либо вы выбираете способ MarshalByObjRef, как вы и сказали. Давай останемся с этим. Чтобы иметь возможность работать с экземпляром в другом домене приложения, вам нужно будет создать экземпляр типа в другом домене приложения, используя активатор (в этом случае вы не можете использовать оператор new), и получить ссылку на него, которая будет прокси на основе типа, который вы знаете (это может быть интерфейс или базовый класс, а не только точный тип). Отражение несколько ограничено в такой ситуации, еще меньше подготовлен asp.net, чтобы выяснить методы удаленного объекта - но вы могли бы помочь ему с правильными интерфейсами.

Итак, давайте представим, что вы создали экземпляр контроллера в другом домене приложения, и у вас есть ссылка удаленного взаимодействия, назначаемая типу интерфейса, который определяет все методы, которые вы должны предоставить asp.net. Теперь появится возможность сериализации, когда вы пытаетесь получить доступ к членам класса контроллера. Каждый параметр метода и тип возвращаемого метода должны быть сериализуемыми. Но не сам класс, поскольку он является MashalByObjRef потомком и не будет рассматриваться как пример. И MashalByObjRef не имеет ничего общего с тем, как вы загружаете сборку в домен приложения.

Но подождите! И MarshalByObjRef, и ApiController являются абстрактными классами. Как вы хотите получить свой фактический класс контроллера из обоих? Ты не можешь Поэтому я не думаю, что вы можете напрямую использовать apicontrollers из другого домена приложения.

Я мог представить две вещи:

1) Загрузите новую подписанную версию в ту же сборку и настройте механизм маршрутизации для перенаправления запросов на последнюю версию (возможно, она еще не действительна, но может быть хорошей отправной точкой: https://www.strathweb.com/2013/08/customizing-controller-discovery-in-asp-net-web-api/). Конечно, при перезапуске вы должны загрузить только самую последнюю версию, если вам не нужно иметь несколько версий параллельно.

2) Сделать слегка сложную инфраструктуру:

  • определить интерфейс для логики контроллера
  • создает apicontroller без версий и без логики, но способный создавать и выгружать домены приложений, загружать сборки в них, сохранять ссылки на экземпляры, реализующие интерфейс, созданный в них выше, и направлять запросы к этим
  • Имейте в виду, что вы не сможете передать некоторые вещи (например, контекст контроллера) в логику в другом домене приложения, вам придется извлечь то, что вам нужно, или воссоздать на другой стороне
  • таким образом, вы можете иметь логический MarshalByObjRef потомок в «удаленном» домене приложения и ваш контроллер ApiController потомок в главном домене приложения.
  • Я бы создал промежуточный абстрактный класс, расширяющий ApiController с возможностью обрабатывать вышеуказанное разделение самостоятельно. Остальная часть приложения не будет знать об этом.
  • Помните о пожизненных сервисах, связанных с удаленным взаимодействием, которые вы можете обрабатывать, используя спонсора или переопределяя некоторые методы MarshalByObjRef.

Не простой подход, вы столкнетесь с некоторыми дополнительными проблемами ...

...