diff --git a/docs/Rules/MA0042.md b/docs/Rules/MA0042.md index 6f8b69b77..6147b9b15 100644 --- a/docs/Rules/MA0042.md +++ b/docs/Rules/MA0042.md @@ -53,6 +53,8 @@ public sealed class Sample } ```` +The rule does not report a diagnostic for `IDbContextFactory.CreateDbContext()`. The `CreateDbContextAsync()` overload was introduced only for specific edge-case scenarios where the factory itself must perform asynchronous initialization, and is not intended as a general-purpose replacement. See [dotnet/efcore#26630](https://github.com/dotnet/efcore/issues/26630) for more details. + ## Additional resources - [Enforcing asynchronous code good practices using a Roslyn analyzer](https://www.meziantou.net/enforcing-asynchronous-code-good-practices-using-a-roslyn-analyzer.htm) diff --git a/src/Meziantou.Analyzer/Rules/DoNotUseBlockingCallInAsyncContextAnalyzer.cs b/src/Meziantou.Analyzer/Rules/DoNotUseBlockingCallInAsyncContextAnalyzer.cs index 56a8f6812..9204dfd33 100755 --- a/src/Meziantou.Analyzer/Rules/DoNotUseBlockingCallInAsyncContextAnalyzer.cs +++ b/src/Meziantou.Analyzer/Rules/DoNotUseBlockingCallInAsyncContextAnalyzer.cs @@ -95,6 +95,7 @@ public Context(Compilation compilation) DbContextSymbol = compilation.GetBestTypeByMetadataName("Microsoft.EntityFrameworkCore.DbContext"); DbSetSymbol = compilation.GetBestTypeByMetadataName("Microsoft.EntityFrameworkCore.DbSet`1"); + DbContextFactorySymbol = compilation.GetBestTypeByMetadataName("Microsoft.EntityFrameworkCore.IDbContextFactory`1"); ServiceProviderServiceExtensionsSymbol = compilation.GetBestTypeByMetadataName("Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions"); if (ServiceProviderServiceExtensionsSymbol is not null) @@ -146,6 +147,7 @@ public Context(Compilation compilation) private INamedTypeSymbol? DbContextSymbol { get; } private INamedTypeSymbol? DbSetSymbol { get; } + private INamedTypeSymbol? DbContextFactorySymbol { get; } public INamedTypeSymbol? Moq_MockSymbol { get; } @@ -248,6 +250,15 @@ private bool HasAsyncEquivalent(IInvocationOperation operation, [NotNullWhen(tru return false; } + // IDbContextFactory.CreateDbContext() - CreateDbContextAsync() is only for specific edge-cases + // https://github.com/dotnet/efcore/issues/26630 + else if (DbContextFactorySymbol is not null && targetMethod.Name is "CreateDbContext" && + (targetMethod.ContainingType.OriginalDefinition.IsEqualTo(DbContextFactorySymbol) || + targetMethod.ContainingType.ImplementsGenericInterface(DbContextFactorySymbol))) + { + return false; + } + else if (Moq_MockSymbol is not null && targetMethod.Name is "Raise" && targetMethod.ContainingType.OriginalDefinition.IsEqualTo(Moq_MockSymbol)) { return false; diff --git a/tests/Meziantou.Analyzer.Test/Rules/DoNotUseBlockingCallInAsyncContextAnalyzer_AsyncContextTests.cs b/tests/Meziantou.Analyzer.Test/Rules/DoNotUseBlockingCallInAsyncContextAnalyzer_AsyncContextTests.cs index 3f8226688..815366b91 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/DoNotUseBlockingCallInAsyncContextAnalyzer_AsyncContextTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/DoNotUseBlockingCallInAsyncContextAnalyzer_AsyncContextTests.cs @@ -1926,6 +1926,33 @@ async Task A() .ValidateAsync(); } + [Fact] + [Trait("Issue", "https://github.com/meziantou/Meziantou.Analyzer/issues/891")] + public async Task IDbContextFactory_CreateDbContext_NoReport() + { + await CreateProjectBuilder() + .WithTargetFramework(TargetFramework.Net6_0) + .AddNuGetReference("Microsoft.EntityFrameworkCore", "6.0.8", "lib/net6.0/") + .AddNuGetReference("Microsoft.EntityFrameworkCore.Abstractions", "6.0.8", "lib/net6.0/") + .WithSourceCode(""" + using System.Threading.Tasks; + using Microsoft.EntityFrameworkCore; + + class BloggingContext : DbContext { } + + class Sample + { + private IDbContextFactory _factory; + + async Task A() + { + await using var context = _factory.CreateDbContext(); + } + } + """) + .ValidateAsync(); + } + [Fact] public async Task IAsyncEnumerable() {