The elegance of anonymous delegates

Among other things, the new anonymous delegates feature of .NET 2.0 provide a really nice way of plugging arbitrary code into an outer shell that you want to reuse. I first saw this technique when Keith used it in a project we were working on to create some ADO.NET helper methods, and it's been written about in several places. I just used it today as I was writing a bunch of methods that all needed to impersonate the user temporarily to make secure calls to a Web service using WSE 3.0 Kerberos authentication. Here's what a typical method looked like before factoring out the impersonation with anonymous delegates:
 
public static EmployeeDetails FindEmployee(int empId)
{
  WindowsIdentity id = HttpContext.Current.User.Identity as WindowsIdentity;
  WindowsImpersonationContext wic = id.Impersonate();
  try
  {
    BrowseOrganizationServiceWse bosw = new BrowseOrganizationServiceWse();
    bosw.SetPolicy("kerberos");
    FindEmployeeResult fer = bosw.FindEmployee(empId);
    EmployeeDetails ed = fer.Item as EmployeeDetails;
    return ed;
  }
  finally
  {
    wic.Undo();
  }
}
 
 
And here's the method I wrote to consolidate the impersonation code into a single location that all my methods could tap into (note I used the ThreadStart delegate just because it has a void() signature and saved me from having to define a new delegate type - this has nothing to do with multi-threading):
 
private static void RunMethodImpersonating(ThreadStart method)
{
  WindowsIdentity id = HttpContext.Current.User.Identity as WindowsIdentity;
  WindowsImpersonationContext wic = id.Impersonate();
  try
  {
    method();
  }
  finally
  {
    wic.Undo();
  }
}
 
UPDATED: I had over-simplified the example for presentation and it wouldn't actually compile - so here's the corrected version.
 
Finally, here's what the method looks like after using the new helper method:
 
public static EmployeeDetails FindEmployee(int empId)
{
  EmployeeDetails ed = null;
 
  RunMethodImpersonating(
    delegate
    {
      BrowseOrganizationServiceWse bosw = new BrowseOrganizationServiceWse();
      bosw.SetPolicy("kerberos");
      FindEmployeeResult fer = bosw.FindEmployee(empId.ToString());
      ed = fer.Item as EmployeeDetails;
    }
  );
 
   return ed;
}
 
This kind of factoring is something I might have done using macros in C++, but anonymous delegates are a much nicer (and typesafe) way of doing it.
 
My only concern with this technique is how readable it is - it certainly takes a second glance to understand what's going on. I could even have taken it a step further and instantiated the Web service proxy and set it's WSE policy inside my helper method, but that would mean the methods that used it would have to implicitly reference a local variable declared in the outer helper method (bosw in this case) which just feels a little weird (just like the Container.DataItem in ASP.NET databinding expressions).
 
UPDATE: Several comments were made about how this particular example could be solved nicely with an IDisposable class wrapper, so here's the same example using an IDisposable wrapper (which I actually prefer for this case).
 
internal class ImpersonationScope : IDisposable
{
  WindowsIdentity _id;
  WindowsImpersonationContext _wic;
  public ImpersonationScope()
  {
    _id = HttpContext.Current.User.Identity as WindowsIdentity;
    _wic = _id.Impersonate();
  }
  public void Dispose()
  {
    _wic.Undo();
  }
}
 
And the FindEmployee implementation becomes:
 
public static EmployeeDetails FindEmployee(int empId)
{
  using (ImpersonationScope scp = new ImpersonationScope())
  {
    BrowseOrganizationServiceWse bosw = new BrowseOrganizationServiceWse();
    bosw.SetPolicy("kerberos");
    FindEmployeeResult fer = bosw.FindEmployee(empId.ToString());
    EmployeeDetails ed = fer.Item as EmployeeDetails;
    return ed;
  }
}

Posted Jun 20 2005, 02:10 PM by fritz-onion
Filed under:

Comments

krumpo wrote re: The elegance of anonymous delegates
on 06-20-2005 12:58 PM
Hello,

and what about simple IDisposable helper e.g. WindowsImpersonationScope with constructor calling Impersonate() and Dispose calling Undo ?

using (WindowsImpersonationScope wis = new WindowsImpersonationScope())
{
BrowseOrganizationServiceWse bosw = new BrowseOrganizationServiceWse();
bosw.SetPolicy("kerberos");
FindEmployeeResult fer = bosw.FindEmployee(empId.ToString());
EmployeeDetails ed = fer.Item as EmployeeDetails;
return ed;
}

It is concise and explicit
Fritz Onion wrote re: The elegance of anonymous delegates
on 06-20-2005 1:04 PM
Hmm, good point, for this particular example that does work well, and is arguably more readable. I may have to switch...
Diego Mijelshon wrote re: The elegance of anonymous delegates
on 06-20-2005 3:13 PM
I've been doing that for a while. Here's a simple class that does the trick:

using System;
using System.Security.Principal;
using System.Web;

namespace WildSoft.Security
{
public class Impersonation : IDisposable
{
public Impersonation()
{
context = (HttpContext.Current.User.Identity as WindowsIdentity).Impersonate();
}

public void Dispose()
{
context.Undo();
}

WindowsImpersonationContext context;
}
}
Fritz Onion wrote re: The elegance of anonymous delegates
on 06-20-2005 3:50 PM
Yep - I already wrote the same class and plugged it in, works great.
AnonymousCoward wrote re: The elegance of anonymous delegates
on 06-20-2005 7:44 PM
Is anyone else thinking Java anonymous inner classes?
Christopher Steen wrote Link Listing - June 20, 2005
on 06-20-2005 8:13 PM
Link Listing - June 20, 2005
Jason Whittington wrote re: The elegance of anonymous delegates
on 06-20-2005 9:44 PM
You've discovered closures, Fritz :) I blogged about this awhile back:

http://neocranium.com/jasonw/archive/2005/02/01/329.aspx

Ian of course, has a long insightful reply... :)
Fritz Onion wrote re: The elegance of anonymous delegates
on 06-21-2005 3:08 AM
Yes Jason - hence the reference to your and Ian's post at the beginning of mine :)
Jigar wrote re: The elegance of anonymous delegates
on 06-21-2005 4:30 AM
Similar to this

http://www.c-sharpcorner.com/Code/2005/May/AnonymusDelegate.asp
Jason Whittington wrote re: The elegance of anonymous delegates
on 06-21-2005 9:03 AM
Sorry Fritz, missed your link. I generally agree with the goodness though - super cool stuff. The "capture" ability makes a lot of things so easy. Personally I like this style though maybe it can be taken too far...?

using System;

public delegate int proc(); class App{static proc zero = delegate { return 0; };static proc s(proc a) { return delegate { return a()+1; }; } static proc sum(proc a, proc b) { return delegate { return a() + b(); }; } static proc diff(proc a, proc b) { return delegate { return a() - b(); }; }static proc f(proc p){ if (p() == s(zero)() || p() == s(s(zero))())return s(zero); else return sum(f(diff(p, s(zero))), f(diff(p, s(s(zero)))));}static void Main(){for (proc p = s(zero); p() < s(s(s(s(s(s(s(s(s(s(zero))))))))))(); p = s(p)){Console.WriteLine(f(p)());}}}

http://neocranium.com/jasonw/archive/2004/08/06/165.aspx


;)
Fritz Onion wrote re: The elegance of anonymous delegates
on 06-21-2005 11:17 AM
Ha! Ooo, that's nasty.
kokos wrote re: The elegance of anonymous delegates
on 06-24-2005 12:20 PM
what if you don't use "using" with WindowsImpersonationScope?
Fritz Onion wrote re: The elegance of anonymous delegates
on 06-24-2005 12:42 PM
You must call Dispose() on the WindowsImpersonationScope to undo the impersonation. The using keyword is the best way to do this since it puts it into an implicit try/finally block and calls Dispose() in the finally block.
Saifee wrote re: The elegance of anonymous delegates
on 06-26-2005 8:09 AM
What I don't understand is how are you returning ed of type EmployeeDetails from a delegate whose return-type is void.
Fritz Onion wrote re: The elegance of anonymous delegates
on 06-27-2005 5:58 AM
Good point Saifee - I had oversimplified the example and left it in an uncompilable state. It's now corrected, plus I've added an example of using an IDisposable wrapper, which I think is actually preferable in this case.
Fritz Onion wrote re: The elegance of anonymous delegates
on 06-27-2005 6:09 AM
Brett's (and Peter's last example) both rely on the 'Select' feature being used (at least in my experiments) which I was not using. I'm just using the generic CommandField.

jdevesa - I saw that internal property on the GridViewCommandEventArgs and felt the same frustration. I'm not sure why it's not available publicly as it is in the GridViewRowEventArgs.
Atul wrote re: The elegance of anonymous delegates
on 06-27-2005 2:50 PM
I am really glad to see the impersonation being done inside an using() block -- I had come w/ the same solution a few months ago. However, since it was used in production code, I couldn't blog about it :).

Marc C. Brooks wrote re: The elegance of anonymous delegates
on 07-06-2005 3:40 PM
I'm concerned that this practice will lead to rampant editor inheritance. How many times will this sort of code be copied precisely because it has no method name?
Joshua Flanagan wrote re: The elegance of anonymous delegates
on 10-25-2005 7:54 PM
I was just considering the IDisposable approach myself. Interestingly, I noticed that WindowsImpersonationContext now implements IDisposable in .NET 2.0. It isn't clear whether it calls .Undo() within Dispose().

One problem I have with all of the IDisposable implementations I've seen (including yours), is that they do not handle the case where a user does not call Dispose. Ideally, when you implement IDisposable, you should also implement a Finalizer, so that everything eventually gets cleaned up. Dispose() is just supposed to be a shortcut to allow you to deterministically clean up after yourself. But it shouldn't be depended on and open the door to errors, should it?

In this case, if someone never calls Dispose, the impersonation will never be undone, even when the helper class falls out of scope.
Is this acceptable?
Fritz Onion wrote re: The elegance of anonymous delegates
on 10-27-2005 6:06 AM
Good poing Joshua, it would be wise too implement a finalizer as well to guarantee cleanup if the client fails to call Dispose().
Didier J. Devroye wrote re: The elegance of anonymous delegates
on 06-07-2006 5:24 AM
After reviewing the MSIL of the WindowsImpersonationContext class under .NET 2.0 it is clear that the Undo() method is called during the finalization process. My 2 cent.
oyunlar wrote re: The elegance of anonymous delegates
on 08-09-2008 3:22 AM

am really glad to see the impersonation being done inside an using() block -- I had come w/ the same solution a few months ago. However, since it was used in production code, I couldn't blog about it :). thanks

Add a Comment

(required)  
(optional)
(required)  
Remember Me?