2016-07-28 9 views
2

Ich bin sicher, dass die Ursache für mein Problem ist, diese ähnliche Frage Kreuzung:Objekttyp kann nicht konvertiert werden Typ Ziel, wenn AppDomains

How do I pass references as method parameters across AppDomains?

und insbesondere this answer, aber ich bin nicht ganz sicher, wie man es repariert.

Das Problem, das ich habe, ist dies, ich habe eine Visual Studio-Erweiterung, die ich schreibe und eines der Dinge, die es in der Lage ist zu tun ist, lädt eine Baugruppe in eine separate Domäne (so kann ich es ablegen, wenn ich bin fertig damit und mache ein paar Sachen damit. Also ich habe meine Domain wie folgt aufgebaut:

// The actual ApplicationBase of the current domain will be the one of VS and not of my plugin 
// We need our new AppDomain to be able to find our assemblies 
var p = System.IO.Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); 
var setup = new AppDomainSetup() 
{ 
    ApplicationBase = p 
}; 
domain = AppDomain.CreateDomain("MyTest_AppDomain", AppDomain.CurrentDomain.Evidence, setup); 
var t = typeof(Proxy); 
proxy = domain.CreateInstanceAndUnwrap(t.Assembly.FullName, 
    t.FullName, 
    false, 
    BindingFlags.Default, 
    null, 
    new object[] { assemblyPath }, 
    null, 
    null) as Proxy; 

ich die ApplicationBase setzen, die für sie in der Lage sein, meine Klasse zu laden Proxy obwohl sie in derselben Anordnung wie der obigen Code definiert ist. Ohne dass ich war immer FileNotFoundExceptions

Nun meine Proxy Klasse etwas wie folgt aussieht:

public class Proxy : MarshalByRefObject 
{ 
    private Assembly _assembly; 

    private IEnumerable<Type> _types; 

    public IEnumerable<Type> Types 
    { 
     get 
     { 
      if (_types == null && _assembly != null) 
      { 
       _spiders = _assembly.GetTypes().Where(t => typeof(IFoo).IsAssignableFrom(t)).ToList(); 
      } 
      return _types; 
     } 
    } 

    public Proxy(string assemblyPath) 
    { 
     _assembly = Assembly.LoadFile(assemblyPath); 
    } 

    public IFoo GetInstanceOf(string fooType) 
    { 
     var type = Types.FirstOrDefault(t => t.FullName == fooType); 
     if (type == null) 
     { 
      throw new InvalidOperationException($"Unknown type {fooType} in assembly {_assembly.FullName}"); 
     } 
     return GetInstanceOf(type); 
    } 
    private IFoo GetInstanceOf(Type fooType) 
    { 
     var obj = Activator.CreateInstance(fooType); 
     return obj as IFoo; 
    } 

    public override object InitializeLifetimeService() 
    { 
     return null; 
    } 
} 

Zurück in der gleichen Klasse, die die neuen AppDomain und die Instanz von Proxy in den neuen AppDomain habe ich etwas wie dies:

public IFoo GetInstanceOf(Type type) 
{ 
    var name = type.FullName; 
    var foo = proxy.GetInstanceOf(name); 
    return foo; 
} 

Zunächst versuchte ich proxy.GetInstanceOf Bestehen der Art direkt zu nennen, aber das hat nicht funktioniert wahrscheinlich fo Aus demselben Grund wie der Code, den ich habe, funktioniert nicht. Als ich in der Linie var foo = proxy.GetInstanceOf... Linie erhalten erhalte ich ein ArgumentException mit der Meldung:

Objekttypen nicht Typ Ziel umgewandelt werden kann.

Ich glaube, das Problem ist, weil die IFoo in MyTest_AppDomain ist nicht die gleiche wie IFoo in DefaultDomain zu dem Ergebnis, aber ich bin nicht sicher, was der richtige Weg, dies zu beheben.

Es kann relevant sein, dass IFoo selbst in einer separaten dritten Assembly definiert ist, die sowohl mein VSIX-Projekt als auch die Assembly, die ich in einer separaten Domäne laden möchte, referenzieren. Ich muss überzeugen, ich denke, MyTest_AppDomain, um die gleiche IFoo Assembly von der gleichen Stelle wie DefaultDomain zu laden, oder sonst überzeugen, dass sie ein und dasselbe sind.

Hinweis: Alle meine Klassen, die IFoo implementieren, erben von einer abstrakten Basis FooBase, die wiederum von MarshalByRefObject erbt.

bearbeiten mehr über dieses Denken mein Problem könnte von hier kommen:

_assembly = Assembly.LoadFile(assemblyPath); 

Die assemblyPath gibt es einen anderen Weg zum ApplicationBase und dieser Weg hat seine eigene Version von foo.dll ist (wo IFoo definiert ist). Also vielleicht lädt es von dort vor dem Laden von ApplicationBase?In Proxy.GetInstanceOf kann ich typeof(IFoo).Assembly.Location betrachten und es gibt mir den erwarteten Pfad, der in ApplicationBase festgelegt wurde, aber ich glaube nicht, wenn meine Assembly geladen wird, wird es foo.dll von dort bekommen.

+0

Möchten Sie, dass IFoo als Referenz erstellt wird oder erwarten Sie eine neue Kopie in Ihrer Anwendungsdomäne? Sie tun das erste mit MarshalByRef, also gehe ich davon aus, dass Sie eine neue Kopie wollen (aber nur für den Fall fragen). Stellen Sie sicher, dass alles, was von IFoo implementiert wird, serialisierbar ist, da die Instanz serialisiert und deserialisiert werden sollte, wenn sie Anwendungsdomänen kreuzt. – Igor

+0

@Igor: Ich möchte eine Instanz in der neuen App-Domäne erstellen und einen Verweis darauf an die Standarddomäne übergeben, damit ich Methoden aufrufen und einige Ereignisse abonnieren kann. –

+0

Das funktioniert nicht (standardmäßig). Anwendungsdomänen verwenden .Net-Remoting, um miteinander zu kommunizieren. Instanzen, die zwischen Anwendungsdomänen übergeben werden, sind im Wesentlichen Kopien. Wenn Sie "IFoo" zurückgeben, erhalten Sie eine Kopie von "IFoo", keine Referenz auf die in Ihrer anderen Anwendungsdomäne (standardmäßig) erstellte Instanz, so dass Sie keine Ereignisse auf IFoo oder Aufrufmethoden abonnieren und die Instanz in Ihrem erwarten können App-Domäne erstellt, um diese Methoden zu verarbeiten. (mehr zu kommen ...) – Igor

Antwort

1

Ich stelle eine schnelle Arbeitsprobe für Sie zusammen. Dies tut, was Sie erwarten würden, IFoo wird von Ref an den Aufrufer gemarshallt.

using System; 
using System.Reflection; 

namespace AppdomainTesting 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      var p = System.IO.Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); 
      var setup = new AppDomainSetup() 
      { 
       ApplicationBase = p 
      }; 
      IProxy proxy; 
      var domain = AppDomain.CreateDomain("MyTest_AppDomain", AppDomain.CurrentDomain.Evidence, setup); 
       var t = typeof(Proxy); 
      proxy = domain.CreateInstanceAndUnwrap(t.Assembly.FullName, 
       t.FullName, 
       false, 
       BindingFlags.Default, 
       null, 
       null, 
       null, 
       null) as IProxy; 

      // works 
      var typeofFoo = typeof(Foo); 
      var foo = proxy.GetFoo(new TypeDef() { Asembly = typeofFoo.Assembly.FullName, FullTypeName = typeofFoo.FullName }); 
      Console.WriteLine(foo.Domain); 

      // Type also implements Serializable attribute (my mistake, I thought it did not) 
      foo = proxy.GetFoo(typeofFoo); 
      Console.WriteLine(foo.Domain); 

      Console.WriteLine(); 
      Console.WriteLine("ENTER to exit"); 
      Console.ReadLine(); 
     } 
    } 

    public interface IFoo 
    { 
     string Domain { get; } 
    } 

    public class Foo : MarshalByRefObject, IFoo 
    { 
     public string Domain 
     { 
      get { return System.AppDomain.CurrentDomain.FriendlyName; } 
     } 
    } 
    public interface IProxy 
    { 
     IFoo GetFoo(TypeDef typeToGet); 
     IFoo GetFoo(Type type); 
    } 
    [Serializable] 
    public class TypeDef 
    { 
     public string Asembly { get; set; } 
     public string FullTypeName { get; set; } 
    } 

    public class Proxy : MarshalByRefObject, IProxy 
    { 
     public IFoo GetFoo(TypeDef typeToGet) 
     { 
      var type = Type.GetType(typeToGet.FullTypeName + ", " + typeToGet.Asembly, true); 
      var instance = Activator.CreateInstance(type) as IFoo; 
      return instance; 
     } 
     public IFoo GetFoo(Type type) 
     { 
      var instance = Activator.CreateInstance(type) as IFoo; 
      return instance; 
     } 
    } 
} 
Verwandte Themen