What I'm trying to do is to be able to intercept calls to an object's methods and properties for cross-cutting concerns. I'm using proxy-based AOP using ContextBoundObject.
However this doesn't work for recursive method calls, The first call against the target will be intercepted by the proxy and successfully invoked, allowing me to do cross-cut here. However subsequent method calls from within the first method will stay within the target class and are not intercepted by the proxy as if no marshaling occurs!
Is there any way I can make it work? (I'm trying to avoid third-party libraries like PostSharp, Unity or Spring.Net)
class Program
{
static void Main(string[] args)
{
var t = new SimpleObject();
t.TestMethod1();
}
}
[Intercept]
class SimpleObject : ContextBoundObject
{
public string TestMethod1()
{
return TestMethod2();
}
public string TestMethod2()
{
return "test";
}
}
[AttributeUsage(AttributeTargets.Class)]
public class InterceptAttribute : ContextAttribute, IContributeObjectSink
{
public InterceptAttribute()
: base("Intercept")
{ }
public override bool IsContextOK(Context ctx, IConstructionCallMessage ctorMsg)
{
return false;
}
public IMessageSink GetObjectSink(MarshalByRefObject obj, IMessageSink nextSink)
{
return new InterceptSink(nextSink);
}
}
public class InterceptSink : IMessageSink
{
public IMessageSink NextSink { get; private set; }
public InterceptSink(IMessageSink nextSink)
{
this.NextSink = nextSink;
}
public IMessage SyncProcessMessage(IMessage msg)
{
IMethodCallMessage mcm = (msg as IMethodCallMessage);
// { cross-cut here }
IMessage rtnMsg = this.NextSink.SyncProcessMessage(msg);
IMethodReturnMessage mrm = (rtnMsg as IMethodReturnMessage);
// { cross-cut here }
return mrm;
}
public IMessageCtrl AsyncProcessMessage(IMessage msg, IMessageSink replySink)
{
return null;
}
}
C# designers have never been in favor of AOP, there's no easy way to intercept method calls without using Proxies and Marshaling, which have their own drawbacks!
In case anyone wants to intercept method/property calls (eg. cross cutting concerns), I've found RealProxy to be of some help.
RealProxy From MSDN:
A client that uses an object across any kind of a remoting boundary is
actually using a transparent proxy for the object. The transparent
proxy provides the illusion that the actual object resides in the
client's space. It achieves this by forwarding calls made on it to the
real object using the remoting infrastructure.
Note: A type being proxied using RealProxy must be either an interface or inherit from MarshalByRefObject.
Here's some implementation of RealProxy using a Factory Method to create a proxy of an object at runtime:
public abstract class RuntimeProxy
{
public static readonly object Default = new object();
public static Target Create<Target>(Target instance, RuntimeProxyInterceptor interceptor) where Target : class
{
return (Target)new InternalProxy<Target>(instance, interceptor).GetTransparentProxy();
}
public static Target Create<Target>(Target instance, Func<RuntimeProxyInvoker, object> factory) where Target : class
{
return (Target)new InternalProxy<Target>(instance, new InternalRuntimeProxyInterceptor(factory)).GetTransparentProxy();
}
class InternalProxy<Target> : RealProxy where Target : class
{
readonly object Instance;
readonly RuntimeProxyInterceptor Interceptor;
public InternalProxy(Target instance, RuntimeProxyInterceptor interceptor)
: base(typeof(Target))
{
Instance = instance;
Interceptor = interceptor;
}
public override IMessage Invoke(IMessage msg)
{
var methodCall = (IMethodCallMessage)msg;
var method = (MethodInfo)methodCall.MethodBase;
try
{
var result = Interceptor.Invoke(new InternalRuntimeProxyInterceptorInvoker(Instance, method, methodCall.InArgs));
if (result == RuntimeProxy.Default)
result = method.ReturnType.IsPrimitive ? Activator.CreateInstance(method.ReturnType) : null;
return new ReturnMessage(result, null, 0, methodCall.LogicalCallContext, methodCall);
}
catch (Exception ex)
{
if (ex is TargetInvocationException && ex.InnerException != null)
return new ReturnMessage(ex.InnerException, msg as IMethodCallMessage);
return new ReturnMessage(ex, msg as IMethodCallMessage);
}
}
}
class InternalRuntimeProxyInterceptor : RuntimeProxyInterceptor
{
readonly Func<RuntimeProxyInvoker, object> Factory;
public InternalRuntimeProxyInterceptor(Func<RuntimeProxyInvoker, object> factory)
{
this.Factory = factory;
}
public override object Invoke(RuntimeProxyInvoker invoker)
{
return Factory(invoker);
}
}
class InternalRuntimeProxyInterceptorInvoker : RuntimeProxyInvoker
{
public InternalRuntimeProxyInterceptorInvoker(object target, MethodInfo method, object[] args)
: base(target, method, args)
{ }
}
}
public abstract class RuntimeProxyInterceptor
{
public virtual object Invoke(RuntimeProxyInvoker invoker)
{
return invoker.Invoke();
}
}
public abstract class RuntimeProxyInvoker
{
public readonly object Target;
public readonly MethodInfo Method;
public readonly ReadOnlyCollection<object> Arguments;
public RuntimeProxyInvoker(object target, MethodInfo method, object[] args)
{
this.Target = target;
this.Method = method;
this.Arguments = new ReadOnlyCollection<object>(args);
}
public object Invoke()
{
return Invoke(this.Target);
}
public object Invoke(object target)
{
if (target == null)
throw new ArgumentNullException("target");
try
{
return this.Method.Invoke(target, this.Arguments.ToArray());
}
catch (TargetInvocationException ex)
{
throw ex.InnerException;
}
}
}
You can use the RuntimeProxy as a factory to create a proxy of an object and intercept all method/property calls and invoke the actual method.
Here's a sample:
class SomeClass : MarshalByRefObject
{
public int Mul(int a, int b)
{
return a * b;
}
public void SetValue(int val)
{
this.Val = val;
}
public int Val { get; set; }
}
Use RuntimeProxy class to create a proxy for an instance of the SomeClass class and intercept the calls:
var test = new SomeClass();
var proxy = RuntimeProxy.Create(test, t =>
{
// cross-cut here
return t.Invoke(); // invoke the actual call
});
var res = proxy.Mul(3, 4); // method with return value
proxy.SetValue(2); // void method, setting some property
var val = proxy.Val; // property access
You could use interface types in case you don't want to inherit from MarshalByRefObject class.
Related
Can I use raw type in C# like Java or is there a workaround for this? I know using Raw Type in Java is bad practice but that can solve my current problem.
I have an object that has a field holding a Pool, so that it can return to that Pool whenever it's done with its job.
//C#
public class MyObject : IPoolable
{
public Pool<MyObject> pool;
}
But if there is a new kind of object (ex: MovableObject), I want my pool field has Pool<MovableObject> not Pool<MyObject>. (There are also many kinds of objects derived from MovableObject or MyObject).
In Java, I could define pool field a raw type of Pool so that there is no compiling error.
//Java
public class MyObject implements IPoolable
{
public Pool pool;
}
I'm using a method to return all kinds sof objects in both versions. In Java, it works well but in C#, it has compiling error.
//Java
static public <T extends MyObject> T createObject(Class<T> type) {
Pool<T> pool = Pools.get(type);
T obj= pool.obtain();
obj.setPool(pool); //This won't have any problem since it is raw type
return obj;
}
//C#
public static T CreateObject<T>() where T : MyObject
{
Pool<T> pool = Pools.GetPool<T>();
T obj= pool.Obtain();
obj.Pool = pool; //Error: cannot convert Pool<T> to Pool<MyAction>
return obj;
}
Edit 1: Providing other classes and Minimal, Reproducible Example
public interface IPoolable
{
void Reset();
}
public abstract class Pool<T> where T : IPoolable
{
private readonly Stack<T> freeObjects = new Stack<T>();
public Pool() { }
protected abstract T InstantiateObject(object[] args);
public T Obtain(object[] args = null)
{
return freeObjects.Count == 0 ? InstantiateObject(args) : freeObjects.Pop();
}
public void Free(T obj)
{
if (obj != null)
{
freeObjects.Push(obj);
Reset(obj);
}
}
protected virtual void Reset(T obj)
{
obj.Reset();
}
public void Clear()
{
freeObjects.Clear();
}
}
//ReflectionPool
public class ReflectionPool<T> : Pool<T> where T : IPoolable
{
public ReflectionPool() : base()
{
}
protected override T InstantiateObject(object[] args)
{
return (T)Activator.CreateInstance(typeof(T), args);
}
}
public class Pools
{
static private readonly Dictionary<Type, object> typePools = new Dictionary<Type, object>();
static public Pool<T> GetPool<T>(int max) where T : IPoolable
{
Type type = typeof(T);
if (!typePools.TryGetValue(type, out object pool))
{
pool = new ReflectionPool<T>();
typePools.Add(type, pool);
}
return (Pool<T>)pool;
}
static public Pool<T> GetPool<T>() where T : IPoolable
{
return GetPool<T>(100);
}
}
So in MyObject class, there is a method making my object doing something, after completing it, the current object need to be returned to Pool
//Base Object
public abstract class MyObject : IPoolable
{
public Pool<MyObject> pool; //This is still a problem
public void CallMe()
{
if (Act()) //If this object completing acting
{
ToPool();
}
}
public abstract bool Act();
public void ToPool()
{
pool.Free(this);
pool = null;
}
public abstract void Reset();
}
//Movable Object
public class MovableObject : MyObject
{
//public Pool<MyObject> pool; //Put this here as comment because I want it become Pool<MovableObject>
public override bool Act()
{
return true; //Return false if not reach destination
}
public override void Reset() { }
public override string ToString()
{
return "Movable";
}
}
//I'm using this class for create object I want
public class ObjectFactory
{
public static T CreateObject<T>() where T : MyObject
{
Pool<T> pool = Pools.GetPool<T>();
if(pool != null)
{
T obj = pool.Obtain();
obj.pool = pool; //Error here: Cannot implicitly convert type 'Pool<T>' to 'Pool<MyObject>'
return obj;
}
return null;
}
public static MovableObject MovableObject()
{
return CreateObject<MovableObject>();
}
}
Edit 2: Re-Updated code in edit 1
I want to be able to put an attribute on a method and have that method inspect the method parameters and the return value. Pseudo code.
[AttributeUsage(AttributeTargets.Method)]
class MyAttribute: Attribute {
public void MethodEnter(MethodInfo info) {
foreach (var param in info.MethodParameters {
Console.WriteLine(param.ToString());
}
}
public void MethodLeave(MethodInfo info) {
Console.WriteLine(info.ReturnValue);
}
}
public class MyClass {
[MyAttribute]
public SomeType foo(Order order, List<OrderLine> orderLines) {
...
}
}
Is this possible?
Basically, I want to be able to log everything that comes in and leaves the function. I've done this previously with PostSharp, but it seems like an overkill to use it for logging.
You could create an aspect by making your class derive from ContextBoundObject and your attribute from ContextAttribute. By reading this article I made the following sample that meet your requirements:
First, our Foo class derived from ContextBoundObject, decorated with our custom class attribute:
[Inpectable]
internal class Foo : ContextBoundObject
{
[InspectableProperty]
public int DoSomething(int a)
{
Console.WriteLine("I am doing something");
a += 1;
return a;
}
public void IamNotLogged()
{
Console.WriteLine("Lol");
}
[InspectableProperty]
public string IamLoggedToo()
{
var msg = "Lol too";
Console.WriteLine(msg);
return msg;
}
}
Now the Inspectable attribute
[AttributeUsage(AttributeTargets.Class)]
public class Inpectable : ContextAttribute
{
public Inpectable() : base("Inspectable")
{
}
public override void GetPropertiesForNewContext(IConstructionCallMessage ccm)
{
ccm.ContextProperties.Add(new InspectorProperty());
}
}
The InspectablePropertyAttribute which will only be used to identify those methods that should be logged:
[AttributeUsage(AttributeTargets.Method)]
public class InspectableProperty : Attribute
{
}
The InspectorProperty, which will capture the context of the instance and intercept the messages passing them to our aspect
public class InspectorProperty : IContextProperty,
IContributeObjectSink
{
public bool IsNewContextOK(Context newCtx) => true;
public void Freeze(Context newContext)
{
}
public string Name { get; } = "LOL";
public IMessageSink GetObjectSink(MarshalByRefObject o,IMessageSink next) => new InspectorAspect(next);
}
And where the magic works, the implementation of our InspectorAspect:
internal class InspectorAspect : IMessageSink
{
internal InspectorAspect(IMessageSink next)
{
NextSink = next;
}
public IMessageSink NextSink { get; }
public IMessage SyncProcessMessage(IMessage msg)
{
if (!(msg is IMethodMessage)) return NextSink.SyncProcessMessage(msg);
var call = (IMethodMessage) msg;
var type = Type.GetType(call.TypeName);
if (type == null) return NextSink.SyncProcessMessage(msg);
var methodInfo = type.GetMethod(call.MethodName);
if (!Attribute.IsDefined(methodInfo, typeof (InspectableProperty)))
return NextSink.SyncProcessMessage(msg);
Console.WriteLine($"Entering method: {call.MethodName}. Args being:");
foreach (var arg in call.Args)
Console.WriteLine(arg);
var returnMethod = NextSink.SyncProcessMessage(msg) as IMethodReturnMessage;
Console.WriteLine($"Method {call.MethodName} returned: {returnMethod?.ReturnValue}");
Console.WriteLine();
return returnMethod;
}
public IMessageCtrl AsyncProcessMessage(IMessage msg, IMessageSink replySink)
{
throw new InvalidOperationException();
}
}
Inside the SyncProcessMessage theInspectorAspect receives all the messages regarding the context (Foo), so we just filter those that are instances of IMethodMessage and then only those belonging to methods decorated with InspectableProperty attribute.
This may not be a ready-to-production solution, but I think that puts you in the right path to research for more info.
Finally testing:
private static void Main(string[] args)
{
var foo = new Foo();
foo.DoSomething(1);
foo.IamNotLogged();
foo.IamLoggedToo();
Console.ReadLine();
}
The output:
EDIT>>>
These are the namespaces needed:
using System.Runtime.Remoting.Activation;
using System.Runtime.Remoting.Contexts;
using System.Runtime.Remoting.Messaging;
You basicaly looking for AOP (Aspect Oriented Programming) solution. PostSharp uses IL injection in your binaries after compilation and in my opinion, this is worst framework I worked with.
Instead of looking into some very complex solution I recommend you to decorate your classes. For example:
public MyClass : IMyClass
{
public object[] MyMethod(object[] args){...}
}
public LoggerDecoratedMyClass : IMyClass
{
private readonly IMyClass _inner;
private readonly ILogger _logger;
public LoggerDecoratedMyClass(IMyClass inner, ILogger logger)
{
_inner = inner;
_logger = logger;
}
public object[] MyMethod(object[] args)
{
try
{
var result = _inner.MyMethod(args);
_logger.LogSuccess(...);
return result;
}
catch (Exception ex)
{
_logger.LogError(..., ex);
throw;
}
}
}
This looks a lot better than attribute bindings, and provide you with ability to control your Aspect dependecies. Also, it forces you to write interface oriented code.
UPDATE
Also, I often use scope logging:
internal struct ScopeLogger : IDisposable
{
private readonly string _name;
public ScopeLogger(ILogger logger, string scopeName, object[] args)
{
_name = scopeName;
_logger = logger;
_logger.LogInfo("Begin {name}: {args}", _name, args);
}
public void Dispose()
{
_logger.LogInfo("End {name}",_name);
}
}
public static IDisposable LogScope(this ILogger logger, string name, params object[] args)
{
return new ScopeLogger(logger, name, args);
}
And simply use it like this:
public LoggerDecoratedMyClass : IMyClass
{
private readonly IMyClass _inner;
private readonly ILogger _logger;
public LoggerDecoratedMyClass(IMyClass inner, ILogger logger)
{
_inner = inner;
_logger = logger;
}
public object[] MyMethod(object[] args)
{
using(_logger.LogScope(nameof(MyMethod), args))
{
return _inner.MyMethod(args);
}
}
}
Decorators this way looks a lot better and shorter.
I'm looking for RealProxy replacement in .NET Core, and this issue forwards me to DispatchProxy.
It has simple API, but it's unclear, how to wrap existing object into proxy.
E.g., having this interface:
interface IFoo
{
string Bar(int boo);
}
and this implementation:
class FooImpl : IFoo
{
public string Bar(int boo)
{
return $"Value {boo} was passed";
}
}
how to get what I want?
class Program
{
static void Main(string[] args)
{
var fooInstance = new FooImpl();
var proxy = DispatchProxy.Create<IFoo, FooProxy>();
var s = proxy.Bar(123);
Console.WriteLine(s);
}
}
class FooProxy : DispatchProxy
{
protected override object Invoke(MethodInfo targetMethod, object[] args)
{
return targetMethod.Invoke(/* I need fooInstance here */, args);
}
}
Since DispatchProxy descendants must have parameterless constructor, the only idea I have is to invent some method, like this:
class FooProxy : DispatchProxy
{
private object target;
public void SetTarget(object target)
{
this.target = target;
}
protected override object Invoke(MethodInfo targetMethod, object[] args)
{
return targetMethod.Invoke(target, args);
}
}
and use it this way:
var fooInstance = new FooImpl();
var proxy = DispatchProxy.Create<IFoo, FooProxy>();
((FooProxy)proxy).SetTarget(fooInstance);
// the rest of code...
Is this correct approach?
You are right that there is no other option here than to cast the generated IFoo to the known proxy type (FooProxy) and use a custom method or property on FooProxy. There is no public API to add constructor arguments or return the proxy as the implementation type. However, DispatchProxy.Create() will return an instance of a subclass of FooProxy whose type is generated at runtime via reflection and IL emitting.
If you are looking at other ways to quickly wrap an implementation and replace interface methods / virtual methods, I suggest using mocking frameworks instead (FakeItEasy, Moq, NSubstitute etc.).
You need to create your own Generic class that inherit from DispatchProxy and has own static Create that has an extra parameter from type target.
example
public class AopAction<T>:DispatchProxy
{
#region Private Fields
private Action<MethodInfo,object[],object> ActAfter;
private Action<MethodInfo,object[]> ActBefore;
private Action<MethodInfo,object[],Exception> ActException;
private T Decorated;
#endregion Private Fields
#region Public Methods
public static T Create(T decorated,Action<MethodInfo,object[]> actBefore = null,Action<MethodInfo,object[],object> actAfter = null,Action<MethodInfo,object[],Exception> actException = null)
{
object proxy = Create<T,AopAction<T>>();
SetParameters();
return (T)proxy;
void SetParameters()
{
var me = ((AopAction<T>)proxy);
me.Decorated = decorated == null ? throw new ArgumentNullException(nameof(decorated)) : decorated;
me.ActBefore = actBefore;
me.ActAfter = actAfter;
me.ActException = actException;
}
}
#endregion Public Methods
#region Protected Methods
protected override object Invoke(MethodInfo targetMethod,object[] args)
{
_ = targetMethod ?? throw new ArgumentException(nameof(targetMethod));
try
{
ActBefore?.Invoke(targetMethod,args);
var result = targetMethod.Invoke(Decorated,args);
ActAfter?.Invoke(targetMethod,args,result);
return result;
}
catch(Exception ex)
{
ActException?.Invoke(targetMethod,args,ex);
throw ex.InnerException;
}
}
#endregion Protected Methods
}
to use your example
var proxy=AopAction<IFoo>.Create(new FooImpl());
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.
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);
}
}
When we invoke Method1 we get the following RemotingException on the second Console.WriteLine:
Remoting cannot find field 'one' on type 'WindowsFormsApplication1.Class1'.
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?
Short answer: Remoting calls do not work on private fields. The async/await rewriting causes an attempt to make a remoting call on a private field.
The issue can be reproduced without async/await. And demonstrating it this way is helpful in understanding what is going on in the async/await case:
[OhMy]
public class Class2 : ContextBoundObject
{
private string one = "1";
public void Method1()
{
var nc = new NestedClass(this);
}
public class NestedClass
{
public NestedClass(Class2 c2)
{
Console.WriteLine(c2.one); // Note: nested classes are allowed access to outer classes privates
}
}
}
static void Main(string[] args)
{
var c2 = new Class2();
// This call causes no problems:
c2.Method1();
// This, however, causes the issue.
var nc = new Class2.NestedClass(c2);
}
Let's walk through what happens line by line:
In Main, we start out in Context0
Since Class2 is a ContextBoundObject and since the OhMyAttribute considers the current context unacceptable, an instance of Class2 is created in Context1 (I'll call this c2_real, and what is returned and stored in c2 is a remoting proxy to c2_real.
When c2.Method1() is called, it is called on the remote proxy. Since we are in Context0, the remote proxy realizes it is not in the correct context so it switches to Context1, and the code within Method1 is executed.
3.a Within Method1 we call the NestedClass constructor which uses c2.one. In this case, we are already in Context1, so the c2.one requires no context switches and so we are using the c2_real object directly.
Now, the problematic case:
We create a new NestedClass passing in the remote proxy c2. No context switches occur here because NestedClass is not a ContextBoundObject.
Within the NestedClass ctor, it access c2.one. The remote proxy notices that we are still in Context0, and so it attempts to remote this call to Context1. This fails because c2.one is a private field. You'll see in Object.GetFieldInfo it is only looking for Public fields:
private FieldInfo GetFieldInfo(String typeName, String fieldName)
{
// ...
FieldInfo fldInfo = t.GetField(fieldName, BindingFlags.Public |
BindingFlags.Instance |
BindingFlags.IgnoreCase);
if(null == fldInfo)
{
#if FEATURE_REMOTING
throw new RemotingException(String.Format(
CultureInfo.CurrentCulture, Environment.GetResourceString("Remoting_BadField"),
fieldName, typeName));
// ...
}
return fldInfo;
}
So, How does async/await end up causing this same issue?
The async/await causes your Class1 to get rewritten such that it uses a nested class with a state machine (used ILSpy to generate):
public class Class1 : ContextBoundObject
{
// ...
private struct <Method1>d__0 : IAsyncStateMachine
{
public int <>1__state;
public AsyncTaskMethodBuilder <>t__builder;
public Class1 <>4__this;
private TaskAwaiter <>u__$awaiter1;
private object <>t__stack;
void IAsyncStateMachine.MoveNext()
{
try
{
int num = this.<>1__state;
if (num != -3)
{
TaskAwaiter taskAwaiter;
if (num != 0)
{
Console.WriteLine(this.<>4__this.one);
taskAwaiter = Task.Delay(50).GetAwaiter();
if (!taskAwaiter.IsCompleted)
{
this.<>1__state = 0;
this.<>u__$awaiter1 = taskAwaiter;
this.<>t__builder.AwaitUnsafeOnCompleted<TaskAwaiter, Class1.<Method1>d__0>(ref taskAwaiter, ref this);
return;
}
}
else
{
taskAwaiter = this.<>u__$awaiter1;
this.<>u__$awaiter1 = default(TaskAwaiter);
this.<>1__state = -1;
}
taskAwaiter.GetResult();
taskAwaiter = default(TaskAwaiter);
Console.WriteLine(this.<>4__this.one);
}
}
catch (Exception exception)
{
this.<>1__state = -2;
this.<>t__builder.SetException(exception);
return;
}
this.<>1__state = -2;
this.<>t__builder.SetResult();
}
// ...
}
private string one = "1";
public Task Method1()
{
Class1.<Method1>d__0 <Method1>d__;
<Method1>d__.<>4__this = this;
<Method1>d__.<>t__builder = AsyncTaskMethodBuilder.Create();
<Method1>d__.<>1__state = -1;
AsyncTaskMethodBuilder <>t__builder = <Method1>d__.<>t__builder;
<>t__builder.Start<Class1.<Method1>d__0>(ref <Method1>d__);
return <Method1>d__.<>t__builder.Task;
}
}
The important thing to notice is that
It's created a nested structure which has access to the privates of Class1
The this variable is lifted and stored in the nested class.
So, what happens here is that
On the initial call to c1.Method1() the remoting proxy notices we are in Context0, and that it needs to switch to Context1.
Eventually, MoveNext is called, and c1.one is called. Since we are already in Context1, no context switch is necessary (so the issue doesn't occur).
Later, since a continuation was registered, a call to MoveNext will occur again to execute the rest of the code after the await. However, this call to MoveNext will not occur inside a call to one of Class1's methods. Thus, when the code c1.one is executed this time, we will be in Context0. The remoting proxy notices we are in Context0, and attempts a context switch. This causes the same failure as above since c1.one is a private field.
Workaround:
I'm not sure of a general workaround, but for this specific case you can work around the issue by not using the this reference in the method. I.e.:
public async Task Method1()
{
var temp = one;
Console.WriteLine(temp);
await Task.Delay(50);
Console.WriteLine(temp);
}
Or switch to using a private property instead of a field.
Here is a more general workaround.
It has the following deficiencies:
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 https://stackoverflow.com/a/22417031/495262).
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);
}
}
}
This is how I used to make method calls:
SvcHelper.Using<SomeWebServiceClient>(proxy =>
{
proxy.SomeMethod();
}
public class SvcHelper
{
public static void Using<TClient>(Action<TClient> action) where TClient : ICommunicationObject, IDisposable, new()
{
}
}
This is how I make method calls:
ChannelFactory<ISomethingWebService> cnFactory = new ChannelFactory<ISomethingWebService>("SomethingWebService");
ISomethingWebService client = cnFactory.CreateChannel();
using (new OperationContextScope((IContextChannel)client))
{
client.SomeMethod();
}
My question is: Instead of replacing every instance of my original method call approach; Is there a way to modify my SvcHelper and do the creation of the channel in the SvcHelper constructor and then simply pass the interface like the following:
SvcHelper.Using<ISomethingWebService>(client =>
{
client.SomeMethod();
}
Hope this makes sense and thanks in advance.
First, you don't want to create a new ChannelFactory<T> every call to the Using helper method. They are the most costly thing to construct in the WCF universe. So, at bare minimum, you will want to use a caching approach there.
Second, you don't want to tie yourself to "client" types at all anymore. Just work straight with the service contract interfaces.
Starting from what you've got, here's where I'd go based on how I've done this in the past:
public class SvcHelper
{
private static ConcurrentDictionary<ChannelFactoryCacheKey, ChannelFactory> ChannelFactories = new ConcurrentDictionary<ChannelFactoryCacheKey, ChannelFactory>();
public static void Using<TServiceContract>(Action<TServiceContract> action) where TServiceContract : class
{
SvcHelper.Using<TServiceContract>(action, "*");
}
public static void Using<TServiceContract>(Action<TServiceContract> action, string endpointConfigurationName) where TServiceContract : class
{
ChannelFactoryCacheKey cacheKey = new ChannelFactoryCacheKey(typeof(TServiceContract), endpointConfigurationName);
ChannelFactory<TServiceContract> channelFactory = (ChannelFactory<TServiceContract>)SvcHelper.ChannelFactories.GetOrAdd(
cacheKey,
missingCacheKey => new ChannelFactory<TServiceContract>(missingCacheKey.EndpointConfigurationName));
TServiceContract typedChannel = channelFactory.CreateChannel();
IClientChannel clientChannel = (IClientChannel)typedChannel;
try
{
using(new OperationContextScope((IContextChannel)typedChannel))
{
action(typedChannel);
}
}
finally
{
try
{
clientChannel.Close();
}
catch
{
clientChannel.Abort();
}
}
}
private sealed class ChannelFactoryCacheKey : IEquatable<ChannelFactoryCacheKey>
{
public ChannelFactoryCacheKey(Type channelType, string endpointConfigurationName)
{
this.channelType = channelType;
this.endpointConfigurationName = endpointConfigurationName;
}
private Type channelType;
public Type ChannelType
{
get
{
return this.channelType;
}
}
private string endpointConfigurationName;
public string EndpointConfigurationName
{
get
{
return this.endpointConfigurationName;
}
}
public bool Equals(ChannelFactoryCacheKey compareTo)
{
return object.ReferenceEquals(this, compareTo)
||
(compareTo != null
&&
this.channelType == compareTo.channelType
&&
this.endpointConfigurationName == compareTo.endpointConfigurationName);
}
public override bool Equals(object compareTo)
{
return this.Equals(compareTo as ChannelFactoryCacheKey);
}
public override int GetHashCode()
{
return this.channelType.GetHashCode() ^ this.endpointConfigurationName.GetHashCode();
}
}
}
This should work:
public class SvcHelper
{
public static void Using<TClient>(Action<TClient> action) where TClient : ICommunicationObject, IDisposable
{
ChannelFactory<TClient> cnFactory = new ChannelFactory<TClient>("SomethingWebService");
TClient client = cnFactory.CreateChannel();
using (new OperationContextScope((IContextChannel)client))
{
action(client);
}
}
}