We have recently had a few occasions where the question came up whether in Dynamics CRM 2011, one plugin execution (i.e. a pass of the Execute()
method) is guaranteed to stay on the same thread.
I'd like to implement tracing using the Ambient Context pattern to avoid passing the tracing service to any class that might want to trace. The problem is that as we know the plugin is only instantiated once per registered step and then serves all subsequent operations from the same instance; that means I can't just have some static property like Tracing.Current
to which I assign the current ITracingService
instance and I'm good to go. If I did that, the operation started last would overwrite the instance for all other operations that might still be running (and this sort of concurrency is not uncommon).
Now if I could be sure everything under the Execute()
method remains in the same thread, I could still use an Ambient Context utilizing the [ThreadStatic]
attribute for static fields:
public static class Tracing
{
[ThreadStatic]
private static ITracingService _current;
public static ITracingService Current
{
get
{
if (null == _current)
{
_current = new NullTracingService();
}
return _current;
}
set { _current = value; }
}
}
I would set this upon entering the Execute()
method and clear it at the end so the reference to the tracing service instance will be removed.
The only thing I could kind of find out about threading in the context of MSCRM plugins is that apparently the individual threads come from the ThreadPool - whatever consequences that might have with regards to my issue.
Does anyone have a deeper insight into how threading is handled with MSCRM plugins - or any other ideas on how the cross-cutting concern of tracing could be handled elegantly with SOLID code in this special case (AOP/dynamic interception are no options here)?
Thanks for any help and pointers.
The simple and smart-ass answer: if it hurts when you do that, then don't do that. :)
Your self-imposed requirement of using the Ambient Context pattern is conflicting with CRM's design pattern. Think of how CRM works - it passes you a IServiceProvider
, with everything you need including the Tracing Service. It handles all the complicated multithreading and optimizations for you, and only asks that you don't try to outsmart it with fancy patterns or static variables or threading tricks.
My recommendation is to use the same pattern - pass the IServiceProvider
to any classes or methods that need it. Much simpler - plus later when you have a weird bug, you'll not question whether you successfully outsmarted Microsoft's engineers or not. :)
CRM creates a single plugin object, and then uses threads as needed to process the requests. So the only thing you can be sure of is that you will have multiple threads running at a single time for a single plugin object.
The threads are managed through IIS, and will get reused if possible. So if you want to ensure that each time Execute
is called, it has a new ITracingService
you'll have to set it. If you just want to ensure that each time Execute
is called, it has one, you'll just need to do an if
statement to check for it.
Since your backing variable is ThreadStatic
, you won't need to worry about threading issues, but since IIS tries to reuse threads, it will not be empty each time Execute
is called.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With