{"id":31529,"date":"2021-01-12T07:23:50","date_gmt":"2021-01-12T14:23:50","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/dotnet\/?p=31529"},"modified":"2021-01-12T07:24:41","modified_gmt":"2021-01-12T14:24:41","slug":"migrating-realproxy-usage-to-dispatchproxy","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/dotnet\/migrating-realproxy-usage-to-dispatchproxy\/","title":{"rendered":"Migrating RealProxy Usage to DispatchProxy"},"content":{"rendered":"<p>As I&#8217;ve helped customers port .NET Framework apps to .NET Core 3.1 and .NET 5, one question that has come up several times is the question of what to do about <a href=\"https:\/\/docs.microsoft.com\/dotnet\/api\/system.runtime.remoting.proxies.realproxy\"><code>System.Runtime.Remoting.Proxies.RealProxy<\/code><\/a> usage. Customers using the API are concerned because they know that remoting is <a href=\"https:\/\/docs.microsoft.com\/dotnet\/core\/porting\/net-framework-tech-unavailable#remoting\">not supported<\/a> on .NET Core. Fortunately, the new <a href=\"https:\/\/www.nuget.org\/packages\/System.Reflection.DispatchProxy\/4.5.1\"><code>DispatchProxy<\/code><\/a> API works as an easy replacement in many cases. In this blog post, I&#8217;m going to briefly discuss how to use <code>DispatchProxy<\/code> (or Castle.Core&#8217;s <code>DynamicProxy<\/code>) in place of <code>RealProxy<\/code> for aspect-oriented programming scenarios and what differences exist between the three proxy APIs.<\/p>\n<p>Despite being in the <code>System.Runtime.Remoting<\/code> namespace, in many cases, users aren&#8217;t actually doing any cross-process remoting with <code>RealProxy<\/code>. Instead, many developers use it as an easy way to implement an <a href=\"https:\/\/wikipedia.org\/wiki\/Aspect-oriented_programming\">aspect-oriented programming (AOP)<\/a> paradigm as explained in <a href=\"https:\/\/docs.microsoft.com\/archive\/msdn-magazine\/2014\/february\/aspect-oriented-programming-aspect-oriented-programming-with-the-realproxy-class\">Aspect-Oriented Programming : Aspect-Oriented Programming with the RealProxy Class<\/a>. The idea is that cross-cutting concerns (like logging or caching) can be handled in a proxy class that wraps other types so that those concerns can be handled centrally without having to modify the original classes.<\/p>\n<p>Although <code>RealProxy<\/code> isn&#8217;t available in .NET Core or .NET 5, a dedicated API was added for exactly this scenario. If you are trying to implement cross-cutting concerns via proxy wrappers (and don&#8217;t care about cross-process remoting), <a href=\"https:\/\/www.nuget.org\/packages\/System.Reflection.DispatchProxy\/4.5.1\"><code>System.Reflection.DispatchProxy<\/code><\/a> will probably fit the bill. And if <code>DispatchProxy<\/code> doesn&#8217;t meet your needs for some reason, the third-party <a href=\"http:\/\/www.castleproject.org\/projects\/dynamicproxy\/\"><code>Castle.DynamicProxy<\/code><\/a> APIs from <a href=\"https:\/\/www.nuget.org\/packages\/Castle.Core\">Castle.Core<\/a> offer another .NET Standard-compatible alternative. The sample code in this post is available on GitHub in the <a href=\"https:\/\/github.com\/mjrousos\/ProxyExploration\">mjrousos\/ProxyExploration<\/a> repository.<\/p>\n<h2>RealProxy example<\/h2>\n<p>To start, let&#8217;s take a look at how <code>RealProxy<\/code> may have been used in a .NET Framework project to add logging.<\/p>\n<p><code>RealProxy<\/code> is an abstract class. To use it, you need to derive from it and implement the abstract <code>Invoke<\/code> method. The proxy is able to generate objects that appear to be of a given type but, when the generated objects&#8217; APIs are used, the proxy&#8217;s <code>Invoke<\/code> method will be called (instead of using the proxied type&#8217;s members). <code>Invoke<\/code> should take whatever action is desired (possibly invoking the indicated member on a wrapped instance of the proxied type) and return a <code>ReturnMessage<\/code> with the result of the operation.<\/p>\n<p>Here is a simple example of a <code>RealProxy<\/code>-based proxy for adding <a href=\"https:\/\/serilog.net\/\">Serilog<\/a> logging around all method calls. The comments in the code explain the role the different methods play in making the proxy work.<\/p>\n<pre><code class=\"CSharp\">\/\/ Simple sample RealProxy-based logging proxy\r\n\/\/ Note that the proxied type must derive from MarshalByRefObject\r\npublic class RealProxyLoggingDecorator&lt;T&gt; : RealProxy where T: MarshalByRefObject\r\n{\r\n    \/\/ A field to store an inner 'real' instance of the proxied type.\r\n    \/\/ All API calls will go to this wrapped instance (after the proxy has \r\n    \/\/ done its logging).\r\n    private readonly T _target;\r\n\r\n    \/\/ The Serilog logger to be used for logging.\r\n    private readonly Logger _logger;\r\n\r\n    \/\/ When creating a new instance of this proxy type, invoke RealProxy's\r\n    \/\/ constructor with the type to be wrapped and, optionally, allow\r\n    \/\/ the caller to provide a 'target' object to be wrapped.\r\n    private RealProxyLoggingDecorator(T target = null) : base(typeof(T))\r\n    {\r\n        \/\/ If no target object is supplied, created a new one.\r\n        if (target == null)\r\n        {\r\n            target = Activator.CreateInstance&lt;T&gt;();\r\n        }\r\n\r\n        _target = target;\r\n\r\n        \/\/ Setup the Serilog logger\r\n        _logger = new LoggerConfiguration()\r\n            .WriteTo.Console().CreateLogger();\r\n        _logger.Information(\"New logging decorator created for object of type {TypeName}\", typeof(T).FullName);\r\n    }\r\n\r\n    \/\/ This convenience method creates an instance of RealProxyLoggingDecorator\r\n    \/\/ and calls RealProxy's GetTransparentProxy method to retrieve the proxy\r\n    \/\/ object (which looks like an instance of T but calls our Invoke method\r\n    \/\/ whenever an API is used).\r\n    public static T Decorate(T target = null) =&gt;\r\n        new RealProxyLoggingDecorator&lt;T&gt;(target).GetTransparentProxy() as T;\r\n\r\n    \/\/ The invoke method is the heart of a RealProxy implementation. Here, we\r\n    \/\/ define what should happen when a member on the proxy object is used.\r\n    public override IMessage Invoke(IMessage msg)\r\n    {\r\n        \/\/ The IMessage argument should be an IMethodCallMessage indicating\r\n        \/\/ which method is being invoked. Interestingly, RealProxy even translates \r\n        \/\/ field access into method calls so those will show up here, too.\r\n        if (msg is IMethodCallMessage methodCallMsg)\r\n        {\r\n            try\r\n            {\r\n                \/\/ Perform the logging that this proxy is meant to provide\r\n                _logger.Information(\"Calling method {TypeName}.{MethodName} with arguments {Arguments}\",\r\n                    methodCallMsg.MethodBase.DeclaringType.Name, methodCallMsg.MethodName, methodCallMsg.Args);\r\n\r\n                \/\/ Cache the method's arguments locally so that out and ref args can be updated at invoke time.\r\n                \/\/ (methodCallMsg.Args can't be updated directly since they are returned by a property getter\r\n                \/\/ and don't refer to a consistent object[])\r\n                var args = methodCallMsg.Args;\r\n\r\n                \/\/ For this proxy implementation, we still want to call the original API \r\n                \/\/ (after logging has happened), so use reflection to invoke the desired\r\n                \/\/ API on our wrapped target object.\r\n                var result = methodCallMsg.MethodBase.Invoke(_target, args);\r\n\r\n                \/\/ A little more logging.\r\n                _logger.Information(\"Method {TypeName}.{MethodName} returned {ReturnValue}\", \r\n                    methodCallMsg.MethodBase.DeclaringType.Name, methodCallMsg.MethodName, result);\r\n\r\n                \/\/ Finally, Invoke should return a ReturnMessage object indicating the result of the operation\r\n                return new ReturnMessage(result, args, args.Length, methodCallMsg.LogicalCallContext, methodCallMsg);\r\n            }\r\n            catch (TargetInvocationException exc)\r\n            {\r\n                \/\/ If the MethodBase.Invoke call fails, log a warning and then return\r\n                \/\/ a ReturnMessage containing the exception.\r\n                _logger.Warning(exc.InnerException, \"Method {TypeName}.{MethodName} threw exception: {Exception}\",\r\n                    methodCallMsg.TypeName, methodCallMsg.MethodName, exc.InnerException);\r\n\r\n                return new ReturnMessage(exc.InnerException, methodCallMsg);\r\n            }\r\n        }\r\n        else\r\n        {\r\n            throw new ArgumentException(\"Invalid message; expected IMethodCallMessage\", nameof(msg));\r\n        }\r\n    }\r\n}\r\n<\/code><\/pre>\n<p>To use the proxy type, a caller just needs to use the <code>Decorate<\/code> helper method to create an instance of the proxy. The returned object will look and act just like the type being proxied except that the proxy&#8217;s <code>Invoke<\/code> method will be used whenever a member of the object is used.<\/p>\n<pre><code class=\"CSharp\">\/\/ Creates an object that acts like an instance of Widget\r\nvar widget = RealProxyLoggingDecorator&lt;Widget&gt;.Decorate(new Widget(\"Widget name\", 9.99));\r\n<\/code><\/pre>\n<h3>Pros and cons of RealProxy<\/h3>\n<ul>\n<li><strong>Pros<\/strong>\n<ul>\n<li>Has been part of the .NET Framework since 1.0 and Mono since 2.0, so it&#8217;s available to any .NET Framework app and may be more familiar to .NET developers.<\/li>\n<li>Intercepts all member access &#8211; even setting or getting field values.<\/li>\n<li>Proxies by wrapping the target object, so a proxy can be created around an already existing object.<\/li>\n<\/ul>\n<\/li>\n<li><strong>Cons<\/strong>\n<ul>\n<li>Can only proxy types derived from <code>MarshalByRefObject<\/code>, so types to be proxied often need to be derived from that type specifically for proxying purposes.<\/li>\n<li>Proxies calls to underlying objects by wrapping the target object. Therefore, if the object being wrapped makes subsequent calls to its own APIs as part of executing a method, those member accesses will not be proxied. For example, if <code>Widget.Buy()<\/code> uses <code>Widget.Price<\/code> internally, calling <code>Buy()<\/code> on a proxy object using the sample <code>RealProxy<\/code> implementation above would log the call to <code>Buy<\/code> but not the subsequent call to <code>Price<\/code>.<\/li>\n<li>Unavailable in .NET Standard, .NET Core, or .NET 5.<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<h2>DispatchProxy as an alternative<\/h2>\n<p><a href=\"https:\/\/www.nuget.org\/packages\/System.Reflection.DispatchProxy\/4.5.1\"><code>System.Reflection.DispatchProxy<\/code><\/a> was created as a .NET Standard alternative to <code>RealProxy<\/code>. It is a very similar API &#8211; it is used by deriving from <code>DispatchProxy<\/code> and implementing an <code>Invoke<\/code> method that is called when a method or property on the proxied type is called. But it also has a few important differences from <code>RealProxy<\/code>. Whereas <code>RealProxy<\/code> only worked with <code>MarshalByRefObject<\/code> types, <code>DispatchProxy<\/code> works based on interfaces. So, target types don&#8217;t need to derive from <code>MarshalByRefObject<\/code>, but they do need to implement an interface and that interface is what will be exposed by the proxy type created by <code>DispatchProxy<\/code>.<\/p>\n<p>A simple implementation of a logging proxy using <code>DispatchProxy<\/code> might look like this:<\/p>\n<pre><code class=\"CSharp\">\/\/ Simple sample DispatchProxy-based logging proxy\r\npublic class DispatchProxyLoggingDecorator&lt;T&gt; : DispatchProxy \r\n    where T: interface \/\/ T must be an interface\r\n{\r\n    \/\/ The Serilog logger to be used for logging.\r\n    private readonly Logger _logger;\r\n\r\n    \/\/ Expose the target object as a read-only property so that users can access\r\n    \/\/ fields or other implementation-specific details not available through the interface\r\n    public T Target { get; private set; }\r\n\r\n    \/\/ DispatchProxy's parameterless ctor is called when a \r\n    \/\/ new proxy instance is Created\r\n    public DispatchProxyLoggingDecorator() : base()\r\n    {\r\n        \/\/ Setup the Serilog logger\r\n        _logger = new LoggerConfiguration()\r\n            .WriteTo.Console().CreateLogger();\r\n        _logger.Information(\"New logging decorator created for object of type {TypeName}\", typeof(T).FullName);\r\n    }\r\n\r\n    \/\/ This convenience method creates an instance of DispatchProxyLoggingDecorator,\r\n    \/\/ sets the target object, and calls DispatchProxy's Create method to retrieve the \r\n    \/\/ proxy implementation of the target interface (which looks like an instance of T \r\n    \/\/ but calls its Invoke method whenever an API is used).\r\n    public static T Decorate(T target = null)\r\n    {\r\n        \/\/ DispatchProxy.Create creates proxy objects\r\n        var proxy = Create&lt;T, DispatchProxyLoggingDecorator&lt;T&gt;&gt;()\r\n            as DispatchProxyLoggingDecorator&lt;T&gt;;\r\n\r\n        \/\/ If the proxy wraps an underlying object, it must be supplied after creating\r\n        \/\/ the proxy.\r\n        proxy.Target = target ?? Activator.CreateInstance&lt;T&gt;();\r\n\r\n        return proxy as T;\r\n    }\r\n\r\n    \/\/ The invoke method is the heart of a DispatchProxy implementation. Here, we\r\n    \/\/ define what should happen when a method on the proxied object is used. The\r\n    \/\/ signature is a little simpler than RealProxy's since a MethodInfo and args\r\n    \/\/ are passed in directly.\r\n    protected override object Invoke(MethodInfo targetMethod, object[] args)\r\n    {\r\n        try\r\n        {\r\n            \/\/ Perform the logging that this proxy is meant to provide\r\n            _logger.Information(\"Calling method {TypeName}.{MethodName} with arguments {Arguments}\", targetMethod.DeclaringType.Name, targetMethod.Name, args);\r\n\r\n            \/\/ For this proxy implementation, we still want to call the original API \r\n            \/\/ (after logging has happened), so use reflection to invoke the desired\r\n            \/\/ API on our wrapped target object.\r\n            var result = targetMethod.Invoke(Target, args);\r\n\r\n            \/\/ A little more logging.\r\n            _logger.Information(\"Method {TypeName}.{MethodName} returned {ReturnValue}\", targetMethod.DeclaringType.Name, targetMethod.Name, result);\r\n\r\n            return result;\r\n        }\r\n        catch (TargetInvocationException exc)\r\n        {\r\n            \/\/ If the MethodInvoke.Invoke call fails, log a warning and then rethrow the exception\r\n            _logger.Warning(exc.InnerException, \"Method {TypeName}.{MethodName} threw exception: {Exception}\", targetMethod.DeclaringType.Name, targetMethod.Name, exc.InnerException);\r\n\r\n            throw exc.InnerException;\r\n        }\r\n    }\r\n}\r\n<\/code><\/pre>\n<p>Using a proxy object generated by <code>DispatchProxy.Create<\/code> is very similar to using one generated from <code>RealProxy<\/code> except that the type of the object is actually the type of the proxy (not the underlying object), which implements the proxied interface. One of the challenges with this is that fields on the proxied type won&#8217;t be available (since they won&#8217;t be present on the interface). To work around this, the proxy can expose the target object or helper methods could be added to the proxy type that allow accessing fields on the target object via reflection, but workarounds like this are a bit messy.<\/p>\n<pre><code class=\"CSharp\">var undecoratedWidget = new Widget(\"Widgetty\", 9.99);\r\n\r\n\/\/ Note that the proxy is of type IWidget rather than Widget.\r\n\/\/ The returned object is actually of type DispatchProxyLoggingDecorator\r\n\/\/ (so any helper methods on that type can be used in addition to IWidget APIs)\r\nvar widget = DispatchProxyLoggingDecorator&lt;IWidget&gt;.Decorate(undecoratedWidget);\r\n<\/code><\/pre>\n<h3>Pros and cons of DispatchProxy<\/h3>\n<ul>\n<li><strong>Pros<\/strong>\n<ul>\n<li>Works with .NET Standard 1.3+ (so it works with both .NET Framework 4.6+ and .NET Core\/.NET 5).<\/li>\n<li>Does not require proxied types to derive from <code>MarshalByRefObject<\/code>.<\/li>\n<li>Proxies by wrapping the target object, so a proxy can be created around an already existing object.<\/li>\n<\/ul>\n<\/li>\n<li><strong>Cons<\/strong>\n<ul>\n<li>Proxies interfaces, not classes, so proxied types must implement an interface and access to any members not in the interface (like fields) is complicated.<\/li>\n<li>Proxies calls to underlying objects by wrapping the target object. Therefore, if the object being wrapped makes subsequent calls to its own APIs as part of executing a method, those member accesses will not be proxied. For example, if <code>Widget.Buy()<\/code> uses <code>Widget.Price<\/code> internally, calling <code>Buy()<\/code> on an object proxied using the sample <code>RealProxy<\/code> implementation earlier would log the call to <code>Buy<\/code> but not the subsequent call to <code>Price<\/code>.<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<h2>DynamicProxy as an alternative<\/h2>\n<p>In most cases, AOP patterns can be implemented either by <code>DispatchProxy<\/code> or <code>RealProxy<\/code> (if targeting .NET Framework). If those don&#8217;t meet your needs, though, there are a variety of third party options available. One popular third-party alternative is Castle.Core&#8217;s <a href=\"http:\/\/www.castleproject.org\/projects\/dynamicproxy\/\">DynamicProxy<\/a>. Unlike <code>DispatchProxy<\/code>, <code>DynamicProxy<\/code> <a href=\"https:\/\/www.nuget.org\/packages\/Castle.Core\/\">works on .NET Framework 3.5 and 4.0<\/a> (in addition to .NET Standard 1.3), so it can be used on older .NET Framework versions.<\/p>\n<p>DynamicProxy is also unique in that it provides <a href=\"https:\/\/github.com\/castleproject\/Core\/blob\/master\/docs\/dynamicproxy-kinds-of-proxy-objects.md#composition-based\">a couple different proxying models<\/a>, so you can choose the one that fits your scenario best. It can proxy by wrapping a target type (this is called composition-based proxying) just like <code>DispatchProxy<\/code>. In this model, the proxy type implements an interface and can, optionally, route proxied calls to an internal target object &#8211; just like <code>DispatchProxy<\/code>. Alternatively, <code>DynamicProxy<\/code> supports inheritance-based proxying, meaning that the proxy object actually derives from the target type and &#8216;intercepts&#8217; API calls by just overriding them. This approach means that subsequent internal calls made by the target object will still be proxied (since the methods are overridden) but it comes with a variety of downsides, as well. First, inheritance-based proxying means that only virtual methods can be proxied and, secondly, because the proxy and the target object are one in the same, it&#8217;s not possible to create a proxy wrapper around an object that already exists. For a more in-depth look at <code>DynamicProxy<\/code>, check out <a href=\"http:\/\/kozmic.net\/dynamic-proxy-tutorial\/\">this tutorial<\/a>.<\/p>\n<p>When using <code>DynamicProxy<\/code>, you create <code>IInterceptor<\/code> types that are responsible for handling calls to the proxy object. A simple logging interceptor might look like this:<\/p>\n<pre><code class=\"CSharp\">\/\/ Simple sample logging interceptor for DynamicProxy\r\nclass DynamicProxyLoggingInterceptor : IInterceptor\r\n{\r\n    \/\/ The Serilog logger to be used for logging.\r\n    private readonly Logger _logger;\r\n\r\n    \/\/ Constructor that initializes the Logger the interceptor will use\r\n    public DynamicProxyLoggingInterceptor(string typeName = null) \r\n    {\r\n        \/\/ Setup the Serilog logger\r\n        _logger = new LoggerConfiguration()\r\n            .WriteTo.Console().CreateLogger();\r\n        _logger.Information($\"New logging decorator created{(string.IsNullOrWhiteSpace(typeName) ? string.Empty : \" for object of type {TypeName}\")}\", typeName);\r\n    }\r\n\r\n    \/\/ The Intercept method is where the interceptor decides how to handle calls to the proxy object\r\n    public void Intercept(IInvocation invocation)\r\n    {\r\n        try\r\n        {\r\n            \/\/ Perform the logging that this proxy is meant to provide\r\n            _logger.Information(\"Calling method {TypeName}.{MethodName} with arguments {Arguments}\", invocation.Method.DeclaringType.Name, invocation.Method.Name, invocation.Arguments);\r\n\r\n            \/\/ Invocation.Proceeds goes on to the next interceptor or, if there are no more interceptors, invokes the method.\r\n            \/\/ The details of how the method are invoked will depend on the proxying model used. The interceptor does not need\r\n            \/\/ to know those details.\r\n            invocation.Proceed();\r\n\r\n            \/\/ A little more logging.\r\n            _logger.Information(\"Finished calling method {TypeName}.{MethodName}\", invocation.Method.DeclaringType.Name, invocation.Method.Name);\r\n        }\r\n        catch (TargetInvocationException exc)\r\n        {\r\n            \/\/ If the subsequent invocation fails, log a warning and then rethrow the exception\r\n            _logger.Warning(exc.InnerException, \"Method {TypeName}.{MethodName} threw exception: {Exception}\", invocation.Method.DeclaringType.Name, invocation.Method.Name, exc.InnerException);\r\n\r\n            throw exc.InnerException;\r\n        }\r\n    }\r\n}\r\n<\/code><\/pre>\n<p>The proxy object itself is created by calling methods on the <code>ProxyGenerator<\/code> type. <a href=\"https:\/\/github.com\/castleproject\/Core\/blob\/master\/docs\/dynamicproxy-kinds-of-proxy-objects.md\">Different APIs<\/a> are used depending on the proxying model being used. Helper methods for creating an inheritance-based proxy and composition-based proxy might look like this:<\/p>\n<pre><code class=\"CSharp\">public class DynamicProxyLoggingDecorator\r\n{\r\n    \/\/ ProxyGenerator is used to create DynamicProxy proxy objects\r\n    private static readonly ProxyGenerator _generator = new ProxyGenerator();\r\n\r\n    \/\/ CreateClassWithProxy uses inheritance-based proxying to create a new object that actually\r\n    \/\/ derives from the provided class and applies interceptors to all APIs before\r\n    \/\/ invoking the base class's implementation. In this model, the proxy\r\n    \/\/ object and target object are the same.\r\n    public static T DecorateViaInheritance&lt;T&gt;() where T: class =&gt;\r\n        _generator.CreateClassProxy&lt;T&gt;(new DynamicProxyLoggingInterceptor(typeof(T).Name));\r\n\r\n    \/\/ CreateInterfaceProxyWithTarget uses composition-based proxying to wrap a target object with\r\n    \/\/ a proxy object implementing the desired interface. Calls are passed to the target object\r\n    \/\/ after running interceptors. This model is similar to DispatchProxy.\r\n    public static T DecorateViaComposition&lt;T&gt;(T target = null) where T: class\r\n    {\r\n        var proxy = target != null ?\r\n            _generator.CreateInterfaceProxyWithTarget(target, new DynamicProxyLoggingInterceptor(typeof(T).Name)) :\r\n\r\n            \/\/ There is also a CreateInterfaceProxyWithoutTarget method but that API assumes there is no wrapped object at all,\r\n            \/\/ and only the interceptors are called. That model can be useful but doesn't match the logging scenario used in this sample.\r\n            _generator.CreateInterfaceProxyWithTarget&lt;T&gt;(Activator.CreateInstance(typeof(T)) as T, new DynamicProxyLoggingInterceptor(typeof(T).Name));\r\n\r\n        return proxy;\r\n    }\r\n}\r\n<\/code><\/pre>\n<h3>Pros and cons of DynamicProxy<\/h3>\n<ul>\n<li><strong>Pros<\/strong>\n<ul>\n<li>Works on both .NET Core\/.NET 5 and .NET Framework 3.5+<\/li>\n<li>Support for multiple proxying patterns adds flexibility<\/li>\n<li>The inheritance-based proxy option enables methods called from within a proxy object to also be proxied (though this has its own drawbacks)<\/li>\n<li>Does not require <code>MarshalByRefObjects<\/code>; can work with interfaces or classes with virtual members<\/li>\n<li>The <code>IInterceptor<\/code> model makes it easy to combine multiple proxy behaviors<\/li>\n<\/ul>\n<\/li>\n<li><strong>Cons<\/strong>\n<ul>\n<li>Proxies are based on interfaces or virtual members on classes, so proxied types must implement interesting APIs according to one of those patterns<\/li>\n<li>Unlike <code>RealProxy<\/code>, cannot proxy field access, though inheritance-based proxying at least allows access to fields more easily than <code>DispatchProxy<\/code> (interceptors are not invoked)<\/li>\n<li>More complicated than other options (multiple proxy creation methods, <code>IInterceptor<\/code>, etc.)<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<h2>Summary and Wrap-up<\/h2>\n<p>The different proxy APIs discussed above all have their own pros and cons. None will be right for all scenarios, but hopefully this post helps explain how AOP patterns can still be used in .NET 5 without needing <code>System.Runtime.Remoting.Proxies.RealProxy<\/code>. In most cases, <code>DispatchProxy<\/code> can be dropped in as a straight-forward replacement. And if an inheritance-based proxying pattern would be more useful, or you need the flexibility of the <code>IInterceptor<\/code> model or the ability to proxy virtual methods not from an interface, Castle.Core&#8217;s <code>DynamicProxy<\/code> is a great option.<\/p>\n<p>As mentioned earlier, the code snippets from this blog post are available (in the context of a sample project exercising the different proxy APIs) on <a href=\"https:\/\/github.com\/mjrousos\/ProxyExploration\">GitHub<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>The RealProxy type is not available in .NET Core or .NET 5, but other replacement types have been added. This post looks at how DispatchProxy and Castle.Core&#8217;s DynamicProxy enable AOP paradigms in .NET 5.<\/p>\n","protected":false},"author":7413,"featured_media":30464,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[685,196],"tags":[],"class_list":["post-31529","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-dotnet","category-dotnet-core"],"acf":[],"blog_post_summary":"<p>The RealProxy type is not available in .NET Core or .NET 5, but other replacement types have been added. This post looks at how DispatchProxy and Castle.Core&#8217;s DynamicProxy enable AOP paradigms in .NET 5.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/31529","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/users\/7413"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/comments?post=31529"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/31529\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/media\/30464"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/media?parent=31529"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/categories?post=31529"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/tags?post=31529"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}