【问题标题】:Cross AppDomain Exception serialization跨 AppDomain 异常序列化
【发布时间】:2011-01-24 00:53:52
【问题描述】:

给定两个应用域: 首先,Library1 和 CommonLibrary 被加载。在第二个 Library2 和 CommonLibrary 中被加载。

Library2 定义了一个继承自 CommonException(在 CommonLibrary 中定义)的 Library2Exception。 当我在第一个 AppDomain 中调用第二个 AppDomain 的 MarshallByRef 上引发 Library2Exception 的方法时,会引发 SerializationException。

确实,.Net 尝试反序列化 Library2Exception,但此类型在 Library2 中定义,在第一个 AppDomain 中找不到。我希望它成为我可以处理的 CommonException。

所以,我的问题是:

【问题讨论】:

标签: c# .net exception serialization appdomain


【解决方案1】:

这是您所要求的序列化绑定器的示例,此例程是自定义的“序列化”,其参数为“序列化绑定器”

// ... This is a class object of type Foo...
public bool Serialize(string sPath, System.Runtime.Serialization.SerializationBinder serializationBinder) {
    bool bSuccessful = false;
    if (serializationBinder == null) return false;
    try {
        using (FileStream fStream = new FileStream(sPath, FileMode.Create)) {
            try {
                BinaryFormatter bf = new BinaryFormatter();

                bf.Binder = serializationBinder;

                bf.Serialize(fStream, this._someFoo);
                bSuccessful = true;
            } catch (System.Runtime.Serialization.SerializationException sEx) {
                System.Diagnostics.Debug.WriteLine(sEx.ToString());
                bSuccessful = false;
            }
        }
    } catch (System.IO.IOException ioEx) {
        System.Diagnostics.Debug.WriteLine(string.Format("[Serialize(...)] - IO EXCEPTION> DETAILS ARE {0}", ioEx.ToString()));
        bSuccessful = false;
    }
    return bSuccessful;
}

public bool Deserialize(string sFileName, System.Runtime.Serialization.SerializationBinder serializationBinder) {
    bool bSuccessful = false;
    //
    if (!System.IO.File.Exists(sFileName)) return false;
    if (serializationBinder == null) return false;
    this._foo = new Foo();
    //
    try {
        using (FileStream fStream = new FileStream(sFileName, FileMode.Open)) {
            try {
                BinaryFormatter bf = new BinaryFormatter();
                bf.Binder = serializationBinder;
                this._foo = (Foo)bf.Deserialize(fStream);
                bSuccessful = true;
            } catch (System.Runtime.Serialization.SerializationException sEx) {
                System.Diagnostics.Debug.WriteLine(string.Format("[DeSerialize(...)] - SERIALIZATION EXCEPTION> DETAILS ARE {0}", sEx.ToString()));
                bSuccessful = false;
            }
        }
    } catch (System.IO.IOException ioEx) {
        System.Diagnostics.Debug.WriteLine(string.Format("[DeSerialize(...)] - IO EXCEPTION> DETAILS ARE {0}", ioEx.ToString()));
        bSuccessful = false;
    }
    return (bSuccessful == true);
}


// End class method for object class type Foo

public class BarBinder : System.Runtime.Serialization.SerializationBinder {
    public override Type BindToType(string assemblyName, string typeName) {
        Type typeToDeserialize = null;
        try {

            // For each assemblyName/typeName that you want to deserialize to
            // a different type, set typeToDeserialize to the desired type.
            string assemVer1 = System.Reflection.Assembly.GetExecutingAssembly().FullName;

            if (assemblyName.StartsWith("Foo")) {
                assemblyName = assemVer1;
                typeName = "FooBar" + typeName.Substring(typeName.LastIndexOf("."), (typeName.Length - typeName.LastIndexOf(".")));
            }
            typeToDeserialize = Type.GetType(String.Format("{0}, {1}", typeName, assemblyName));
        } catch (System.Exception ex1) {
            throw ex1;
        } finally {
        }
        return typeToDeserialize;
    }
}

And 被这样调用:

_foo.DeSerialize(@"C:\foo.dat", new BarBinder());

当 'BarBinder' 被实例化并分配给 BinaryFormatter 的 Binder 属性时会发生什么,因为序列化的数据具有类型名称 Foo.SomeClass,我们应用了 'BarBinder',将类型名称重命名为 'FooBar.SomeClass ' 有效地使数据属于另一种类型...

【讨论】:

  • 谢谢,但 BinarySerializer 无法解决我的 AppDomain 问题。
【解决方案2】:

我找到了!覆盖 GetObjectData 以更改异常类型:

  [Serializable]
  public class CommonException : Exception
  {
    public CommonException() { }
    public CommonException(string message)
     : base(message) { }
    public CommonException(string message, Exception inner)
     : base(message, inner) { }
    protected CommonException(
    SerializationInfo info,
    StreamingContext context)
      : base(info, context)
    { }

    public override void GetObjectData(
    SerializationInfo info,
    StreamingContext context)
    {
      if (context.State == StreamingContextStates.CrossAppDomain)
        info.SetType(typeof(CommonException));
      base.GetObjectData(info, context);
    }
  }

【讨论】:

  • 我真的不认为这是个好主意。它违反了异常处理策略取决于异常类型的所有异常处理原则。在您的情况下,您将所有异常类型切片为 CommonException。在这种情况下,抛出 CommonException 并添加包含所有适当信息的消息要容易得多。
  • 我的 Library1 异常处理无法了解 Library2 异常类型...只有 Library1 和 Common 异常类型。
  • 在这种情况下,只需从 Library2 中抛出 CommonException(因为在任何情况下您都无法正确处理所有其他异常)。
  • 如果我可以处理 CommonException,我不明白为什么我不能正确处理继承 CommonException 的异常。如果是这样,异常根本不应该继承 CommonException。
  • Sergey 的观点是,如果您要在膝盖上削减异常类,为什么要使用派生异常类呢?不要设计一个需要晦涩或奇异的技巧来产生简单结果的系统,只需从简单的操作开始。换句话说,如果你想让 Lib1 处理 Lib2 抛出的异常,那么你应该让 Lib2 抛出一个与 Lib1 兼容的异常类型——也就是说,在公共 DLL 中定义所有此类异常类型,并且只抛出公共异常。
【解决方案3】:

您应该在第一个应用程序域中加载 Library2,或者您应该抛出一些在 CommonLibrary 中定义的异常。

附:引用抛出的异常(在一个应用程序域内),因为它们是引用类型,但它们是在不同的应用程序域之间“按值”抛出的(因为它们不是 MarshalByRef 后代),并且您无法更改此行为。考司:

//Oops! I can't do that!
public class MyException : Exception, MarshalByRef
{
}

附言您可以使用序列化代理或类似的东西来解决您的问题,但我认为它更清晰、更容易显式抛出常见异常类型。

【讨论】:

  • 感谢您的回答,但是,我将如何使用序列化代理?如何注册?
  • 考虑 Jeffrey Richter 的文章运行时序列化,第 3 部分:msdn.microsoft.com/en-us/magazine/cc188950.aspx。但我真的不确定这是一个好的解决方案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-07-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多