2013-10-17 67 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

相關問題