Cross AppDomain Exception сериализация - PullRequest
4 голосов
/ 05 марта 2010

Учитывая два домена приложения: в первом загружаются Library1 и CommonLibrary. Во второй загружены Library2 и CommonLibrary.

Библиотека2 определяет исключение Library2Exception, которое наследуется от CommonException (определенного в CommonLibrary). Когда я вызываю в первом AppDomain метод для MarshallByRef второго AppDomain, который генерирует исключение Library2Exception, возникает исключение SerializationException.

Действительно, .Net пытается десериализовать Library2Exception, но этот тип определен в Library2, который не найден в первом AppDomain. Я хочу, чтобы он стал CommonException, с которым я могу справиться.

Итак, мои вопросы:

  • Как мы можем контролировать сериализацию между AppDomain , как SerializationBinder , который будет работать на BinaryFormatter ?
  • Возможно ли иметь исключение ByRef вместо ByValue (сериализовано)?

Ответы [ 3 ]

3 голосов
/ 05 марта 2010

Я нашел!Переопределите 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);
    }
  }
2 голосов
/ 05 марта 2010

Вы должны либо загрузить Library2 в первый домен приложения, либо вы должны выдать какое-то исключение, определенное в CommonLibrary.

P.S. Исключения генерируются по ссылке (внутри одного домена приложения), поскольку они являются ссылочными типами, но они генерируются «по значению» между различными доменами приложения (поскольку они не являются потомками MarshalByRef), и вы не можете изменить это поведение. Cosider:

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

P.S.S. Вы можете использовать суррогаты сериализации или что-то в этом роде для решения вашей проблемы, но я думаю, что это намного чище и проще явно бросить тип общего исключения.

0 голосов
/ 05 марта 2010

Вот пример связывателя сериализации, как вы просили, эта подпрограмма представляет собой пользовательский «Serialize» с «SerializationBinder», используемым в качестве его параметра

// ... 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;
    }
}

И вызывается так:

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

Что происходит, когда экземпляр BarBinder создается и присваивается свойству BinderFormatter, поскольку сериализованные данные имеют имя типа Foo.SomeClass, мы применили BarBinder, переименовав имя типа в FooBar.SomeClass 'эффективно сделать данные принадлежащими другому типу ...

...