How to wrap existing object instance into DispatchProxy?
Asked Answered
L

2

9

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?

Larainelarboard answered 5/7, 2017 at 9:50 Comment(7)
The cast to FooProxy above won't compile because the type of proxy is IFoo. Declaring proxy to be of type object will solve the problemHyden
@robertfriberg: It will compile. This was pasted directly from sample, which compiles and runs as expected.Larainelarboard
Doesn't compile for me, see twitter.com/robertfriberg/status/903527168403275784Hyden
DispatchProxy.Create<IFoo, FooProxy>() return an IFoo, the compiler won't allow a cast too FooProxy. I'm using VS2017, C# 7.0, and .Net Standard 1.6Hyden
@robertfriberg: ah, I see. Our samples are not identical. The reason is that your code uses generics. While cast above is allowed for non-generic code, generic one requires double cast (my production code is generic one and contains double cast too). But in the context of a question, code from above is OK.Larainelarboard
I have an answer that I've supplied to a very similar question here: https://mcmap.net/q/541245/-logging-using-aop-in-net-core-2-1 - it might help to see a working implementation of DispatchProxy?Unpopular
Surprisingly DispatchProxy is not nicely usable out of the box. Creating a generic Utility class is a suggested solution: devblogs.microsoft.com/dotnet/…Deni
A
6

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.).

Amandine answered 5/7, 2017 at 10:18 Comment(3)
Thanks for the answer. I'm using proxies for some sort of extended logging. There's no goal to replace implementation. I just need to have ability to turn on/off parameters and return values logging for particular instance, and call original implementation.Larainelarboard
So the cleanest way would probably be to add sth like a Logger and TargetInstance property on your custom dispatch class if you want to use DispatchProxy.Amandine
fyi the source code is here: github.com/dotnet/corefx/tree/master/src/…Amandine
H
4

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());
Heinrik answered 9/10, 2019 at 17:28 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.