DEVKIT1015 - phuocle/Dynamics-Crm-DevKit GitHub Wiki
This analyzer detects usage of blocking async patterns like GetAwaiter().GetResult(), .Result, and .Wait() in plugins. While these patterns are sometimes necessary in plugins (since async Execute is not supported), they can cause deadlocks if not used carefully.
Blocking async operations can cause issues:
- Deadlocks: Can occur if the async operation captures a synchronization context
- Performance: Thread is blocked waiting instead of being available for other work
- Exception Handling: Exceptions may be wrapped differently than expected
| Pattern | Example | Risk |
|---|---|---|
GetAwaiter().GetResult() |
task.GetAwaiter().GetResult() |
Medium |
.Result |
task.Result |
Medium |
.Wait() |
task.Wait() |
Medium |
public class MyPlugin : IPlugin
{
public void Execute(IServiceProvider serviceProvider)
{
// ⚠️ Info: Can cause deadlocks
var result = httpClient.GetAsync(url).GetAwaiter().GetResult();
// ⚠️ Info: Can cause deadlocks
var data = task.Result;
// ⚠️ Info: Can cause deadlocks
task.Wait();
}
}public class BetterPlugin : IPlugin
{
public void Execute(IServiceProvider serviceProvider)
{
// ✅ Use ConfigureAwait(false) to avoid deadlocks
var result = httpClient.GetAsync(url)
.ConfigureAwait(false)
.GetAwaiter()
.GetResult();
// ✅ Or better: use synchronous APIs when available
var request = WebRequest.Create(url);
var response = request.GetResponse();
}
}public class BestPlugin : IPlugin
{
public void Execute(IServiceProvider serviceProvider)
{
// ✅ Use synchronous APIs
using (var client = new WebClient())
{
var data = client.DownloadString(url);
}
// ✅ Or use HttpWebRequest synchronously
var request = (HttpWebRequest)WebRequest.Create(url);
request.Timeout = 15000;
request.KeepAlive = false;
using (var response = request.GetResponse())
using (var reader = new StreamReader(response.GetResponseStream()))
{
var content = reader.ReadToEnd();
}
}
}In plugins, async patterns with ConfigureAwait(false) may be acceptable when:
- No synchronous API available: Some modern APIs only offer async methods
-
Using
ConfigureAwait(false): Prevents deadlocks by not capturing sync context - Timeout is set: Always set a timeout for external calls
If you must use async patterns and understand the risks:
#pragma warning disable DEVKIT1015
var result = task.GetAwaiter().GetResult();
#pragma warning restore DEVKIT1015Or in .editorconfig:
[*.cs]
dotnet_diagnostic.DEVKIT1015.severity = none- DEVKIT1009 - Set KeepAlive to false for external HTTP calls
- DEVKIT1010 - Set Timeout for external HTTP calls
| Property | Value |
|---|---|
| Rule ID | DEVKIT1015 |
| Category | DynamicsCrm.DevKit |
| Severity | Warning |
| Enabled by default | Yes |