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