Rebus, исключение при создании AppDomain / Instance из async Handler

У нас возникла проблема с новой (асинхронной) версией Rebus, которой не было в старой версии.

При обработке сообщения rebus и попытке создать AppDomain и Instance для динамического запуска кода плагина он всегда выдает исключение. Чтобы сделать пример максимально простым, я сделал тестовый метод:

public static void Test()
{
    AppDomain ad = AppDomain.CreateDomain("Test");
    Loader loader = (Loader)ad.CreateInstanceAndUnwrap(typeof(Loader).Assembly.FullName, typeof(Loader).FullName);
}

class Loader : MarshalByRefObject
{
}

Когда я вызываю метод из «нормального» кода, он работает, но когда я вызываю его из (асинхронного) метода обработки сообщения Rebus, он выдает исключение:

Обнаружено исключение System.Runtime.Serialization.SerializationException
HResult = -2146233076 Сообщение = Тип Rebus.Transport.DefaultTransactionContext в сборке Rebus, Версия = 1.0.0.0, Культура = нейтральный, PublicKeyToken = null не помечен как сериализуемый . Source = mscorlib StackTrace: в System.AppDomain.CreateInstanceAndUnwrap (String assemblyName, String typeName) в App.Bus.MessageParse.Process.Test () в d: \ Project \ App.Bus.MessageParser \ Process.cs: строка 45 в приложении .Bus.MessageParse.Process.d__0.MoveNext () в d: \ Project \ App.Bus.MessageParser \ Process.cs: строка 28 InnerException:

Есть идеи о проблеме?


person Tommi Pitkälä    schedule 01.12.2015    source источник


Ответы (1)


Rebus хранит свой контекст транзакции в AmbientTransactionContext.Current, который поддерживается контекстом логического вызова потока, который автоматически передается в продолжения, когда вы что-то await.

Очевидно, он также переходит в созданные домены приложений;)

Я мог бы пометить DefaultTransactionContext как сериализуемый, но боюсь, что тогда вы просто получите исключение, сообщающее, что элементы в словаре контекста транзакции не сериализуемы.

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

Что-то вроде следующего должно помочь:

public async Task Handle(SomeMessage message)
{
    var transactionContext = AmbientTransactionContext.Current;
    AmbientTransactionContext.Current = null;
    try
    {
        JuggleWithAppDomainsInHere();
    }
    finally
    {
        AmbientTransactionContext.Current = transactionContext;
    }
}

Если в вашем приложении распространена практика, когда вы делаете что-то с доменами приложений, я предлагаю вам обернуть «удаление-и-восстановление-из-окружающей-Ребуса-транзакции» во что-то IDisposable, чтобы вы могли

using(new DismantleAmbientRebusStuff())
{
    JuggleWithAppDomainsInHere();
}
person mookid8000    schedule 01.12.2015