Skip to content
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

feat: support [Column] #85

Merged
Merged
75 changes: 38 additions & 37 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
@@ -1,39 +1,40 @@
<Project>
<!--<Import Project="$([MSBuild]::GetPathOfFileAbove(Directory.Packages.props, $(MSBuildThisFileDirectory)..))" />..-->
<ItemGroup>
<PackageVersion Include="BenchmarkDotNet" Version="0.13.10" />
<PackageVersion Include="FastMember" Version="1.5.0" />
<PackageVersion Include="Dapper" Version="2.1.21" />
<PackageVersion Include="Dapper.AOT" Version="1.0.14" />
<PackageVersion Include="Dapper.StrongName" Version="2.1.21" />
<PackageVersion Include="Nerdbank.GitVersioning" Version="3.6.133" />
<PackageVersion Include="Npgsql" Version="7.0.6" />
<PackageVersion Include="Microsoft.Bcl.AsyncInterfaces" Version="8.0.0" />
<PackageVersion Include="Microsoft.Build" Version="17.7.2" />
<PackageVersion Include="Microsoft.Build.Utilities.Core" Version="17.7.2" />
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp.Analyzer.Testing.XUnit" Version="1.1.1" />
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp.CodeFix.Testing.XUnit" Version="1.1.1" />
<!-- 4.8 would be nice, but: let's try to offer down-level compiler support -->
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="[4.4.0]" />
<PackageVersion Include="Microsoft.CodeAnalysis.VisualBasic.Analyzer.Testing.XUnit" Version="1.1.1" />
<PackageVersion Include="Microsoft.CodeAnalysis.VisualBasic.CodeFix.Testing.XUnit" Version="1.1.1" />
<!-- 4.8 would be nice, but: let's try to offer down-level compiler support -->
<PackageVersion Include="Microsoft.CodeAnalysis.VisualBasic.Workspaces" Version="[4.4.0]" />
<PackageVersion Include="Microsoft.Data.SqlClient" Version="5.1.2" />
<PackageVersion Include="Microsoft.EntityFrameworkCore" Version="8.0.0" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.0" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
<PackageVersion Include="Microsoft.SourceLink.GitHub" Version="1.1.1" />
<PackageVersion Include="Microsoft.SqlServer.TransactSql.ScriptDom" Version="161.8910.0" />
<PackageVersion Include="Oracle.ManagedDataAccess" Version="21.12.0" />
<PackageVersion Include="Oracle.ManagedDataAccess.Core" Version="3.21.120" />
<PackageVersion Include="System.Collections.Immutable" Version="8.0.0" />
<PackageVersion Include="System.Data.Common" Version="4.3.0" />
<PackageVersion Include="System.Data.SqlClient" Version="4.8.5" />
<PackageVersion Include="System.Memory" Version="4.5.5" />
<PackageVersion Include="Testcontainers.PostgreSql" Version="3.6.0" />
<!-- these are bound by Microsoft.CodeAnalysis.CSharp.*.Testing.XUnit -->
<PackageVersion Include="xunit" Version="[2.3.0]" />
<PackageVersion Include="xunit.runner.visualstudio" Version="[2.3.0]" />
</ItemGroup>
<!--<Import Project="$([MSBuild]::GetPathOfFileAbove(Directory.Packages.props, $(MSBuildThisFileDirectory)..))" />..-->
<ItemGroup>
<PackageVersion Include="BenchmarkDotNet" Version="0.13.10" />
<PackageVersion Include="FastMember" Version="1.5.0" />
<PackageVersion Include="Dapper" Version="2.1.21" />
<PackageVersion Include="Dapper.AOT" Version="1.0.14" />
<PackageVersion Include="Dapper.StrongName" Version="2.1.21" />
<PackageVersion Include="Nerdbank.GitVersioning" Version="3.6.133" />
<PackageVersion Include="Npgsql" Version="7.0.6" />
<PackageVersion Include="Microsoft.Bcl.AsyncInterfaces" Version="8.0.0" />
<PackageVersion Include="Microsoft.Build" Version="17.7.2" />
<PackageVersion Include="Microsoft.Build.Utilities.Core" Version="17.7.2" />
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp.Analyzer.Testing.XUnit" Version="1.1.1" />
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp.CodeFix.Testing.XUnit" Version="1.1.1" />
<!-- 4.8 would be nice, but: let's try to offer down-level compiler support -->
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="[4.4.0]" />
<PackageVersion Include="Microsoft.CodeAnalysis.VisualBasic.Analyzer.Testing.XUnit" Version="1.1.1" />
<PackageVersion Include="Microsoft.CodeAnalysis.VisualBasic.CodeFix.Testing.XUnit" Version="1.1.1" />
<!-- 4.8 would be nice, but: let's try to offer down-level compiler support -->
<PackageVersion Include="Microsoft.CodeAnalysis.VisualBasic.Workspaces" Version="[4.4.0]" />
<PackageVersion Include="Microsoft.Data.SqlClient" Version="5.1.2" />
<PackageVersion Include="Microsoft.EntityFrameworkCore" Version="8.0.0" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.0" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
<PackageVersion Include="Microsoft.SourceLink.GitHub" Version="1.1.1" />
<PackageVersion Include="Microsoft.SqlServer.TransactSql.ScriptDom" Version="161.8910.0" />
<PackageVersion Include="Oracle.ManagedDataAccess" Version="21.12.0" />
<PackageVersion Include="Oracle.ManagedDataAccess.Core" Version="3.21.120" />
<PackageVersion Include="System.Collections.Immutable" Version="8.0.0" />
<PackageVersion Include="System.ComponentModel.Annotations" Version="5.0.0" />
<PackageVersion Include="System.Data.Common" Version="4.3.0" />
<PackageVersion Include="System.Data.SqlClient" Version="4.8.5" />
<PackageVersion Include="System.Memory" Version="4.5.5" />
<PackageVersion Include="Testcontainers.PostgreSql" Version="3.6.0" />
<!-- these are bound by Microsoft.CodeAnalysis.CSharp.*.Testing.XUnit -->
<PackageVersion Include="xunit" Version="[2.3.0]" />
<PackageVersion Include="xunit.runner.visualstudio" Version="[2.3.0]" />
</ItemGroup>
</Project>
41 changes: 41 additions & 0 deletions docs/rules/DAP042.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# DAP042

Usage of [`[Column]`](https://learn.microsoft.com/dotnet/api/system.componentmodel.dataannotations.schema.columnattribute)
attribute and `[DbValue]` attributes are conflicting - Dapper will choose either one or another's name override.

This conflict can lead to unexpected behavior, so we recommend to use only one of them.

Bad:

``` csharp
class MyType
{
[DbValue(Name = "MyOtherOtherName")]
[UseColumnAttribute]
[Column("MyOtherName")]
public int MyThisName { get; set; }
}
```

Good:

``` csharp
class MyType
{
[DbValue(Name = "MyOtherOtherName")]
// without [Column] and [UseColumnAttribute]
public int MyThisName { get; set; }
}
```

Another good:

``` csharp
class MyType
{
// without [DbValue]
[UseColumnAttribute]
[Column("MyOtherName")]
public int MyThisName { get; set; }
}
```
28 changes: 28 additions & 0 deletions docs/rules/DAP043.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# DAP043

If you are looking at `DAP043`, then most probably you wanted to use [`[Column]`](https://learn.microsoft.com/dotnet/api/system.componentmodel.dataannotations.schema.columnattribute)
attribute to override the name of the type member.

However, due to [historical reasons](https://stackoverflow.com/a/77073456) you need to add a "marker attribute" `[UseColumnAttribute]`
to explicitly let Dapper know you want to override the type member's name.

Bad:

``` csharp
class MyType
{
[Column("MyOtherName")]
public int MyThisName { get; set; }
}
```

Good:

``` csharp
class MyType
{
[UseColumnAttribute]
[Column("MyOtherName")]
public int MyThisName { get; set; }
}
```
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ public static readonly DiagnosticDescriptor
ValueTypeSingleFirstOrDefaultUsage = LibraryWarning("DAP038", "Value-type single row 'OrDefault' usage", "Type '{0}' is a value-type; it will not be trivial to identify missing rows from {1}"),
FactoryMethodMultipleExplicit = LibraryError("DAP039", "Multiple explicit factory methods", "Only one factory method should be marked [ExplicitConstructor] for type '{0}'"),
ConstructorOverridesFactoryMethod = LibraryWarning("DAP041", "Constructor overrides factory method", "Type '{0}' has both constructor and factory method; Constructor will be used instead of a factory method"),
ParameterNameOverrideConflict = LibraryWarning("DAP042", "Parameter name override conflict", "A column name is specified via both [DbValue] and [Column]; '{0}' will be used"),
UseColumnAttributeNotSpecified = LibraryWarning("DAP043", "[Column] has no effect", "Attach the [UseColumnAttribute] attribute to make Dapper consider [Column]"),

// SQL parse specific
GeneralSqlError = SqlWarning("DAP200", "SQL error", "SQL error: {0}"),
Expand Down
30 changes: 30 additions & 0 deletions src/Dapper.AOT.Analyzers/CodeAnalysis/DapperAnalyzer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,14 @@ private void ValidateDapperMethod(in OperationAnalysisContext ctx, IOperation sq
var resultMap = MemberMap.CreateForResults(resultType, location);
if (resultMap is not null)
{
if (resultMap.Members.Length != 0)
{
foreach (var member in resultMap.Members)
{
ValidateMember(member, onDiagnostic);
}
}

// check for single-row value-type usage
if (flags.HasAny(OperationFlags.SingleRow) && !flags.HasAny(OperationFlags.AtLeastOne)
&& resultMap.ElementType.IsValueType && !CouldBeNullable(resultMap.ElementType))
Expand Down Expand Up @@ -817,6 +825,28 @@ enum ParameterMode
? null : new(rowCountHint, rowCountHintMember?.Member.Name, batchSize, cmdProps);
}

static void ValidateMember(ElementMember member, Action<Diagnostic>? reportDiagnostic)
{
ValidateColumnAttribute();

void ValidateColumnAttribute()
{
if (member.HasDbValueAttribute && member.ColumnAttributeData.IsCorrectUsage)
{
reportDiagnostic?.Invoke(Diagnostic.Create(Diagnostics.ParameterNameOverrideConflict,
location: member.GetLocation(Types.DbValueAttribute),
additionalLocations: member.AsAdditionalLocations(Types.ColumnAttribute, allowNonDapperLocations: true),
messageArgs: member.DbName));
}
if (member.ColumnAttributeData.ColumnAttribute == ColumnAttributeData.ColumnAttributeState.Specified
&& member.ColumnAttributeData.UseColumnAttribute == ColumnAttributeData.UseColumnAttributeState.NotSpecified)
{
reportDiagnostic?.Invoke(Diagnostic.Create(Diagnostics.UseColumnAttributeNotSpecified,
location: member.GetLocation(Types.ColumnAttribute, allowNonDapperLocations: true)));
}
}
}

internal static ImmutableArray<ElementMember>? SharedGetParametersToInclude(MemberMap? map, ref OperationFlags flags, string? sql, Action<Diagnostic>? reportDiagnostic, out SqlParseOutputFlags parseFlags)
{
SortedDictionary<string, ElementMember>? byDbName = null;
Expand Down
Loading
Loading