Как передать ссылки в качестве параметров метода через домены приложений? - PullRequest
8 голосов
/ 29 мая 2010

Я пытался заставить работать следующий код (все определено в одной сборке):

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» я собираюсь вызвать «remoteObj.GetSomeData ()». Это должно быть сделано, потому что метод 'byte []' из метода 'GetSomeData' должен быть 'вычислен' в первом домене приложения. Что я должен сделать, чтобы избежать исключения, или что я могу сделать, чтобы достичь того же результата?

Ответы [ 3 ]

11 голосов
/ 19 октября 2011

Фактическая причина была в том, что ваша dll загружалась из разных мест в двух разных доменах приложения. Это заставляет .NET думать, что это разные сборки, что, конечно, означает, что типы разные (даже если у них одинаковое имя класса, пространство имен и т. Д.).

Причина, по которой тест Джеффа не прошел при запуске через инфраструктуру модульного тестирования, заключается в том, что платформы модульного тестирования обычно создают домены приложений с ShadowCopy, установленным в значение "true". Но ваш созданный вручную AppDomain по умолчанию будет ShadowCopy = "false". Это может привести к загрузке DLL-файлов из разных мест, что приводит к хорошему «тип объекта не может быть преобразован в целевой тип». ошибка.

ОБНОВЛЕНИЕ: После дальнейшего тестирования кажется, что база данных различается между двумя доменами приложений. Если они совпадают, то приведенный выше сценарий работает. Если они отличаются, это не так (даже если я подтвердил, что dll загружен в оба AppDomains из одного и того же каталога с помощью windbg) Также, если я включу ShadowCopy = "true" в обоих моих AppDomains, то произойдет сбой с другим сообщением: «System.InvalidCastException: объект должен реализовывать IConvertible».

ОБНОВЛЕНИЕ2: Дальнейшее чтение заставляет меня поверить, что оно связано с Загрузка контекстов . При использовании одного из методов «From» (Assembly.LoadFrom или appDomain.CreateInstanceFromAndUnwrap), если сборка находится в одном из обычных путей загрузки (ApplicationBase или один из путей поиска), она загружается в Default Загрузить контекст. Если сборка там не найдена, она загружается в контекст загрузки. Поэтому, когда оба AppDomains имеют совпадающие ApplicationBase, тогда, даже если мы используем метод «From», они оба загружаются в соответствующий контекст загрузки соответствующего AppDomain по умолчанию. Но если база приложения отличается, то один домен приложения будет иметь сборку в контексте загрузки по умолчанию, а другой - в контексте загрузки из нее.

2 голосов
/ 29 мая 2010

Я могу продублировать проблему, и, похоже, она связана с TestDriven.net и / или xUnit.net. Если я запускаю C.Init () в качестве тестового метода, я получаю то же сообщение об ошибке. Однако, если я запускаю C.Init () из консольного приложения, я не получаю исключение.

Видите ли вы то же самое, запустив C.Init () из модульного теста?

Редактировать: я также могу продублировать проблему, используя NUnit и TestDriven.net. Я также могу продублировать ошибку, используя NUnit Runner вместо TestDriven.net. Таким образом, проблема, похоже, связана с выполнением этого кода через среду тестирования, хотя я не уверен, почему.

0 голосов
/ 27 июля 2014

Это комментарий к @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;
}

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

Если у кого-то есть лучший способ сделать это, пожалуйста, скажите мне.

...