DapperLib / DapperAOT

Build time tools in the flavor of Dapper

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Build error "The type 'InterceptsLocationAttribute' exists in both ...."

bakedpatato opened this issue · comments

Describe the bug

Build error :
"The type 'InterceptsLocationAttribute' exists in both 'test, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' and 'test, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" is thrown for simple dotnet8 console project.

Where are you seeing this?

  • what Dapper/Dapper.StrongName version? 2.1.24
  • what Dapper.AOT/Dapper.Advisor version? 1.0.31
  • if relevant: what database backend? SQL Server, Microsoft.Data.SqlClient 5.1.2

To Reproduce

  • Create a new dotnet 8 console project
  • Put "[module: DapperAot]" below the usings
  • Create a string for the SQL query
  • Create a class for dapper to map the query to
  • Put a query method in the class querying the database as such:
 using (var connection = new SqlConnection(connectionString))
{
    await connection.OpenAsync();
    using (var tran = await connection.BeginTransactionAsync(IsolationLevel.Snapshot))
    {
        var returned= await connection.QueryAsync<Class>(Query, transaction: tran);
    }
}

Expected behavior
Soultion builds with no error.
Screenshots

Additional context
"InterceptorsPreviewNamespaces" csproj element exists in PropertyGroup
Dotnet version:

dotnet --info
.NET SDK:
 Version:           8.0.100
 Commit:            57efcf1350
 Workload version:  8.0.100-manifests.71b9f198 

No repro locally, using:

<Project Sdk="Microsoft.NET.Sdk">

    <PropertyGroup>
        <OutputType>Exe</OutputType>
        <TargetFramework>net8.0</TargetFramework>
        <ImplicitUsings>enable</ImplicitUsings>
        <Nullable>enable</Nullable>
        <InterceptorsPreviewNamespaces>$(InterceptorsPreviewNamespaces);Dapper.AOT</InterceptorsPreviewNamespaces>
    </PropertyGroup>

    <ItemGroup>
        <PackageReference Include="Dapper" Version="2.1.24" />
        <PackageReference Include="Dapper.AOT" Version="1.0.31" />
        <PackageReference Include="Microsoft.Data.SqlClient" Version="5.1.2" />
    </ItemGroup>
</Project>

and

using Dapper;
using Microsoft.Data.SqlClient;
using System.Data;

[module: DapperAot]

const string connectionString = "whatever", Query = "select * from SomeTable";
using var connection = new SqlConnection(connectionString);
await connection.OpenAsync();
using var tran = await connection.BeginTransactionAsync(IsolationLevel.Snapshot);
var returned = await connection.QueryAsync<Class>(Query, transaction: tran);

class Class {
    public int Id { get; set; }
}

Are you sure there isn't some other library in play that might be contributing generator code? If you're using devenv, you can expand:

  • Solution
    • (your project)
      • Analyzers
        • (look at each analyzer in turn)
          • (look here for an expandable item)

At the last bullet - ignore the things that look like analyzer warnings/errors/etc - CA2260 etc; what you're after is an expandable pair of cogs somewhere in the list (probably at the end, after the analyzer rules), for example Dapper.AOT has 2 generators - so under Dapper.AOT we have a bunch of analyzers, then finally:

image

Is there some other package (other than Dappper.AOT) which is contributing generated code? There will probably be a bunch of system ones that aren't generating anything - these don't matter (see below) - I'm looking for something that is generating a .cs file

These are not generating:

image

To clarify: we do attempt to see whether InterceptsLocationAttribute exists in the incoming build, and if it does: we don't emit it - so we only emit the attribute if we think it is necessary; however, we can't see generated code (by design), so if another generator emits the attribute: we could hit a conflict.

To minimize this, Dapper uses a "file scoped class", i.e.

namespace System.Runtime.CompilerServices
{
    // this type is needed by the compiler to implement interceptors - it doesn't need to
    // come from the runtime itself, though

    [global::System.Diagnostics.Conditional("DEBUG")] // not needed post-build, so: evaporate
    [global::System.AttributeUsage(global::System.AttributeTargets.Method, AllowMultiple = true)]
    sealed file class InterceptsLocationAttribute : global::System.Attribute
    {
        public InterceptsLocationAttribute(string path, int lineNumber, int columnNumber)
        {
            _ = path;
            _ = lineNumber;
            _ = columnNumber;
        }
    }
}

which means that we're playing nicely and shouldn't impact other people; but if someone else is emitting the attribute and is not playing nicely, they'll break us, but the error: is theirs. Hence my idea to see if we can find where it is coming from.

I've added some tweaks, but I don't expect them to help unless the conflicting attribute is coming from your code; we ideally need to find where the conflicting attribute is, and steer that generator to play nicely

Testing further, it looks to me like even if there's a conflict: the file-local one is selected without causing an error. No matter what I do, I can't seem to reproduce an issue here.

Is there anything unusual about your setup that I might need to know about? Any unusual build tools / config? operating system?

Marc,

Appreciate the effort!

What you have is pretty much what I have locally. I was originally thinking it was s.t.j because I was getting this for a project that was using it but I get it for even the simplest use of Dapper.Aot.

I'm using VS 2023 17.9 Preview 2 on Windows 11 Enterprise, and while there's a bunch of the usual corporate security junk on the machine it's otherwise a pretty clean dotnet setup.

I'll continue to poke around on my side, if I can provide any other information please let me know!

well, one thing you could try: comment out the dapper bits for now (or just use [DapperAot(false)]- it should compile again; add:

Console.WriteLine(nameof(System.Runtime.CompilerServices.InterceptsLocationAttribute));

Now right-click on InterceptsLocationAttribute, "go to definition" (or press F12) - and it should show us where a competing definition is coming from. I'm hoping it doesn't say:

CS0234 The type or namespace name 'InterceptsLocationAttribute' does not exist in the namespace 'System.Runtime.CompilerServices' (are you missing an assembly reference?)

also worth checking: do you have any weird things in a Directory.Build.props file (in any ancestor/parent folder), perhaps bringing in a random file/reference that you aren't accounting for?

Now right-click on InterceptsLocationAttribute, "go to definition" (or press F12) - and it should show us where a competing definition is coming from. I'm hoping it doesn't say:

CS0234 The type or namespace name 'InterceptsLocationAttribute' does not exist in the namespace 'System.Runtime.CompilerServices' (are you missing an assembly reference?)

Sadly, that's exactly what it says 🙁

Ok; well, I'm intrigued but almost out of options here. I can't repro, and I can't Intuit where it is coming from. Is it possible to post your actual csproj/cs, like I have above?

Also: does it build from the command-line?

Same error when running dotnet build
and sure:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net8.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <InterceptorsPreviewNamespaces>$(InterceptorsPreviewNamespaces);Dapper.AOT</InterceptorsPreviewNamespaces>
  </PropertyGroup>
	<ItemGroup>
		<PackageReference Include="Dapper" Version="2.1.24" />		
		<PackageReference Include="Dapper.AOT" Version="1.0.31" />
		<PackageReference Include="Microsoft.Data.SqlClient" Version="5.1.2" />

	</ItemGroup>
</Project>
using Dapper;
using Microsoft.Data.SqlClient;
using System.Runtime.CompilerServices;

string connectionString =
    "blah";
string reportQuery =
    "select blah from dbo.blah";

Console.WriteLine(nameof(System.Runtime.CompilerServices.InterceptsLocationAttribute));


await A.Query(connectionString,reportQuery);


public class A
{
    public async static Task<IEnumerable<A>> Query(string connectionString,string reportQuery) {
        using (var connection = new SqlConnection(connectionString))
        {
            await connection.OpenAsync();

            using (var tran = await connection.BeginTransactionAsync())
            {
                return  await connection.QueryAsync<A>(reportQuery, transaction: tran);
            }
        }

    }
    public string blahbla { get; set; }
}

This is very intriguing, but the short version is: "I have no clue what is happening here". The exact same code (your version) works absolutely fine locally - and it turns out that even if there was a conflict: the file-local type seems to be reliably selected, i.e. no error. I'm still on 17.9 preview 1, so I'll try updating that and see if that breaks things, but... weird!

The only other thing I can think that might influence this is any .props files in ancestor folders - i.e. if your project is c:\Code\Whatever\Console42\Console42.csproj, then a .props file (especially Directory.Build.props) in c:\Code\Whatever or c:\Code or c:\ could still be contributing changes the the build. It is definitely worth taking a moment to rule that out, but: I don't expect you'll find anything

No change in 17.9 preview 2, still works fine; Windows 11 Pro (Insider Preview) 10.0.23601, .NET SDK 8.0.100

Very weird. I can try to poke @RikkiGibson to see if they can think of anything that might cause this. I'm entirely out of ideas.

If you could share a binlog for the failing build I might be able to get to the bottom of it. May not have time to take a look till after holidays though.

The only other thing I can think that might influence this is any .props files in ancestor folders - i.e. if your project is c:\Code\Whatever\Console42\Console42.csproj, then a .props file (especially Directory.Build.props) in c:\Code\Whatever or c:\Code or c:\ could still be contributing changes the the build. It is definitely worth taking a moment to rule that out, but: I don't expect you'll find anything

Tried moving the project to the root of my drive, still the same thing ☹️

If you could share a binlog for the failing build I might be able to get to the bottom of it. May not have time to take a look till after holidays though.

Binlog attached: msbuild.zip

Thanks yall for all your help!

Ya'll are going to love this, I am now unable to reproduce that error for that simple project 😂

But I still have one that's erroring out with the DapperAot annotation which I have attached here: msbuild.zip

I'm wondering if this is some weirdness with the security software on this machine, this machine has SentinelOne on it and I've run into issues with it messing with my development workflow.

I have a working theory; setting up repro; if correct, the fix is simple...

OK, theory confirmed. Stand down @RikkiGibson - this doesn't need your level of expertise!

The problem is here:

<PackageReference Include="Dapper.Advisor" Version="1.0.31">
  <PrivateAssets>all</PrivateAssets>
  <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Dapper.AOT" Version="1.0.31" />		

You see, this ends up with two parallel copies of the analyzer tools, which run independently. This is a little surprising - I would have expected the build outputs to be normalized (flattened), but: I guess this is necessary in case different analyzers have different and mutually incompatible versions of some files.

The solution: don't do that:

<PackageReference Include="Dapper.AOT" Version="1.0.31" />

is sufficient, and is the more complete bundle:

  • Dapper.Advisor is a dummy dev dependency that simply bundles Dapper.AOT.Analyzers
  • Dapper.AOT is a proper library (containing the runtime part of Dapper.AOT - base types, etc) that also bundles Dapper.AOT.Analyzers

I'll think whether there's a better way we can package these two things.