2010-05-28 17 views
7

I zostały próbuje uzyskać następujący kod do pracy (wszystko jest zdefiniowana w tym samym zespole):Jak przekazać referencje jako parametry metody w AppDomains?

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." 
    } 
} 

} 

Co staram się zrobić, to przekazać referencję typu „A” przykład utworzony w najpierw AppDomena do domeny podrzędnej i niech domena podrzędna uruchomi metodę w pierwszej domenie. W pewnym punkcie kodu "B" zadzwonię do "remoteObj.GetSomeData()". Należy to zrobić, ponieważ "bajt []" z metody "GetSomeData" musi być "obliczony" na pierwszym appdomain. Co należy zrobić, aby uniknąć wyjątku lub co mogę zrobić, aby osiągnąć ten sam wynik?

+0

Twój kod działa dla mnie. –

+0

+1 Dla mnie też. Jakie wersje CLR, Visual Studio (jeśli istnieje), C#, itp? Jakieś inne okoliczności? –

+0

Dziwne, mam zamiar ponownie sprawdzić –

Odpowiedz

2

Mogę zduplikować problem i wydaje się być powiązany z TestDriven.net i/lub xUnit.net. Jeśli uruchomię C.Init() jako metodę testową, otrzymam ten sam komunikat o błędzie. Jednak jeśli uruchomię C.Init() z aplikacji konsoli, nie otrzymuję wyjątku.

Czy widzisz to samo, uruchamiając C.Init() z testu jednostkowego?

Edytuj: Jestem również w stanie skopiować ten błąd, używając NUnit i TestDriven.net. Jestem również w stanie zduplikować błąd za pomocą NUnit Runner zamiast TestDriven.net. Problem wydaje się być związany z uruchamianiem tego kodu za pośrednictwem platformy testowej, choć nie jestem pewien dlaczego.

+0

To nie było w ramy testowania, ale pomogło mi dowiedzieć się, co było przyczyną, dzięki. –

+0

Fajnie, co było przyczyną? –

+0

Nie jestem do końca pewien, ale miało to coś wspólnego ze ścieżką uruchamiania aplikacji. –

10

Rzeczywista przyczyna spowodowała, że ​​biblioteka dll została załadowana z różnych lokalizacji w dwóch różnych domenach aplikacji. To powoduje, że .NET uważa, że ​​są to różne zespoły, co oczywiście oznacza, że ​​typy są różne (nawet jeśli mają tę samą nazwę klasy, przestrzeń nazw itp.).

Powód, dla którego test Jeffa zakończył się niepowodzeniem po uruchomieniu frameworka testów jednostkowych, polega na tym, że frameworki testów jednostkowych generalnie tworzą AppDomains z opcją ShadowCopy ustawioną na "true". Ale twoja ręcznie utworzona AppDomain będzie domyślnie ShadowCopy = "false". To spowodowałoby załadowanie bibliotek dll z różnych lokalizacji, co prowadzi do ładnego "Typu obiektu nie można przekonwertować na typ celu". błąd.

AKTUALIZACJA: Po dalszych testach wygląda na to, że ApplicationBase różni się między dwoma AppDomains. Jeśli pasują, to powyższy scenariusz działa. Jeśli są różne, to nie działa (nawet jeśli potwierdziłem, że biblioteka dll jest ładowana do obu AppDomains z tego samego katalogu przy użyciu windbg). Ponadto, jeśli włączę ShadowCopy = "true" w obu moich AppDomains, to zawiedzie z inną wiadomością: "System.InvalidCastException: Object musi implementować IConvertible".

UPDATE2: Dalsze czytanie prowadzi mnie do przekonania, że ​​jest to związane z Load Contexts. Gdy używasz jednej z metod "Od" (Assembly.LoadFrom lub appDomain.CreateInstanceFromAndUnwrap), jeśli zestaw znajduje się w jednej ze ścieżek normalnego ładowania (ApplicationBase lub jedna ze ścieżek sondujących), to jest on ładowany do Default Załaduj kontekst. Jeśli zestaw nie zostanie tam znaleziony, zostanie załadowany do kontekstu Load-From. Więc gdy oba AppDomains mają pasujące biblioteki ApplicationBase, to mimo że używamy metody "From", oba są ładowane do kontekstu obciążenia domyślnego AppDomain. Ale gdy baza aplikacji jest inna, wtedy jedna AppDomena będzie miała złożenie w jej domyślnym kontekście obciążenia, podczas gdy druga ma złożenie w swoim kontekście Load-From.

+0

, ale nawet po ** 'setup.ApplicationBase = AppDomain.CurrentDomain.SetupInformation.ApplicationBase;' ** problem nadal prezentuje ... – Shelest

+0

@Slelest Spróbuj ustawić swoją aplikację na Path.GetDirectoryName ("ścieżka pliku dll") – Anshul

+0

@RussellMcClure : Dzięki za twoje informacje - pomogło mi to rozwiązać to dla mnie. Niech możesz rzucić okiem na moją odpowiedź i przekazać mi swoje przemyślenia na temat mojego rozwiązania, ponieważ nie jest to naprawdę eleganckie IMO ... – ChrFin

0

To jest komentarz do @RussellMcClure ale jak to jest kompleks o komentarz mogę napisać to jako odpowiedź:

jestem wewnątrz ASP.NET aplikacji i wyłączenie shadow-copy (co również rozwiązać problem) tak naprawdę nie jest opcją, ale znalazłem następujące rozwiązanie:

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; 
} 

ten będzie korzystać z katalogu cień kopię głównego app-domeny jako bazę aplikacji i dodaj wszystkie kopie z kopiowaniem w tle do ścieżki prywatnej, jeśli włączone jest kopiowanie w tle.

Jeśli ktoś ma lepszy sposób robienia tego, proszę mi powiedzieć.