Вопрос по remoting, appdomain, c#, marshalbyrefobject, .net – Как передать ссылки в качестве параметров метода через домены приложений?

8

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

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' должен быть 'вычислен' в первом домене приложения. Что я должен сделать, чтобы избежать исключения, или что я могу сделать, чтобы достичь того же результата?

странно, я собираюсь проверить еще раз Thiago de Arruda
Ваш код работает для меня. Mark Byers
+1 Для меня тоже. Какие версии CLR, Visual Studio (если есть), C # и т. Д.? Любые другие обстоятельства? Fyodor Soikin
Я могу продублировать ошибку в .NET 4 Jeff Schumacher

Ваш Ответ

3   ответа
11

Основной причиной было то, что ваша 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 по умолчанию. Но если база приложения отличается, то один домен приложения будет иметь сборку в контексте загрузки по умолчанию, а другой - сборку в контексте загрузки.

но даже послеsetup.ApplicationBase = AppDomain.CurrentDomain.SetupInformation.ApplicationBase; выпуск до сих пор дарит ... Shelest
@RussellMcClure: Я хотел бы поблагодарить вас за ваше объяснение! Действительно, загрузка контекстов - вот почему .NET может думать, что одна и та же сборка отличается. Я бы порекомендовал посмотреть путь к загруженным модулям с помощью окна «Модули» в Visual Studio для устранения проблем. Если вы видите сборку, загруженную в контексте Default и Load-From, это может вызвать проблемы. Arxo Clay
@Shelest Попробуйте установить в ApplicationBase значение Path.GetDirectoryName («путь к файлу dll»). Anshul
Вы можете создать экземпляр объекта в дочернем домене приложения, указав точный путь к загруженной в данный момент сборке для типа объекта в вашемActivator.CreateInstanceFrom() вызов. Этот путь можно получить черезtypeof(MyMarshalByRefObjType).Assembly.ManifestModule.FullyQualifiedName. ryanoshea
@RussellMcClure: Спасибо за вашу информацию - это помогло мне решить это для меня. Можете ли вы взглянуть на мой ответ и высказать свое мнение о моем решении, поскольку оно не очень элегантное ИМО ... ChrFin
0

но поскольку он сложный для комментария, я публикую это как ответ:

Я нахожусь внутри приложения 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;
}

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

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

2

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

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

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

Это не было в рамках тестирования, но это помогло мне понять, что было причиной, спасибо. Thiago de Arruda
Я не совсем уверен, но это было связано с путем запуска приложения Thiago de Arruda
@Jeff: Я добавляю этот комментарий, чтобы "пинговать" тебя, так как я на несколько месяцев опоздал на эту вечеринку. Мне было бы интересно узнать, согласны ли вы с моим ответом. Russell McClure
Круто, что было причиной? Jeff Schumacher

Похожие вопросы