Как пройти ссылки как параметры метода по адресу appdomains?
-
05-10-2019 - |
Вопрос
Я пытался получить следующий код для работы (все определено в той же сборке):
namespace SomeApp{
public class A : MarshalByRefObject
{
public byte[] GetSomeData() { // }
}
public class B : MarshalByRefObject
{
private A remoteObj;
public void SetA(A remoteObj)
{
this.remoteObj = remoteObj;
}
}
public class C
{
A someA = new A();
public void Init()
{
AppDomain domain = AppDomain.CreateDomain("ChildDomain");
string currentAssemblyPath = Assembly.GetExecutingAssembly().Location;
B remoteB = domain.domain.CreateInstanceFromAndUnwrap(currentAssemblyPath,"SomeApp.B") as B;
remoteB.SetA(someA); // this throws an ArgumentException "Object type cannot be converted to target type."
}
}
}
То, что я пытаюсь сделать, это передавать ссылку на экземпляр «A», созданный в первом AppDomain к домену домена, и иметь дочерний домен, выполнил метод в первом домене. В какой-то момент в «B» код я собираюсь назвать «REDUREOBJ.Getsomedata ()». Это должно быть сделано, потому что метод «байт []» из «Getsomedata» должен быть «рассчитанным» на первом приложении. Что мне делать, чтобы избежать исключения, или что я могу сделать для достижения того же результата?
Решение
Я могу дублировать проблему, и, похоже, связано с testdriven.net и / или xunit.net. Если я запускаю C.init () в качестве метода теста, я получаю то же сообщение об ошибке. Однако, если я запускаю C.init () из приложения консоли, я не получаю исключение.
Вы видите то же самое, запустите C.Init () из модульного теста?
Редактировать: Я также могу продублировать проблему с помощью NUNIT и TESTDRIVEN.NET. Я также могу продублировать ошибку, используя NUNIT Runner вместо TestDriven.net. Таким образом, проблема, кажется, связана с выполнением этого кода через систему тестирования, хотя я не уверен, почему.
Другие советы
Фактическая корневая причина была вашей DLL загружена из разных мест в двух разных доменах приложений. Это приводит к тому, что они думают, что они разные сборки, которые, конечно, означает, что типы различны (даже если они имеют одинаковое имя класса, пространство и т. Д.).
Испытание по причинам, причина, когда проходил тест Джеффа, когда выполнял блок тестовой базы, заключается в том, что структура тестирования единиц, как правило, создает Appdomains с помощью ShadowCopy, установленной на «True». Но ваш вручную создан Appdomain по умолчанию для ShadowCopy = «false». Это приведет к загрузке DLL из разных мест, которые приводят к приятным «Тип объекта», нельзя преобразовать в тип цели ». ошибка.
ОБНОВЛЕНИЕ: После дальнейшего тестирования, кажется, спускается к ApplicationBase, отличающейся между двумя appdomains. Если они совпадают, то приведенный выше сценарий работает. Если они отличаются, это не так, даже если я подтвердил, что DLL загружается как в Appdomains, из того же каталога, используя WINDBG) Кроме того, если я включаю ShadowCopy = «True» в обоих моих Appodomains, то это не удается С другим сообщением: «System.invalidcastexception: объект должен реализовать iconblible».
UPDATE2: дальнейшее чтение ведет меня верить, что это связано с Загрузить контексты. Отказ Когда вы используете один из методов «из» (AssembloadFrom или appdomain.createinstancefromandunwrap), если сборка найден в одном из нормальных путей нагрузки (ApplicationBase или один из путей зондирования), то он загружен в по умолчанию. Контекст нагрузки. Если сборка там не найдена, то она загружена в нагрузку - от контекста. Таким образом, когда оба appodomains имеют соответствующую прикладной панель, то даже хотя мы используем метод «из», они оба загружаются в контекст нагрузки на по умолчанию соответствующую настройку. Но когда ApplicationBase различны, то один appdomain будет иметь сборку в его контексте нагрузки по умолчанию, а другой имеет сборку в ее нагрузке.
Это комментарий @russellmcclure, но, как это сложно для комментариев, я публикую это как ответ:
Я внутри приложения ASP.NET и отключение тени-копирования (которая также решила проблему) на самом деле не является опцией, но я нашел следующее решение:
AppDomainSetup adSetup = new AppDomainSetup();
if (AppDomain.CurrentDomain.SetupInformation.ShadowCopyFiles == "true")
{
var shadowCopyDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
if (shadowCopyDir.Contains("assembly"))
shadowCopyDir = shadowCopyDir.Substring(0, shadowCopyDir.LastIndexOf("assembly"));
var privatePaths = new List<string>();
foreach (var dll in Directory.GetFiles(AppDomain.CurrentDomain.SetupInformation.PrivateBinPath, "*.dll"))
{
var shadowPath = Directory.GetFiles(shadowCopyDir, Path.GetFileName(dll), SearchOption.AllDirectories).FirstOrDefault();
if (!String.IsNullOrWhiteSpace(shadowPath))
privatePaths.Add(Path.GetDirectoryName(shadowPath));
}
adSetup.ApplicationBase = shadowCopyDir;
adSetup.PrivateBinPath = String.Join(";", privatePaths);
}
else
{
adSetup.ApplicationBase = AppDomain.CurrentDomain.SetupInformation.ApplicationBase;
adSetup.PrivateBinPath = AppDomain.CurrentDomain.SetupInformation.PrivateBinPath;
}
Это будет использовать каталог Shadow-Copy из основного домена приложения в качестве приложения-базы и добавить все теневые скопированные сборки на закрытый путь, если включена тень-копия.
Если кто-то имеет лучший способ сделать это, пожалуйста, скажите мне.