在dotnet core中的RealProxy?

48

我正在使用命名空间System.Runtime.Remoting.ProxiesSystem.Runtime.Remoting.Messaging来进行C#中的AOP工作。

我试图将我的应用程序从.Net Framework 4.6迁移到dnxcore / dotnet core。

Intellisense说,这两个命名空间在我的框架版本(netcoreapp1.0 / dnxcore50)中不可用。有没有想法这两个命名空间是否会出现?或者如何像使用RealProxy类一样获取AOP?

我不想使用第三方库 - 我只想使用.Net提供给我的东西。


你找到解决方案了吗? - Rafael Enriquez
1
@RafaelEnriquez 直到今天,在 asp net core 1 中还没有实现这个功能。也许在 1.1 或 1.2 版本中会有。如果我找到了什么,我会在这里发布答案 :) - Matthias Burger
那么它的解决方案是什么呢? https://www.nuget.org/packages/System.Runtime/ 是否与 .NetCore 兼容? - Haseeb Jadoon
@HaseebJadoon 我没有使用当前版本的netstandard/netcore/whatevernet进行测试 - 我为AOP编写了自己的实现。这篇文章很快就要过生日了 - 但是请随意使用当前版本进行测试,并告诉我们它是否有效 :) - Matthias Burger
1
@MatthiasBurger 我正在将Spring.NET移植到.NET Standard。今天,DI/IOC/AOP已经可以工作了。你可以在https://github.com/spring-projects/spring-net/issues/133上看到一些细节。 - Luiz Carlos Faria
3个回答

52

看起来RealProxy不会出现在.NET Core/Standard中。在这个问题中,一位微软开发人员建议DispatchProxy作为替代方案。

另外,一些现有的AOP框架可能已经支持.NET Core,或者将来会支持(在问题的评论中可以看到)。

一个替代方案是DispatchProxy,它有一个很好的例子:http://www.c-sharpcorner.com/article/aspect-oriented-programming-in-c-sharp-using-dispatchproxy/

如果我们简化代码,就会得到这样:

public class LoggingDecorator<T> : DispatchProxy
{
    private T _decorated;

    protected override object Invoke(MethodInfo targetMethod, object[] args)
    {
        try
        {
            LogBefore(targetMethod, args);

            var result = targetMethod.Invoke(_decorated, args);

            LogAfter(targetMethod, args, result);
            return result;
        }
        catch (Exception ex) when (ex is TargetInvocationException)
        {
            LogException(ex.InnerException ?? ex, targetMethod);
            throw ex.InnerException ?? ex;
        }
    }

    public static T Create(T decorated)
    {
        object proxy = Create<T, LoggingDecorator<T>>();
        ((LoggingDecorator<T>)proxy).SetParameters(decorated);

        return (T)proxy;
    }

    private void SetParameters(T decorated)
    {
        if (decorated == null)
        {
            throw new ArgumentNullException(nameof(decorated));
        }
        _decorated = decorated;
    }

    private void LogException(Exception exception, MethodInfo methodInfo = null)
    {
        Console.WriteLine($"Class {_decorated.GetType().FullName}, Method {methodInfo.Name} threw exception:\n{exception}");
    }

    private void LogAfter(MethodInfo methodInfo, object[] args, object result)
    {
        Console.WriteLine($"Class {_decorated.GetType().FullName}, Method {methodInfo.Name} executed, Output: {result}");
    }

    private void LogBefore(MethodInfo methodInfo, object[] args)
    {
        Console.WriteLine($"Class {_decorated.GetType().FullName}, Method {methodInfo.Name} is executing");
    }
}

如果我们有一个示例类Calculator,对应的接口(此处未显示):

public class Calculator : ICalculator
{
    public int Add(int a, int b)
    {
        return a + b;
    }
}

我们可以直接像这样使用它。
static void Main(string[] args)
{
    var decoratedCalculator = LoggingDecorator<ICalculator>.Create(new Calculator());
    decoratedCalculator.Add(3, 5);
    Console.ReadKey();
}

2
所以,现在 DispatchProxy 是最好的选择。感谢您分享知识。 :) - Matthias Burger
只是评论一下targetMethod.Invoke使用反射并且速度较慢。而且如果不通过更慢的反射跳过某些困难,它无法用于异步方法。这个方法虽然可以工作,但实际上并不是太理想。 - Sleeper Smith
遗憾的是,DispatchProxy 只能与接口一起使用,而不能与类一起使用。 - ADM-IT

13

您可以使用System.Reflection.DispatchProxy或您自己的简单装饰器实现。请参考维基百科上的装饰器模式页面以获取实现示例。

当前在.NET Core中,您无法使用构造函数注入来使用DispatchProxy。您必须使用DispatchProxy.Create()工厂方法和属性注入,然后显式转换为要使用的代理类型。有关更多信息,请查看.NET Core GitHub存储库中的DispachProxyTest.cs

这是一个继承了DispatchProxy的简单通用装饰器示例:

class GenericDecorator : DispatchProxy
{
    public object Wrapped { get; set; }
    public Action<MethodInfo, object[]> Start { get; set; }
    public Action<MethodInfo, object[], object> End { get; set; }
    protected override object Invoke(MethodInfo targetMethod, object[] args)
    {
        Start?.Invoke(targetMethod, args);
        object result = targetMethod.Invoke(Wrapped, args);
        End?.Invoke(targetMethod, args, result);
        return result;
    }
}

以及其使用方法:

class Program
{
    static void Main(string[] args)
    {
        IEcho toWrap = new EchoImpl();
        IEcho decorator = DispatchProxy.Create<IEcho, GenericDecorator>();
        ((GenericDecorator)decorator).Wrapped = toWrap;
        ((GenericDecorator)decorator).Start = (tm, a) => Console.WriteLine($"{tm.Name}({string.Join(',', a)}) is started");
        ((GenericDecorator)decorator).End = (tm, a, r) => Console.WriteLine($"{tm.Name}({string.Join(',', a)}) is ended with result {r}");
        string result = decorator.Echo("Hello");
    }

    class EchoImpl : IEcho
    {
        public string Echo(string message) => message;
    }

    interface IEcho
    {
        string Echo(string message);
    }
}

1

您可以使用支持 .Net Core 的 Castle.Core

using Castle.DynamicProxy;
using System;
using System.Reflection;

namespace ObservableProxyTest
{
    class Program
    {
        private static readonly ProxyGenerator Generator = new ProxyGenerator();

        static void Main(string[] args)
        {
            var obj = new MyEntity()
            {
                Name = "My name",
                Description = "My description"
            };

            var proxy = Generator.CreateClassProxyWithTarget(obj, new ObservableInterceptor());

            Console.WriteLine("Object changed: " + proxy.IsChanged);

            proxy.Name = "My name 2";
            proxy.Description = "My description 2";

            Console.WriteLine("Object changed: " + proxy.IsChanged);

            Console.ReadLine();
        }
    }

    internal interface IObservable
    {
        bool IsChanged { get; }
        void SetChanged();
    }

    public abstract class BaseEntity : IObservable
    {
        public virtual bool IsChanged { get; protected set; }

        public void SetChanged()
        {
            IsChanged = true;
        }
    }

    public class MyEntity : BaseEntity
    {
        // Virtual keyword is very important
        public virtual string Name { get; set; }
        public virtual string Description { get; set; }
    }

    internal class ObservableInterceptor : IInterceptor
    {
        public void Intercept(IInvocation invocation)
        {
            var observable = invocation.InvocationTarget as IObservable;
            if (observable != null && !observable.IsChanged && IsSetter(invocation.Method))
            {
                observable.SetChanged();
            }
            invocation.Proceed();
        }

        private bool IsSetter(MethodInfo method)
        {
            return method.IsSpecialName && method.Name.StartsWith("set_", StringComparison.OrdinalIgnoreCase);
        }
    }

}

请也阅读非常好的文章:https://fullboarllc.com/change-tracking-structuremap-dynamicproxy/

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接