ContextBoundObject抛出远程错误后等待 [英] ContextBoundObject Throws a Remoting Error After Await

查看:206
本文介绍了ContextBoundObject抛出远程错误后等待的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有写拦截方法,使用ContextBoundObject S和ContextAttribute调用一些日志记录code。在code是基于code项目的样本

I have some logging code that was written to intercept method calls using ContextBoundObject s and a ContextAttribute. The code is based on a Code Project sample.

这一切工作得很好,直到我们开始使用这个库code,利用异步和等待。现在,我们运行code远程处理时出错。下面是再现问题一个简单的例子:

This all worked fine until we started using this library with code that leverages async and await. Now we get remoting errors when running the code. Here is a simple example that reproduces the issue:

public class OhMyAttribute : ContextAttribute
{
    public OhMyAttribute() : base("OhMy")
    {
    }
}

[OhMy]
public class Class1 : ContextBoundObject
{
    private string one = "1";
    public async Task Method1()
    {
        Console.WriteLine(one);
        await Task.Delay(50);
        Console.WriteLine(one);
    }
}

当我们调用方法1 我们得到以下 RemotingException 第二 Console.WriteLine

When we invoke Method1 we get the following RemotingException on the second Console.WriteLine:

Remoting cannot find field 'one' on type 'WindowsFormsApplication1.Class1'.

有没有什么办法来解决这个问题,使用内置的C#方法,还是我们来看看像PostSharp的替代解决方案?

Is there any way to get around this problem using built in C# methods or do we have to look at an alternative solution like PostSharp?

推荐答案

下面是一个更一般的解决方法。

Here is a more general workaround.

它有以下不足:


  • 它不支持更改的SynchronizationContext ContextBoundObject 。它将罚球在这种情况下。

  • 它不支持使用的情况下,等待 SynchronizationContext.Current 为空 TaskScheduler.Current 不是 TaskScheduler.Default 。在这种情况下,通常等待将捕获的的TaskScheduler 并用它来发布工作的其余部分,但由于这解决方案设置了的SynchronizationContext 的TaskScheduler 不会被捕获。因此,当检测到这种情况,它会罚球

  • 它不支持使用 .ConfigureAwait(假)因为这将导致的SynchronizationContext 不被捕获。不幸的是,我无法检测到这种情况。但是,如果用户想获得 .ConfigureAwait(假)像行为底层直通的SynchronizationContext ,他们可以使用自定义awaiter(见<一href=\"http://stackoverflow.com/a/22417031/495262\">http://stackoverflow.com/a/22417031/495262).

  • It does not support changing the SynchronizationContext within the ContextBoundObject. It will throw in that case.
  • It does not support the case of using await when SynchronizationContext.Current is null and the TaskScheduler.Current is not the TaskScheduler.Default. In this scenario, normally await would capture the TaskScheduler and use that to post the remainder of the work, but since this solution sets the SynchronizationContext the TaskScheduler would not be captured. Thus, when this situation is detected, it will throw.
  • It does not support using .ConfigureAwait(false) since that will cause the SynchronizationContext to not be captured. Unfortunately, I could not detect this case. However, if the user does want to get .ConfigureAwait(false) like behavior for the underlying pass-through SynchronizationContext, they can use a custom awaiter (see http://stackoverflow.com/a/22417031/495262).

一个有趣的事情是,我试图创建一个通过的SynchronizationContext 。也就是说,我不希望覆盖任何现有的的SynchronizationContext ,而是保留其行为和层在它上面做的工作在适​​当的范围内的行为。一个更好的做法有任何意见都欢迎。

One interesting thing here is that I've attempted to create a "pass through" SynchronizationContext. That is, I didn't want to overwrite any existing SynchronizationContext, but rather retain its behavior and layer on top of it the behavior of doing the work in the proper context. Any comments on a better approach are welcome.

    using System;
    using System.Runtime.Remoting.Activation;
    using System.Runtime.Remoting.Contexts;
    using System.Runtime.Remoting.Messaging;
    using System.Threading;
    using System.Threading.Tasks;

    namespace ConsoleApplication1
    {
        class Program
        {
            static void Main(string[] args)
            {
                var c1 = new Class1();
                var t = c1.Method1();
                Func<Task> f = c1.Method1;
                f.BeginInvoke(null, null);

                Console.ReadKey();
            }
        }

        [MyContext]
        public class Class1 : ContextBoundObject
        {
            private string one = "1";
            public async Task Method1()
            {
                Console.WriteLine(one);
                await Task.Delay(50);
                Console.WriteLine(one);
            }
        }

        sealed class MyContextAttribute : ContextAttribute
        {
            public MyContextAttribute()
                : base("My")
            {
            }

            public override void GetPropertiesForNewContext(IConstructionCallMessage ctorMsg)
            {
                if (ctorMsg == null)
                    throw new ArgumentNullException("ctorMsg");

                ctorMsg.ContextProperties.Add(new ContributeInstallContextSynchronizationContextMessageSink());
            }

            public override bool IsContextOK(Context ctx, IConstructionCallMessage ctorMsg)
            {
                return false;
            }
        }

        sealed class ContributeInstallContextSynchronizationContextMessageSink : IContextProperty, IContributeServerContextSink
        {
            public ContributeInstallContextSynchronizationContextMessageSink()
            {
            }

            public IMessageSink GetServerContextSink(IMessageSink nextSink)
            {
                return new InstallContextSynchronizationContextMessageSink(nextSink);
            }

            public string Name { get { return "ContributeInstallContextSynchronizationContextMessageSink"; } }

            public bool IsNewContextOK(Context ctx)
            {
                return true;
            }

            public void Freeze(Context ctx)
            {
            }
        }

        sealed class InstallContextSynchronizationContextMessageSink : IMessageSink
        {
            readonly IMessageSink m_NextSink;

            public InstallContextSynchronizationContextMessageSink(IMessageSink nextSink)
            {
                m_NextSink = nextSink;
            }

            public IMessageSink NextSink
            {
                get { return m_NextSink; }
            }

            public IMessageCtrl AsyncProcessMessage(IMessage msg, IMessageSink replySink)
            {
                var contextSyncContext = new ContextSynchronizationContext(SynchronizationContext.Current);
                var syncContextReplacer = new SynchronizationContextReplacer(contextSyncContext);

                DelegateMessageSink.SyncProcessMessageDelegate replySyncDelegate = (n, m) => SyncProcessMessageDelegateForAsyncReply(n, m, syncContextReplacer);
                var newReplySink = new DelegateMessageSink(replySink, replySyncDelegate, null);
                return m_NextSink.AsyncProcessMessage(msg, newReplySink);
            }

            public IMessage SyncProcessMessage(IMessage msg)
            {
                var contextSyncContext = new ContextSynchronizationContext(SynchronizationContext.Current);
                using (new SynchronizationContextReplacer(contextSyncContext))
                {
                    var ret = m_NextSink.SyncProcessMessage(msg);
                    return ret;
                }
            }

            private IMessage SyncProcessMessageDelegateForAsyncReply(IMessageSink nextSink, IMessage msg, SynchronizationContextReplacer syncContextReplacer)
            {
                syncContextReplacer.Dispose();
                return nextSink.SyncProcessMessage(msg);
            }

            private void PreChecks()
            {
                if (SynchronizationContext.Current != null)
                    return;

                if (TaskScheduler.Current != TaskScheduler.Default)
                    throw new InvalidOperationException("InstallContextSynchronizationContextMessageSink does not support calling methods with SynchronizationContext.Current as null while Taskscheduler.Current is not TaskScheduler.Default");
            }
        }

        sealed class SynchronizationContextReplacer : IDisposable
        {
            SynchronizationContext m_original;
            SynchronizationContext m_new;

            public SynchronizationContextReplacer(SynchronizationContext syncContext)
            {
                m_original = SynchronizationContext.Current;
                m_new = syncContext;
                SynchronizationContext.SetSynchronizationContext(m_new);
            }

            public void Dispose()
            {
                // We don't expect the SynchronizationContext to be changed during the lifetime of the SynchronizationContextReplacer
                if (SynchronizationContext.Current != m_new)
                    throw new InvalidOperationException("SynchronizationContext was changed unexpectedly.");

                SynchronizationContext.SetSynchronizationContext(m_original);
            }
        }

        sealed class ContextSynchronizationContext : PassThroughSynchronizationConext
        {
            readonly Context m_context;

            private ContextSynchronizationContext(SynchronizationContext passThroughSyncContext, Context ctx)
                : base(passThroughSyncContext)
            {
                if (ctx == null)
                    throw new ArgumentNullException("ctx");

                m_context = ctx;
            }

            public ContextSynchronizationContext(SynchronizationContext passThroughSyncContext)
                : this(passThroughSyncContext, Thread.CurrentContext)
            {
            }

            protected override SynchronizationContext CreateCopy(SynchronizationContext copiedPassThroughSyncContext)
            {
                return new ContextSynchronizationContext(copiedPassThroughSyncContext, m_context);
            }

            protected override void CreateSendOrPostCallback(SendOrPostCallback d, object state)
            {
                CrossContextDelegate ccd = () => d(state);
                m_context.DoCallBack(ccd);
            }
        }

        abstract class PassThroughSynchronizationConext : SynchronizationContext
        {
            readonly SynchronizationContext m_passThroughSyncContext;

            protected PassThroughSynchronizationConext(SynchronizationContext passThroughSyncContext)
                : base()
            {
                m_passThroughSyncContext = passThroughSyncContext;
            }

            protected abstract void CreateSendOrPostCallback(SendOrPostCallback d, object state);
            protected abstract SynchronizationContext CreateCopy(SynchronizationContext copiedPassThroughSyncContext);

            public sealed override void Post(SendOrPostCallback d, object state)
            {
                var d2 = CreateSendOrPostCallback(d);
                if (m_passThroughSyncContext != null)
                    m_passThroughSyncContext.Post(d2, state);
                else
                    base.Post(d2, state);
            }

            public sealed override void Send(SendOrPostCallback d, object state)
            {
                var d2 = CreateSendOrPostCallback(d);
                if (m_passThroughSyncContext != null)
                    m_passThroughSyncContext.Send(d2, state);
                else
                    base.Send(d2, state);
            }

            public sealed override SynchronizationContext CreateCopy()
            {
                var copiedSyncCtx = m_passThroughSyncContext != null ? m_passThroughSyncContext.CreateCopy() : null;
                return CreateCopy(copiedSyncCtx);
            }

            public sealed override void OperationCompleted()
            {
                if (m_passThroughSyncContext != null)
                    m_passThroughSyncContext.OperationCompleted();
                else
                    base.OperationCompleted();
            }

            public sealed override void OperationStarted()
            {
                if (m_passThroughSyncContext != null)
                    m_passThroughSyncContext.OperationStarted();
                else
                    base.OperationStarted();
            }

            public sealed override int Wait(IntPtr[] waitHandles, bool waitAll, int millisecondsTimeout)
            {
                return m_passThroughSyncContext != null ?
                    m_passThroughSyncContext.Wait(waitHandles, waitAll, millisecondsTimeout) :
                    base.Wait(waitHandles, waitAll, millisecondsTimeout);
            }

            private SendOrPostCallback CreateSendOrPostCallback(SendOrPostCallback d)
            {
                SendOrPostCallback sopc = s => CreateSendOrPostCallback(d, s);
                return sopc;
            }
        }

        sealed class DelegateMessageSink : IMessageSink
        {
            public delegate IMessage SyncProcessMessageDelegate(IMessageSink nextSink, IMessage msg);
            public delegate IMessageCtrl AsyncProcessMessageDelegate(IMessageSink nextSink, IMessage msg, IMessageSink replySink);

            readonly IMessageSink m_NextSink;
            readonly SyncProcessMessageDelegate m_syncProcessMessageDelegate;
            readonly AsyncProcessMessageDelegate m_asyncProcessMessageDelegate;

            public DelegateMessageSink(IMessageSink nextSink, SyncProcessMessageDelegate syncProcessMessageDelegate, AsyncProcessMessageDelegate asyncProcessMessageDelegate)
            {
                m_NextSink = nextSink;
                m_syncProcessMessageDelegate = syncProcessMessageDelegate;
                m_asyncProcessMessageDelegate = asyncProcessMessageDelegate;
            }

            public IMessageCtrl AsyncProcessMessage(IMessage msg, IMessageSink replySink)
            {
                return (m_asyncProcessMessageDelegate != null) ?
                    m_asyncProcessMessageDelegate(m_NextSink, msg, replySink) :
                    m_NextSink.AsyncProcessMessage(msg, replySink);
            }

            public IMessageSink NextSink
            {
                get { return m_NextSink; }
            }

            public IMessage SyncProcessMessage(IMessage msg)
            {
                return (m_syncProcessMessageDelegate != null) ?
                    m_syncProcessMessageDelegate(m_NextSink, msg) :
                    m_NextSink.SyncProcessMessage(msg);
            }
        }
    }

这篇关于ContextBoundObject抛出远程错误后等待的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆