Click here to Skip to main content
15,861,168 members
Articles / Programming Languages / C#
Article

Dynamic/Transparent Proxy using DynamicProxy.NET

Rate me:
Please Sign up or sign in to vote.
4.90/5 (19 votes)
21 Oct 20039 min read 208.8K   3.9K   78   9
Learn how easy it is to create Dynamic/Transparent proxies in .NET using DynamicProxy.NET

Introduction

The Java community has had the joy of dynamic proxies since JDK 1.3 and .NET has had its counterpart, the TransparentProxy, since .NET version 1.0. However, the .NET TransparentProxy isn't as easy to use as its Java counterpart. This is why DynamicProxy.NET was made.

What's a proxy?

Before we continue to look at DynamicProxy.NET it might be a good idea to recapture what a proxy is and what it can be used for. If you're familiar with proxies, just skip down to the next section.

The proxy design pattern was first made popular and well documented in the Gang Of Four (a.k.a. GoF) book: Design Patterns: Elements of Reusable Object-Oriented Software by Erich Gamma (Eclipse), Richard Helm, Ralph Johnson and John Vlissides (a.k.a. the Gang of Four). The intent of the proxy pattern is to "Provide a surrogate or placeholder for another object to control access to it". An example of this is to control the security for the object (protection proxy), defer its instantiation until required (virtual proxy) and access to a remote object (remote proxy). There are other types of proxies such as a caching proxy, only your imagination sets the limit. The beauty of it is that the core of the pattern is the same.

The "bad" thing about proxies is that you have to make a replica class for the class you wish to proxy. This means, that if you wish to make a proxy for an Image, say you wish to make a virtual proxy so the content of the image first is loaded when it's supposed to be rendered, you would have to create a new ImageProxy class by hand, which has the same methods as the Image class. This is where DynamicProxy.NET comes to the rescue; instead of creating the proxy by hand, the DynamicProxy.NET will automatically, at runtime, create a dynamic/transparent proxy for the object you wish to proxy. Sounds like magic? Well it isn't, it's just part of the beauty of platforms such as Java and .NET (and other dynamic languages for that matter).

Creating a proxy without DynamicProxy.NET

In the tutorial project, DynamicProxyTutorial, we've made a simple FileViewer class which we want to create protection proxy for. Before we start coding we need to be aware of a special requirement that DynamicProxy.NET has:

Requirement: DynamicProxy.NET requires your code to use interface based implementation.

What this means is that you separate interface and implementation, so you for instance have an interface called ISimpleInterface which defines the methods. You then create a class, for instance called SimpleClass, which implements the interface ISimpleInterface. .NET supports implementing more than one interface and DynamicProxy.NET also supports proxying of more than one interface.

This is the reason why the FileViewer class implements the IFileViewer interface. A class as simple as this, could normally do without an interface.

IFileViewer.cs - the interface for the file viewer:

C#
using System;

namespace DynamicProxyTutorial
{
    /// <summary>
    /// Interface for a fileviewer (a really simple one).
    /// NB: An interface is required if you wish to use DynamicProxy.NET
    /// </summary>
    public interface IFileViewer
    {
        /// <summary>
        /// Opens the file, reads the content and closes the file afterwards.
        /// We perform the reading in a method,
        /// since you can't proxy constructor calls
        /// (which for this simple sclass would
        /// be the best place to load the content).
        /// </summary>
        void ReadFromFile();

        /// <summary>
        /// Returns the content of the file.
        /// Please call ReadFromFile first.
        /// </summary>
        string Content {
            get;
        }

        /// <summary>
        /// Returns the path for the file being viewed
        /// </summary>
        string FilePath {
            get;
        }
    }
}

FileViewer.cs - the file viewer implementation:

C#
using System;
using System.IO;

namespace DynamicProxyTutorial
{
    /// <summary>
    /// File Viewer implementation.
    /// </summary>
    public class FileViewer : IFileViewer {
        private String content;
        private String filePath;

        public FileViewer(string filePath) {
            this.filePath = filePath;
        }

        public void ReadFromFile() {
            using (StreamReader reader = File.OpenText(filePath)) {
                content = reader.ReadToEnd();
            }
        }

        public string Content {
            get {
                return content;
            }
        }

        public string FilePath {
            get {
                return filePath;
            }
        }
    }
}

The point of this simple example is to protect the ReadFromFile() method, which accesses a file on the hard drive. To protect that file from being read in the proxy, we "invent" our own protection system, where the user/programmer can specify which user ID is required to be allowed to access the file. This user ID, called the allowedUserID in the code, is compared to the user ID, or rather the Name of the current WindowsIdentity, before allowing file access. This is not how it should be done in real life, but it serves as a simple working example.

Since we're going to implement the first proxy by hand, we have to create a new class, here called FileViewerProxy, that must implement the IFileViewer interface. The constructor takes the path to the file being viewed as well as the user ID of the user that should be allowed to view the file.

FileViewerProxy.cs - the hand made proxy:

C#
using System;
using System.IO;
using System.Security.Principal;

namespace DynamicProxyTutorial {

    /// <summary>
    /// Hand made proxy for the IFileViewer.
    /// </summary>
    public class FileViewerProxy : IFileViewer {
        /// <summary>
        /// User ID that is allowed to read/write from files
        /// </summary>
        private string allowedUserID;
        /// <summary>
        /// The real fileviewer that we're proxying
        /// </summary>
        private IFileViewer realFileViewer;

        public FileViewerProxy(string filePath, string allowedUserID) {
            this.allowedUserID = allowedUserID;
            realFileViewer = new FileViewer(filePath);
        }

        /// <summary>
        /// Checks if the current user is the same as the allowed user.
        /// If it isn't a SecurityException is thrown
        /// </summary>
        /// <exception cref="SecuritException">If the
        /// current user isn't allowed to
        /// read/write from the file, this exception
        /// is thrown</exception>
        private void checkPermission() {
            if (WindowsIdentity.GetCurrent().Name != allowedUserID) {
                throw new SecurityException("From : " +
                        this.GetType().Name + "\r\nUserID '" +
                        WindowsIdentity.GetCurrent().Name +
                        " isn't allowed to read/write from the file: '" +
                        realFileViewer.FilePath + "'. " +
                        "Allowed user: '" + allowedUserID + "'");
            }
        }

        // ---------------- Proxy methods  -------------
        // It's these method wrappers that DynamicProxy.NET creates
        // for you in a generic way

        public void ReadFromFile() {
            checkPermission();
            realFileViewer.ReadFromFile();
        }

        public string Content {
            get {
                return realFileViewer.Content;
            }
        }

        public string FilePath {
            get {
                return realFileViewer.FilePath;
            }
        }
    }
}

As you can see, we have to implement our own delegating version of all the methods in the IFileViewer interface. In this case it isn't that bad, but consider a medium sized class, with 20 methods and 5 properties. It takes some time to create this by hand, plus you have to keep it in sync with changes to the interface. In the example, only the ReadFromFile() method is checked for security constraints, since it's where all the interesting file handling happens. In the proxying ReadFromFile() method, we call checkPermissions before calling the real FileViewer instance. If the permissions aren't satisfied, the checkPermissions method will throw a SecurityException, which will be propagated all the way to the main application.

Creating a proxy with DynamicProxy.NET

Instead of creating the proxied object methods and properties by hand, you can instead use DynamicProxy.NET. In this example we have some additional data, namely the allowedUserID, that we need to keep for each proxy instance, so we decided to encapsulate it in a class of its own. The implementation can be found in the class DynamicFileViewerProxy.

DynamicFileViewerProxy.cs - the DynamicProxy.NET implementation:

C#
    using System;
    using System.Reflection;
    using System.Security.Principal;
    using Cramon.NetExtension.DynamicProxy;

    namespace DynamicProxyTutorial    {
        ///    <summary>
        /// Class the encapsulates that logic for
        /// checking permissions on a dynamic proxy.
        /// This class utilizes the DynamicProxy.NET
        /// framework to create a dynamic proxy for a FileViewer.
        /// The reason why we need to encapsulate this,
        /// is that we need a place to place the allowedUserID and
        /// the invocation handler delegate.
        /// </summary>
        public class DynamicFileViewerProxy    {

        private IDynamicProxy dynamicFileViewerProxy;
        private string allowedUserID;
        public DynamicFileViewerProxy(string filePath,
                                   string allowedUserID) {

            this.allowedUserID = allowedUserID;
            // Create the real FileViewer that we wish to proxy
            IFileViewer realViewer = new FileViewer(filePath);
            // Create a dynamic proxy for the real FileViewer.
            // DynamicProxy.NET will automatically
            // create a dynamic proxy instance that
            // implements the methods that FileViewer exposes.
            dynamicFileViewerProxy =
                  (IDynamicProxy)
                  DynamicProxyFactory.Instance.CreateProxy
                  (realViewer,
                  new InvocationDelegate(invocationHandler));
        }

        public IFileViewer Proxy {
            get {
                return (IFileViewer)dynamicFileViewerProxy;
            }
        }

        /// <summary>
        /// Checks if the current user is
        /// the same as the allowed user.
        /// If it isn't a SecurityException is thrown.
        /// (same as in FileViewerProxy)
        /// </summary>
        /// <exception cref="SecuritException">If the
        /// current user isn't allowed to
        /// read/write from the file, this exception
        /// is thrown</exception>
        private void checkPermission() {
            if (WindowsIdentity.GetCurrent().Name != allowedUserID) {
                // Get a hold of the proxy we're proxying for
                IFileViewer realFileViewer =
                   (IFileViewer)dynamicFileViewerProxy.ProxyTarget;
                throw new SecurityException("From : " +
                    this.GetType().Name + "\r\nUserID '"
                    + WindowsIdentity.GetCurrent().Name +
                    " isn't allowed to read/write from the file: '" +
                    realFileViewer.FilePath + "'. " +
                    "Allowed user: '" + allowedUserID + "'");
            }
        }


        private object invocationHandler(object target,
                MethodBase method, object[] parameters) {
            // Target contains the object that we are proxying
            // (for this example to work, it's not actually
            // necessary to cast the target to the real type).
            // With the implementation we have made here we
            // already know the target object (because we store
            // it in the dynamicFileViewerProxy). The reason
            // why the target is provided is that it makes it
            // possible to create really generic invocationHandlers
            // which can handle invocations for
            // many different target objects.
            IFileViewer realViewer = (IFileViewer)target;

            // Do the work of our protection proxy
            // (but only for the ReadFromFile method)
            if (method.Name == "ReadFromFile") {
                checkPermission();
            }

            // Call the method the user requested on the real target
            return method.Invoke(realViewer, parameters);
        }
    }
}

A part from the checkPermission() method and the constructor where we create a FileViewer instance to proxy, all the implementation is kept in the invocationHandler() method. The ReadFromFile() and the two properties have been cut down to just 3 lines (if you remove the comments and unnecessary cast). The line:

C#
dynamicFileViewerProxy
      = (IDynamicProxy)DynamicProxyFactory.Instance.CreateProxy(realViewer,
      new InvocationDelegate(invocationHandler));

is where the the dynamic proxy is created. It uses the CreateProxy() method in the DynamicProxyFactory to create the dynamic proxy instance. The tricky part is that you need to tell the dynamic proxy what it should do when you call a method in it. For this we have the InvocationDelegate. In the code above, we create a new InvocationDelegate to the method invocationHandler. The invocationHandler is our implementation of the InvocationDelegate. Every time a method is called on the dynamic proxy instance, the invocationHandler() method is called. The invocationHandler() is the controller, which is actively controlling the method invocation. The InvocationHandler receives three parameters for each invocation:

  • target: which is a reference to the real object being proxied. In this example, the target will refer to the FileViewer instance.
  • method: the method instance for the method that was called. The Method class comes from the reflection API.
  • parameters: an array containing all the parameters that was given to the method.

When you're done doing what ever it is that your proxy is supposed to do, in our case check security, we call the real method on the target object, in the case the FileViewer instance. This is again done using the reflection API again using this line of code: return method.Invoke(realViewer, parameters);.

If you wish to get more information and details on the DynamicProxy.NET API, please refer to the online documentation.

The tutorial application

To make the tutorial more accessible, we've built a Windows application from where you can control the parameters of the application.

Image 1

The checkbox "Use DynamicProxy.NET" decides whether the handmade FileViewerProxy or the DynamicProxy.NET proxy, DynamicFileViewerProxy class, is used. The "UserID allowed to view files" textbox specifies the user ID that is allowed to view the various files. It defaults to the value of the currently logged on user. Press the "Get Current User" to retrieve its information again. Press the "Select a File" button to select which file to view. We'll cover the "Compare Proxy Performance" in the performance section.

When a file has been selected, it is shown in the "large" textbox on the right hand side of the window. The code for creating the right proxy type is encapsulated in the FileViewerProxyFactory. This was done to keep the code out of the MainForm class.

FileViewerProxyFactory.cs - the factory for creating new proxy instances:

C#
using System;

namespace DynamicProxyTutorial {
    /// <summary>
    /// Simple factory (also a pattern from GoF)
    /// that encapsulates the creation of
    /// proxies for the IFileViewer interface.
    /// There's no requirement for encapsulting this in a factory,
    /// it's just a good idea to keep this
    /// seperate from the usage of the fileviewer
    /// </summary>
    public class FileViewerProxyFactory {

        /// <summary>
        /// Keep the constructor private,
        /// to prevent instantiations.
        /// </summary>
        private FileViewerProxyFactory() {
        }

        /// <summary>
        /// Static method to create new proxy instances
        /// </summary>
        /// <param name="filePath">The path to
        /// the file we want to view</param>
        /// <param name="allowedUserID">The userid for
        /// the user that's allowed to view the file</param>
        /// <param name="useDynamicProxyNET">If true we
        /// use DynamicProxy.NET to create a proxy at runtime, else
        /// we use the handwritten FileViewerProxy class</param>
        /// <returns>A new File Viewer
        /// proxy instance</returns>
        public static IFileViewer createProxy(string filePath,
             string allowedUserID, bool useDynamicProxyNET) {
            if (useDynamicProxyNET) {
                return new DynamicFileViewerProxy(filePath,
                                        allowedUserID).Proxy;
            } else {
                return new FileViewerProxy(filePath, allowedUserID);
            }
        }
    }
}

The FileViewerProxyFactory is being called from the selectFileButton_Click() method.

selectFileButton_Click - the main method in the MainForm tutorial application:

C#
private void selectFileButton_Click(object sender, System.EventArgs e) {
    // Show the Open file dialog
    DialogResult dialogResult = selectFileDialog.ShowDialog();
    // If the user selected Open then start the fileviewing process
    if (dialogResult == DialogResult.OK) {
        // Clear the old content of the content box
        fileContentTextBox.Text = "";

        // Create a protection proxy for a fileviewer
        IFileViewer fileViewer =
           FileViewerProxyFactory.createProxy
             (selectFileDialog.FileName,
             allowedUserID.Text,
             useDynamicProxyCheckBox.Checked);
        // Try reading the content
        try {
            fileViewer.ReadFromFile();
        } catch (SecurityException exception) {
            MessageBox.Show(this, exception.Message,
               "Security Exception", MessageBoxButtons.OK,
               MessageBoxIcon.Error);
            return;
        }
        // Show the content
        fileContentTextBox.Text = fileViewer.Content;
    }
}

If the fileViewer.ReadFromFile() method succeeds, the fileContentTextBox is updated with the contents of the file (we recommend only opening text files ;-) ). If a security constraint isn't met and a SecurityException is thrown a message box will explain what went wrong:

Image 2

DynamicProxy.NET performance

How expensive is DynamicProxy.NET?

There's no doubt that proxying as a whole requires more memory and CPU time than no proxying at all, but with the speed of our processor and the amount of memory our computers have, this is not a big deal. What's more interesting is how much slower, CPU wise, is the DynamicProxy.NET approach compared to the hand made proxy? To give you a clue to how the two compare, we did a simple and non-scientific performance measurement. This performance test can be performed by clicking the "Compare Proxy Performance" button.

The result from my machine, a Intel Centrino 1.3 GHz, 512 MB RAM portable, is shown below:

Performance Test is running... 
================================================================== 
Tests per category : 1000000 
GC wait per category: 5000 ms. 
------------------------------------------------------------------ 
Average time to create Hand made proxy: 0,00036 ms. 
Average time to create Dynamic proxy: 0,003054 ms. 
Average time to call Content property Hand made proxy: 5E-05 ms. 
Average time to call Content property Dynamic Proxy: 0,01444 ms.
================================================================== 
Performance Test is done... 

As you can see, the creation time for a dynamic proxy compared to a hand made one is about 10 times more expensive, but still only takes 0.003 milliseconds. The most expensive part of the DynamicProxy.NET approach is the method/property invocation part. It takes in average 0.015 milliseconds to invoke a method on a dynamic proxy, whereas it takes so little time, that it's almost beyond the scope of millisecond resolution timers, to invoke a method in the handmade proxy using direct method invocation. This difference is mainly due to the fact that reflection is used in DynamicProxy.NET and that reflective programming always will be slower than direct method invocation.

Conclusion

Proxy is a really good design pattern for many uses and DynamicProxy.NET makes it easy to create proxies dynamically at runtime, while still giving you good performance. This tutorial is by no means a complete guide to proxies nor to the features of the Transparent/Real proxy support, which DynamicProxy.NET relies upon, in .NET. We hope you've seen the idea and will start using proxies in your own applications where they make sense. Unless you have a really high performance system running many invocations, we would advocate using the DynamicProxy.NET approach compared to doing the proxies by hand. It saves you a lot of time and it's easy to work with. Should you, when profiling your application, find a performance problem caused by DynamicProxy.NET, you can always change that particular proxy with a handmade one and get better performance, while maintaining the advantages you can gain with proxies.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
Denmark Denmark
10 years experience in software design. In my job as a software architect I work with Java and J2EE. .net is so far only a hobby project, but nonetheless interesting Wink | ;-)

Comments and Discussions

 
QuestionAnother approach using aal.codeplex.com Pin
Ulrik Born21-May-14 20:43
Ulrik Born21-May-14 20:43 
GeneralMy vote of 5 Pin
Peter C Camilleri25-Mar-13 9:59
Peter C Camilleri25-Mar-13 9:59 
GeneralVery good article.... Pin
Zac Papac31-Jan-06 10:22
Zac Papac31-Jan-06 10:22 
GeneralViewing the properties of the proxied interface Pin
Guy Vinograd17-Feb-05 9:33
Guy Vinograd17-Feb-05 9:33 
GeneralRe: Viewing the properties of the proxied interface Pin
Guy Vinograd17-Feb-05 9:43
Guy Vinograd17-Feb-05 9:43 
GeneralGreat article Pin
YSelf13-Dec-04 13:06
YSelf13-Dec-04 13:06 
Questionhow to intercept self calls Pin
poyeah23-Mar-04 22:15
poyeah23-Mar-04 22:15 
Generallinks are broken Pin
NelsonKL17-Oct-03 11:19
NelsonKL17-Oct-03 11:19 
GeneralRe: links are broken Pin
SKI_BUM21-Oct-03 11:20
SKI_BUM21-Oct-03 11:20 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.