0

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

这些处理程序可以接收(有一个接收数据包并返回一个数据包的方法)数据包,它们都实现了相同的接口(比如说IPacket)并返回一个答案(也是一个IPacket)。

我接收到这些数据包(通过 tcp 连接),而我的程序并不熟悉特定的类(虽然它是一个已知的接口 - IPacket,但不同的实现)。

因此,当我尝试反序列化一个数据包(将其交给处理程序)时,我得到了一个异常。

  • 我序列化成字节反序列化回对象(使用二进制序列化程序)*

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

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

但它没有工作..

我假设我需要找到一种方法在运行时添加对这些 dll 的引用,然后我的程序将识别它们..(我认为也许在包类上使用 Export(typeof()..) 会有所帮助,会它?)

尝试反序列化时遇到的异常是找不到类名..

*我已经编辑了主题,希望它更清楚一点,谢谢=]


  • 编辑:

我并不是说这可以用 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 吗?

此外,非常感谢您对代码的解释。

4

2 回答 2

1

MEF 肯定会帮助您,但不仅限于此。您必须使用ISerializationSurrogate您可以在此处找到以下内容的大部分解释。

因此,给定数据包接口的以下定义:

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

以及匹配的实现,在定义具体数据包的同一个程序集中:

[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());
    }
}

产生:

数据包信息:姓名:Joe

数据包信息:测量:42.42

于 2013-10-18T00:19:02.763 回答
0

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

于 2013-10-18T00:02:48.090 回答