EFCore.BulkExtensions
EFCore.BulkExtensions copied to clipboard
SQL Server/PostgreSQL conflict: ambiguous reference for IndexBuilder
Starting from version 6.0.4 there is an issue with IncludeProperties method in IndexBuilder.
Ambiguous invocation: Microsoft.EntityFrameworkCore.Metadata.Builders.IndexBuilder<TEntity> IncludeProperties<TEntity>(this Microsoft.EntityFrameworkCore.Metadata.Builders.IndexBuilder<TEntity>, System.Linq.Expressions.Expression<System.Func<TEntity,object?>>) (in class SqlServerIndexBuilderExtensions) Microsoft.EntityFrameworkCore.Metadata.Builders.IndexBuilder<TEntity> IncludeProperties<TEntity>(this Microsoft.EntityFrameworkCore.Metadata.Builders.IndexBuilder<TEntity>, System.Linq.Expressions.Expression<System.Func<TEntity,object>>) (in class NpgsqlIndexBuilderExtensions) match
The problem is that the EFCore.BulkExtensions now refers Microsoft.EntityFrameworkCore.SqlServer and Microsoft.EntityFrameworkCore.PostgreSQL which has completely the same set of extensions for EF builders.
Looks like it will be better to create separate packages for Postgres and SQL Server implementations.
How do you reproduce this problem?
In RunUpdate test it works without issue:
PropertiesToInclude = new List<string> { nameof(Item.Description) },
public class MyEntityConfig : IEntityTypeConfiguration<MyEntity>
{
protected override void ConfigureEntity(EntityTypeBuilder<MyEntity> builder)
{
builder.HasIndex(it => it.UserId)
.IncludeProperties(it => new { it.DeletedDate });
}
}
The problem is that even if I specify the extensions class implicitly there is an autogenerated snapshot that have the same issue.
Try this fix at the moment: https://github.com/dotnet/efcore/issues/20780
Use explicit call like this:
protected override void ConfigureEntity(EntityTypeBuilder<MyEntity> builder)
{
builder.HasIndex(it => it.UserId);
Expression<Func<MyEntity, object?>> expr = it => new { it.DeletedDate };
SqlServerIndexBuilderExtensions.IncludeProperties(builder, expr);
}
Thank you for the references. These solutions are ok as work-arounds, but is there any plans to fix this issues?
Will consider all options.
I am also running into this issue lately, after upgrading to .NET 6.
Will consider all options.
As @echernyavsky already opted, would it be an option to expose the Sql and Postgress depencies in an explicit package (e.g. like EF does it as well). So we get a BulkExtensions.Sql and BulkExtensions.Postgress package, which determines the provider used.
As a workaround you can install Npgsql.EntityFrameworkCore.PostgreSQL
explicitly and give it some kind of alias. Npgsql will be virtually disabled by doing that.
For more info see: extern-alias
<Project Sdk="Microsoft.NET.Sdk">
<ItemGroup>
<PackageReference Include="EFCore.BulkExtensions" Version="6.2.9" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="6.0.2">
<Aliases>EfCoreNpgsql</Aliases>
</PackageReference>
...
As a workaround you can install
Npgsql.EntityFrameworkCore.PostgreSQL
explicitly and give it some kind of alias. Npgsql will be virtually disabled by doing that.For more info see: extern-alias
<Project Sdk="Microsoft.NET.Sdk"> <ItemGroup> <PackageReference Include="EFCore.BulkExtensions" Version="6.2.9" /> <PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="6.0.2"> <Aliases>EfCoreNpgsql</Aliases> </PackageReference> ...
The workaround works, although I'd still prefer for this package not to force me to load a specific provider I am not using.
We also just ran into this issue. I guess the common approach is to have a core package like EFCore.BulkExtensions
which is provider independent, and then you have additional provider specific packages like EFCore.BulkExtensions.SqlServer
and EFCore.BulkExtensions.PostgreSQL
.
I'm fully on @FrankHoogmans side: I would like EFCore.BulkExtensions not to pull providers. According to this comment it is considered unusual to pull multiple providers.
I have created a proof of concept for splitting the library into multiple ones: https://github.com/FrankHoogmans/EFCore.BulkExtensions/tree/proof-of-concept/split-into-provider-packages
This would seperate the explicit dependencies and their respective code into seperate libraries. The only breaking change is that is is needed to call provider specific registration code on startup of your app (once), to ensure everything is setup correctly:
SQLServer.BulkExtensions.EnableSQLServer();
SQLite.BulkExtensions.EnableSQLite();
PostgreSql.BulkExtensions.EnablePostgreSql();
There are some things I am not completely satisfied with, but it's a proof of concept that I think works and showcases the possibilities. Refactoring would always be an option. All units tests (except for integration tests, did not have all providers available locally) are working.
@borisdj Would any future development in this direction (multiple packages) be considered as an option?
I've been considering that option. Second approach that came to mind is to keep main NuGet with all providers but to have separate packages that would just exclude other providers and its folders. This would be done with some automated procedure before doing publish. Reason for this is to keep maintaining easier. Lately don't have much spare time.
Another thing is, I have planned to do some larger refactoring first (no timeframe yet), which would include more code separation that would then enable easier implementation of either of these 'splitting' methods. Note: https://devblogs.microsoft.com/dotnet/app-trimming-in-net-5/
@FrankHoogmans will analyze your repo more when time comes for splits. Thx for the effort.
@borisdj You could potentially set PrivateAssets="all"
for the package references related to providers. This should then exclude the reference when packing and publishing. Found this blog post illustrating it nicely: https://www.jocheojeda.com/2019/07/22/how-to-exclude-package-dependencies-in-a-nuget-package/
But this would require that the code itself in the library is separated enough that no access to the provider is attempted as it would lead to TypeLoadException
s unless you actually install the other provider packages as integrator.
I've encountered a similar problem after upgrading my application that uses DB Functions (DateDiff, etc.).
I'm getting the error about EF.Functions.DateDiffDay
being ambiguous between Microsoft.EntityFrameworkCore.SqlServerDbFunctionsExtensions.DateDiffDay
and Microsoft.EntityFrameworkCore.MySqlDbFunctionsExtensions.DateDiffDay
.
I've tried the workaround suggested above, by directly referencing the Pomelo MySql library that is used by this BulkExtensions project, and aliasing it, so my project file now looks like so:
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="6.0.2">
<Aliases>PomeloMySql</Aliases>
</PackageReference>
But this has not resolved my problem. I still get the same build error.
Please advise what I may be missing? (For clarity, my application only uses SqlServer, and NOT MySql.)
Next Structure (was only on version 6.6.0):
Num | Project | References | NuGet |
---|---|---|---|
[0] | EFCore.BulkExtensions | - | |
[1] | EFCore.BulkExtensions.SqlServer | [0] | EFCore.BulkExtensions.SqlServer |
[2] | EFCore.BulkExtensions.PostgreSql | [0] | EFCore.BulkExtensions.PostgreSql |
[3] | EFCore.BulkExtensions.MySql | [0] | EFCore.BulkExtensions.MySql |
[4] | EFCore.BulkExtensions.SQLite | [0] | EFCore.BulkExtensions.SQLite |
[5] | EFCore.BulkExtensions.Common | [1,2,3,4] | EFCore.BulkExtensions |
[0] | EFCore.BulkExtensions is shared core project that has no Nuget. Instead project [5] EFCore.BulkExtensions.Common references all other projects thereby includes all providers and it has NuGet with base name: EFCore.BulkExtensions to keep full compatibility with previous versions.
** UPDATE ** Latest Structure:
Num | Project | NuGet |
---|---|---|
[0] | EFCore.BulkExtensions | EFCore.BulkExtensions |
[1] | EFCore.BulkExtensions.SqlServer | EFCore.BulkExtensions.SqlServer |
[2] | EFCore.BulkExtensions.PostgreSql | EFCore.BulkExtensions.PostgreSql |
[3] | EFCore.BulkExtensions.MySql | EFCore.BulkExtensions.MySql |
[4] | EFCore.BulkExtensions.Sqlite | EFCore.BulkExtensions.Sqlite |
EFCore.BulkExtensions is main Project and Nuget that references all providers. Other per provider projects are empty shells (has just .csproj file) in repo and only on build common files and their adapter files are copied (with script) to the project that is then build for NuGet generation.