2013-10-17 45 views
0

我有我动态加载的类(使用mef),它们是某种处理程序(这些工作正常)。加载未知类型

这些处理程序可以接收(有一种方法可以接收数据包并返回一个数据包),这些数据包都实现相同的接口(比如IPacket)并返回一个答案(IPacket)。我通过tcp连接接收这些数据包,我的程序不熟悉特定的类(虽然它是一个已知的接口 - IPacket,但是不同的实现)。

所以,当我试图反序列化一个数据包(把它交给处理程序)时,我得到一个异常。

  • 我串行化为字节的反序列化回对象(使用二进制序列)*

我可以访问该数据包的实现应该是动态的,唯一的方法,因为DLL被存储在文件夹中我可以访问。

我认为我可以使用Assembly.LoadFrom来熟悉我的程序和数据包,因为我甚至不需要安装它们,只需反序列化(获取接口的一个实例)并交给处理程序,然后将返回一个答案,我会再次发送。

但它没有工作..

我认为我需要找到一种方法,在运行时,然后我的程序会识别它们添加到这些DLL的参考。(我想,也许使用出口(typeof运算()..)对包类要帮忙的,不是吗?)

尝试反序列化的是,类名没有找到时,我得到的例外..

* I编辑了这个话题,我希望它更清楚一点,谢谢=]


  • 编辑:

我不是说这是可解的MEF的,我只是虽然它可能是。 这绝对可以用反射来解决。我有一个文件夹,其中包含我希望我的程序在运行时识别的所有类,我只需要在运行时“加载”它们,就好像我已经在同一个文件夹中添加了对dll的引用。

所以基本上我需要做的是:

从文件夹中加载某个接口(IPacket在这个例子中)所有的实现。我不需要实例化它们,但只需将它们作为变量接收,而不会收到这种类型不在我的项目中的异常。


所以我发现这个片断:

static constructor() { 
    AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve); 

}

static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args) { 
    Assembly ayResult = null; 
    string sShortAssemblyName = args.Name.Split(',')[0]; 
    Assembly[] ayAssemblies = AppDomain.CurrentDomain.GetAssemblies(); 
    foreach (Assembly ayAssembly in ayAssemblies) { 
     if (sShortAssemblyName == ayAssembly.FullName.Split(',')[0]) { 
      ayResult = ayAssembly; 
      break; 
     } 
    } 
    return ayResult; 
} 

这似乎是接近我所期待的,但我真的不明白这一点。 有没有办法中和这个,以便它只加载某个文件夹中的dll? 我的程序会不会熟悉dll?

此外,代码的解释将不胜感激。

+6

你正在使用什么解串器?你有什么确切的例外? –

+1

我编辑了你的标题。请参阅:“[应该在其标题中包含”标签“](http://meta.stackexchange.com/questions/19190/)”,其中的共识是“不,他们不应该”。 –

+0

你能多解释一下吗?您正在导出该类型,但未由MEF加载或发生了什么? –

回答

0

看看this example,特别是BindToType的方法。我想你可以检查程序集是否被加载。如果不是,则使用反射(或MEF,如果您愿意)加载它。然后简单地返回base.BindToType,它应该工作。 (除非两个机器之间的组装版本不同,那么您可能需要自己找到类型。)

1

MEF肯定会帮助您,但不仅如此。您必须使用ISerializationSurrogate。下面的大部分解释可以找到here

所以,给出了数据包的接口定义如下:

public interface IPacket 
{ 
    string GetInfo(); 
} 

有以下几种实现方式,居住在自己的装配:

[Export(typeof(IPacket))] 
class FirstPacket : IPacket 
{ 
    public FirstPacket() 
    { 
     Name = "Joe"; 
    } 

    public string Name { get; set; } 

    public string GetInfo() 
    { 
     return "Name: " + Name; 
    } 
} 

[Export(typeof(IPacket))] 
class SecondPacket : IPacket 
{ 
    public SecondPacket() 
    { 
     Measurement = 42.42m; 
    } 

    public decimal Measurement { get; set; } 

    public string GetInfo() 
    { 
     return "Measurement: " + Measurement; 
    } 
} 

现在,我们将定义另一个界面,有点像:

public interface IPacketSurrogateProvider 
{ 
    void AddSurrogate(SurrogateSelector toSelector); 
} 

而匹配的实现,在相同的a ssembly其中混凝土分组被定义为:

[Export(typeof(IPacketSurrogateProvider))] 
class FirstPacketSurrogateProvider : IPacketSurrogateProvider, ISerializationSurrogate 
{ 
    public void AddSurrogate(SurrogateSelector toSelector) 
    { 
     toSelector.AddSurrogate(typeof(FirstPacket), new StreamingContext(StreamingContextStates.All), this); 
    } 

    public void GetObjectData(object obj, SerializationInfo info, StreamingContext context) 
    { 
     info.AddValue("Name", ((FirstPacket)obj).Name); 
    } 

    public object SetObjectData(object obj, SerializationInfo info, StreamingContext context, ISurrogateSelector selector) 
    { 
     ((FirstPacket)obj).Name = info.GetString("Name"); 

     return obj; 
    } 
} 

[Export(typeof(IPacketSurrogateProvider))] 
class SecondPacketSurrogateProvider : IPacketSurrogateProvider, ISerializationSurrogate 
{ 
    public void AddSurrogate(SurrogateSelector toSelector) 
    { 
     toSelector.AddSurrogate(typeof(SecondPacket), new StreamingContext(StreamingContextStates.All), this); 
    } 

    public void GetObjectData(object obj, SerializationInfo info, StreamingContext context) 
    { 
     info.AddValue("Measurement", ((SecondPacket)obj).Measurement); 
    } 

    public object SetObjectData(object obj, SerializationInfo info, StreamingContext context, ISurrogateSelector selector) 
    { 
     ((SecondPacket)obj).Measurement = info.GetDecimal("Measurement"); 

     return obj; 
    } 
} 

而现在,在其中确实有参考一个与所述接口的组件,但不与一个与所述实施方式中,与具有相同的部署文件夹作为两者的以上:

public static void Test() 
{ 
    var container = new CompositionContainer(new DirectoryCatalog(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location))); 

    var packets = container.GetExportedValues<IPacket>().ToArray(); 
    var packetSurrogateProviders = container.GetExportedValues<IPacketSurrogateProvider>(); 

    var surrogateSelector = new SurrogateSelector(); 
    foreach (var provider in packetSurrogateProviders) 
    { 
     provider.AddSurrogate(surrogateSelector); 
    } 

    var deserializedPackets = new IPacket[] { }; 
    using (var stream = new MemoryStream()) 
    { 
     var formatter = new BinaryFormatter {SurrogateSelector = surrogateSelector}; 

     formatter.Serialize(stream, packets); 

     stream.Position = 0; 

     deserializedPackets = (IPacket[])formatter.Deserialize(stream); 
    } 

    foreach (var packet in deserializedPackets) 
    { 
     Console.WriteLine("Packet info: {0}", packet.GetInfo()); 
    } 
} 

主要生产:

包信息:名称:乔

包信息:测量:42.42

+0

谢谢你的回答,但我有一些问题。 – user2599803

+0

谢谢你的回答,但我有一些问题。 为什么我需要代孕班?我可以使用mef将所有IPacket加载到IPacket列表中,而不用它,我很确定我的程序会识别它们。 我只是认为,因为我实际上并不需要它们在列表中,我只是想让我的程序在运行时以某种方式识别它们... 在我看来,它应该更简单,我有路径并且可以加载程序集,它不需要这么多的努力.. – user2599803

+0

您必须明白,导出/在我的示例中通过MEF加载数据包仅仅是为了方便。我也可以从自己的程序集中序列化数据包,然后在接收端进行反序列化,而无需添加引用。您的问题不是识别该类型,因为据我了解,您拥有IPacket接口就足够了。你的问题是让BinaryFormatter识别类型,这就是代理的用途。另一个问题是为什么你不使用WCF进行通信? – galenus