Multithreading Resource Modifications (Puts)

Jun 26, 2014 at 2:04 PM
I am attempting to modify Group membership to several Groups. Processing RmResourceChanges can be very time consuming if there are many custom AuthZ and AuthN policies defined for the Group resources. Since this is being done in ASP.Net, page timeouts can easily become a factor. Each of the RmResourceChanges objects process on a single group. The RmResourceChanges are contained in a custom object List<T>. Since each call to DefaultClient.Put(RmResourceChanges) can block up to 12 seconds, I need to execute these in parallel.

I am using Rob Volk's Parallel ForEach LINQ extension for .Net 3.5.
http://robvolk.com/parallel-foreach-loop-in-c-3-5/

Once I build up the collection of RmResourceChanges transactions in the List<T>, I am attempting to submit them in parallel to the web service...
List<T>.EachParallel(t => t.SubmitTransaction(defaultClient)));
The SubmitTransaction method is a public method on the list item that performs the Put.
Think:
try
{
    defaultClient.Put(this.Transaction);
    this.Status = "Submitted";
}
catch ....

Result for each Put : "The endpoint could not dispatch the request."

I then assumed the Resource Management Client was not thread safe, and as a test, modified my call to instantiate a new client per transaction:
List<T>.EachParallel(t => t.SubmitTransaction(new DefaultClient(CredentialCache.DefaultNetworkCredentials)));
Unfortunately, I am still receiving the faulted response : "The endpoint could not dispatch the request."

I believe this should eliminate any potential race conditions within the client. Anyone else experience this issue?
Jun 26, 2014 at 5:21 PM
I figured out the problem and it has nothing to do with the Client or the FIM WCF. The issue is the user context Threads are created when using the ThreadPool.QueueUserWorkItem when your main thread is Impersonating. You must pass the identity into the method executed in the new thread, and re-impersonate prior to performing the Put.

Each Action is now passed like this:
System.Security.Principal.WindowsIdentity identity = System.Security.Principal.WindowsIdentity.GetCurrent();
List<T>.EachParallel(t => t.SubmitTransaction(client, identity)); 
My SubmitTransaction method performs the impersonation like so:
System.Security.Principal.WindowsImpersonationContext wi = null;
try
{
    wi = identity.Impersonate();
    defaultClient.Put(this.Transaction);
    this.Status = "Submitted";
}
catch {...}
finally
{
    if (wi != null)
    {
        wi.Undo();
    }
}   
The key to finding this was to enable Tracing on the FIM Service. WCF could not lookup the Network Service Account:
GetCurrentUserFromSecurityIdentifier: No such user NT AUTHORITY\NETWORK SERVICE, S-1-5-20

I hope this helps someone!