-
Notifications
You must be signed in to change notification settings - Fork 3.4k
Suppress warnings on uninitialized DbSet properties #26795
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| <?xml version="1.0" encoding="utf-8"?> | ||
|
|
||
| <root> | ||
| <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> | ||
| <xsd:element name="root" msdata:IsDataSet="true"> | ||
|
|
||
| </xsd:element> | ||
| </xsd:schema> | ||
| <resheader name="resmimetype"> | ||
| <value>text/microsoft-resx</value> | ||
| </resheader> | ||
| <resheader name="version"> | ||
| <value>1.3</value> | ||
| </resheader> | ||
| <resheader name="reader"> | ||
| <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> | ||
| </resheader> | ||
| <resheader name="writer"> | ||
| <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> | ||
| </resheader> | ||
| <data name="UninitializedDbSetWarningSuppressionJustification" xml:space="preserve"> | ||
| <value>DbSet properties on DbContext subclasses are automatically populated by the DbContext constructor.</value> | ||
| </data> | ||
| <data name="InternalUsageMessageFormat" xml:space="preserve"> | ||
| <value>{0} is an internal API that supports the Entity Framework Core infrastructure and not subject to the same compatibility standards as public APIs. It may be changed or removed without notice in any release.</value> | ||
| </data> | ||
| <data name="InternalUsageTitle" xml:space="preserve"> | ||
| <value>Internal EF Core API usage.</value> | ||
| </data> | ||
| </root> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,84 @@ | ||
| // Licensed to the .NET Foundation under one or more agreements. | ||
| // The .NET Foundation licenses this file to you under the MIT license. | ||
|
|
||
| using System.Collections.Immutable; | ||
| using Microsoft.CodeAnalysis; | ||
| using Microsoft.CodeAnalysis.CSharp.Syntax; | ||
| using Microsoft.CodeAnalysis.Diagnostics; | ||
|
|
||
| namespace Microsoft.EntityFrameworkCore; | ||
|
|
||
| [DiagnosticAnalyzer(LanguageNames.CSharp)] | ||
| public sealed class UninitializedDbSetDiagnosticSuppressor : DiagnosticSuppressor | ||
| { | ||
| private static readonly SuppressionDescriptor _suppressUninitializedDbSetRule = new( | ||
| id: "SPR1001", | ||
| suppressedDiagnosticId: "CS8618", | ||
| justification: AnalyzerStrings.UninitializedDbSetWarningSuppressionJustification); | ||
|
|
||
| /// <inheritdoc /> | ||
| public override ImmutableArray<SuppressionDescriptor> SupportedSuppressions { get; } | ||
| = ImmutableArray.Create(_suppressUninitializedDbSetRule); | ||
|
|
||
| /// <inheritdoc /> | ||
| public override void ReportSuppressions(SuppressionAnalysisContext context) | ||
| { | ||
| INamedTypeSymbol? dbSetTypeSymbol = null; | ||
| INamedTypeSymbol? dbContextTypeSymbol = null; | ||
|
|
||
| foreach (var diagnostic in context.ReportedDiagnostics) | ||
| { | ||
| // We have an warning about an uninitialized non-nullable property. | ||
| // Get the node, and make sure it's a property who's type syntactically contains DbSet (fast check before getting the | ||
| // semantic model, which is heavier). | ||
| if (diagnostic.Location.SourceTree is not { } sourceTree | ||
| || sourceTree.GetRoot().FindNode(diagnostic.Location.SourceSpan) is not PropertyDeclarationSyntax propertyDeclarationSyntax | ||
| || !propertyDeclarationSyntax.Type.ToString().Contains("DbSet")) | ||
|
roji marked this conversation as resolved.
|
||
| { | ||
| continue; | ||
| } | ||
|
|
||
| // Get the semantic symbol and do some basic checks | ||
| if (context.GetSemanticModel(sourceTree).GetDeclaredSymbol(propertyDeclarationSyntax) is not IPropertySymbol propertySymbol | ||
| || propertySymbol.IsStatic | ||
| || propertySymbol.IsReadOnly) | ||
| { | ||
| continue; | ||
| } | ||
|
|
||
| if (dbSetTypeSymbol is null || dbContextTypeSymbol is null) | ||
| { | ||
| dbSetTypeSymbol = context.Compilation.GetTypeByMetadataName("Microsoft.EntityFrameworkCore.DbSet`1"); | ||
| dbContextTypeSymbol = context.Compilation.GetTypeByMetadataName("Microsoft.EntityFrameworkCore.DbContext"); | ||
|
|
||
| if (dbSetTypeSymbol is null || dbContextTypeSymbol is null) | ||
| { | ||
| return; | ||
| } | ||
| } | ||
|
|
||
| // Check that the property is actually a DbSet<T>, and that its containing type inherits from DbContext | ||
| if (propertySymbol.Type.OriginalDefinition.Equals(dbSetTypeSymbol, SymbolEqualityComparer.Default) | ||
| && InheritsFrom(propertySymbol.ContainingType, dbContextTypeSymbol)) | ||
| { | ||
| context.ReportSuppression(Suppression.Create(SupportedSuppressions[0], diagnostic)); | ||
| } | ||
|
|
||
| static bool InheritsFrom(ITypeSymbol typeSymbol, ITypeSymbol baseTypeSymbol) | ||
| { | ||
| var baseType = typeSymbol.BaseType; | ||
| while (baseType is not null) | ||
| { | ||
| if (baseType.Equals(baseTypeSymbol, SymbolEqualityComparer.Default)) | ||
| { | ||
| return true; | ||
| } | ||
|
|
||
| baseType = baseType.BaseType; | ||
| } | ||
|
|
||
| return false; | ||
| } | ||
|
Comment on lines
+67
to
+81
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is it intentional to look at the full hierarchy, not only the parent?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes - the user's DbContext type can be lower down the hierarchy, rather than directly extend DbContext. A good example is ASP.NET Identity, where user context types can extend IdentityDbContext, which itself extends DbContext. The moment DbContext is somewhere in the base type list, we know its constructor will get invoked, and therefore the DbSets will get populated. |
||
| } | ||
| } | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.