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

ef.core 7 support #263

Merged
merged 5 commits into from
Nov 12, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 3 additions & 41 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,46 +1,8 @@
#ignore thumbnails created by windows
Thumbs.db
#Ignore files build by Visual Studio
*.obj
*.pdb
#Ignore files build by IDE
*.user
*.aps
*.pch
*.vspscc
*_i.c
*_p.c
*.ncb
*.suo
*.tlb
*.tlh
*.bak
*.cache
*.ilk
*.log
*.cs.ide
[Bb]in
[Dd]ebug*/
*.lib
*.sbr
bin/
obj/
[Rr]elease*/
_ReSharper*/
[Tt]est[Rr]esult*
linq2db.sln.docstates
Tests/**/UserDataProviders*.*
NuGet/*.nupkg
!*.dll
!*.exe
!*.pdb
linq2db.sln.ide/graph
linq2db.sln.ide/
!Redist/**
/packages
/.vs/*
*.lock.json
/api
/linq2db.github.io
#cake
/tools
/.tools
/.tools/
/.idea/
5 changes: 4 additions & 1 deletion Build/linq2db.Default.props
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<Project>
<PropertyGroup>
<Version>5.13.0</Version>
<Version>7.0.0</Version>

<Authors>Svyatoslav Danyliv, Igor Tkachev, Dmitry Lukashenko, Ilya Chudin</Authors>
<Product>Linq to DB</Product>
Expand All @@ -27,8 +27,11 @@
<GenerateAssemblyVersionAttribute>true</GenerateAssemblyVersionAttribute>
<GenerateAssemblyFileVersionAttribute>true</GenerateAssemblyFileVersionAttribute>
<GenerateNeutralResourcesLanguageAttribute>false</GenerateNeutralResourcesLanguageAttribute>
<DisableImplicitNamespaceImports>true</DisableImplicitNamespaceImports>

<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>

<TargetFrameworks>net6.0</TargetFrameworks>
</PropertyGroup>

<PropertyGroup>
Expand Down
4 changes: 2 additions & 2 deletions Build/linq2db.Tests.props
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
<Import Project="linq2db.Default.props" />

<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<TargetFrameworks>net7.0</TargetFrameworks>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" />
<PackageReference Include="NUnit3TestAdapter">
Expand Down
26 changes: 13 additions & 13 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
@@ -1,27 +1,27 @@
<Project>
<ItemGroup>
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.2.0" />
<PackageVersion Include="NUnit3TestAdapter" Version="4.2.1" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.4.0" />
<PackageVersion Include="NUnit3TestAdapter" Version="4.3.0" />
<PackageVersion Include="NUnit" Version="3.13.3" />
<PackageVersion Include="FluentAssertions" Version="6.6.0" />
<PackageVersion Include="FluentAssertions" Version="6.8.0" />

<PackageVersion Include="linq2db" Version="4.3.0" />
<PackageVersion Include="linq2db.Tools" Version="4.3.0" />

<PackageVersion Include="Microsoft.SourceLink.GitHub" Version="1.1.1" />

<PackageVersion Include="Microsoft.EntityFrameworkCore.Relational" Version="5.0.17" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.InMemory" Version="5.0.17" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Relational" Version="7.0.0" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.InMemory" Version="7.0.0" />

<PackageVersion Include="Microsoft.Extensions.Logging" Version="5.0.0" />
<PackageVersion Include="Microsoft.Extensions.Logging.Console" Version="5.0.0" />
<PackageVersion Include="Microsoft.Extensions.Logging" Version="7.0.0" />
<PackageVersion Include="Microsoft.Extensions.Logging.Console" Version="7.0.0" />

<PackageVersion Include="Pomelo.EntityFrameworkCore.MySql" Version="5.0.4" />
<PackageVersion Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="5.0.10" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Sqlite" Version="5.0.17" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.SqlServer" Version="5.0.17" />
<PackageVersion Include="Pomelo.EntityFrameworkCore.MySql" Version="6.0.2" />
<PackageVersion Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="7.0.0" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Sqlite" Version="7.0.0" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.SqlServer" Version="7.0.0" />

<PackageVersion Include="EntityFrameworkCore.FSharp" Version="5.0.3" />
<PackageVersion Include="FSharp.Core" Version="6.0.5" />
<PackageVersion Include="EntityFrameworkCore.FSharp" Version="6.0.7" />
<PackageVersion Include="FSharp.Core" Version="7.0.0" />
</ItemGroup>
</Project>
6 changes: 3 additions & 3 deletions NuGet/linq2db.EntityFrameworkCore.nuspec
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<title>Linq to DB (linq2db) extensions for Entity Framework Core</title>
<authors>Igor Tkachev, Ilya Chudin, Svyatoslav Danyliv, Dmitry Lukashenko</authors>
<owners>Igor Tkachev, Ilya Chudin, Svyatoslav Danyliv, Dmitry Lukashenko</owners>
<copyright>Copyright © 2020-2021 Igor Tkachev, Ilya Chudin, Svyatoslav Danyliv, Dmitry Lukashenko</copyright>
<copyright>Copyright © 2020-2022 Igor Tkachev, Ilya Chudin, Svyatoslav Danyliv, Dmitry Lukashenko</copyright>
<description>Allows to execute Linq to DB (linq2db) queries in Entity Framework Core DbContext.</description>
<summary />
<tags>linq linq2db LinqToDB ORM database entity-framework-core EntityFrameworkCore EFCore DB SQL SqlServer SqlCe SqlServerCe MySql Firebird SQLite Oracle ODP PostgreSQL DB2</tags>
Expand All @@ -14,8 +14,8 @@
<projectUrl>https://github.com/linq2db/linq2db.EntityFrameworkCore</projectUrl>
<license type="file">MIT-LICENSE.txt</license>
<dependencies>
<group targetFramework=".NETStandard2.1">
<dependency id="Microsoft.EntityFrameworkCore.Relational" version="5.0.17" />
<group targetFramework="net6.0">
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

net7.0. ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

<dependency id="Microsoft.EntityFrameworkCore.Relational" version="7.0.0" />
<dependency id="linq2db" version="4.3.0" />
</group>
</dependencies>
Expand Down
2 changes: 1 addition & 1 deletion Source/LinqToDB.EntityFrameworkCore/EFConnectionInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ namespace LinqToDB.EntityFrameworkCore
/// <summary>
/// Contains database connectivity information, extracted from EF.Core.
/// </summary>
public class EFConnectionInfo
public sealed class EFConnectionInfo
{
/// <summary>
/// Gets or sets database connection instance.
Expand Down
40 changes: 20 additions & 20 deletions Source/LinqToDB.EntityFrameworkCore/EFCoreMetadataReader.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Data;
Expand Down Expand Up @@ -31,7 +30,7 @@ namespace LinqToDB.EntityFrameworkCore
/// <summary>
/// LINQ To DB metadata reader for EF.Core model.
/// </summary>
internal class EFCoreMetadataReader : IMetadataReader
internal sealed class EFCoreMetadataReader : IMetadataReader
{
readonly IModel? _model;
private readonly RelationalSqlTranslatingExpressionVisitorDependencies? _dependencies;
Expand All @@ -40,8 +39,7 @@ internal class EFCoreMetadataReader : IMetadataReader
private readonly ConcurrentDictionary<MemberInfo, EFCoreExpressionAttribute?> _calculatedExtensions = new();
private readonly IDiagnosticsLogger<DbLoggerCategory.Query>? _logger;

public EFCoreMetadataReader(
IModel? model, IInfrastructure<IServiceProvider>? accessor)
public EFCoreMetadataReader(IModel? model, IInfrastructure<IServiceProvider>? accessor)
{
_model = model;
if (accessor != null)
Expand All @@ -61,7 +59,7 @@ public T[] GetAttributes<T>(Type type, bool inherit = true) where T : Attribute
if (typeof(T) == typeof(TableAttribute))
{
var storeObjectId = GetStoreObjectIdentifier(et);
return new[] { (T)(Attribute)new TableAttribute(storeObjectId!.Value.Name) { Schema = storeObjectId!.Value.Schema } };
return new[] { (T)(Attribute)new TableAttribute() { Schema = storeObjectId?.Schema, Name = storeObjectId?.Name } };
}
if (typeof(T) == typeof(QueryFilterAttribute))
{
Expand Down Expand Up @@ -208,7 +206,7 @@ public T[] GetAttributes<T>(Type type, MemberInfo memberInfo, bool inherit = tru

if (prop != null)
{
var discriminator = et.GetDiscriminatorProperty();
var discriminator = et.FindDiscriminatorProperty();

var isPrimaryKey = prop.IsPrimaryKey();
var primaryKeyOrder = 0;
Expand All @@ -225,14 +223,14 @@ public T[] GetAttributes<T>(Type type, MemberInfo memberInfo, bool inherit = tru
if (_annotationProvider != null && storeObjectId != null)
{
if (prop.FindColumn(storeObjectId.Value) is IColumn column)
annotations = annotations.Concat(_annotationProvider.For(column));
annotations = annotations.Concat(_annotationProvider.For(column, false));
}

var isIdentity = annotations
.Any(a =>
{
if (a.Name.EndsWith(":ValueGenerationStrategy"))
return a.Value?.ToString()!.Contains("Identity") == true;
return a.Value?.ToString()?.Contains("Identity") == true;

if (a.Name.EndsWith(":Autoincrement"))
return a.Value is bool b && b;
Expand All @@ -242,7 +240,7 @@ public T[] GetAttributes<T>(Type type, MemberInfo memberInfo, bool inherit = tru
{
if (a.Value is string str)
{
return str.ToLower().Contains("nextval");
return str.ToLowerInvariant().Contains("nextval");
}
}

Expand All @@ -259,7 +257,8 @@ public T[] GetAttributes<T>(Type type, MemberInfo memberInfo, bool inherit = tru
}
else
{
dataType = SqlDataType.GetDataType(typeMapping.ClrType).Type.DataType;
var ms = _model != null ? LinqToDBForEFTools.GetMappingSchema(_model, null) : MappingSchema.Default;
dataType = ms.GetDataType(typeMapping.ClrType).Type.DataType;
}
}

Expand All @@ -278,7 +277,7 @@ public T[] GetAttributes<T>(Type type, MemberInfo memberInfo, bool inherit = tru
{
(T)(Attribute)new ColumnAttribute
{
Name = prop.GetColumnName(storeObjectId!.Value),
Name = storeObjectId != null ? prop.GetColumnName(storeObjectId.Value) : null,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

storeObjectId is not value type anymore?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is, but there were assumption that GetStoreObjectIdentifier will return non-null value. I'm not sure it is always true, so I've replaced code a bit to treat it as nullable value to avoid NRE

Length = prop.GetMaxLength() ?? 0,
CanBeNull = prop.IsNullable,
DbType = prop.GetColumnType(),
Expand Down Expand Up @@ -408,7 +407,7 @@ public T[] GetAttributes<T>(Type type, MemberInfo memberInfo, bool inherit = tru
return Array.Empty<T>();
}

class ValueConverter : IValueConverter
sealed class ValueConverter : IValueConverter
{
public ValueConverter(
LambdaExpression convertToProviderExpression,
Expand All @@ -425,7 +424,7 @@ public ValueConverter(

}

class SqlTransparentExpression : SqlExpression
sealed class SqlTransparentExpression : SqlExpression
{
public Expression Expression { get; }

Expand All @@ -439,7 +438,7 @@ protected override void Print(ExpressionPrinter expressionPrinter)
expressionPrinter.Print(Expression);
}

protected bool Equals(SqlTransparentExpression other)
private bool Equals(SqlTransparentExpression other)
{
return ReferenceEquals(this, other);
}
Expand Down Expand Up @@ -596,23 +595,24 @@ string PrepareExpressionText(Expression? expr)
return text;
}

// https://github.com/npgsql/efcore.pg/blob/main/src/EFCore.PG/Query/Expressions/Internal/PostgresBinaryExpression.cs
if (newExpression.GetType().Name == "PostgresBinaryExpression")
{
// Handling NpgSql's PostgresBinaryExpression

var left = newExpression.GetType().GetProperty("Left")?.GetValue(newExpression) as Expression;
var right = newExpression.GetType().GetProperty("Right")?.GetValue(newExpression) as Expression;
var left = (Expression)newExpression.GetType().GetProperty("Left")!.GetValue(newExpression)!;
var right = (Expression)newExpression.GetType().GetProperty("Right")!.GetValue(newExpression)!;

var operand = newExpression.GetType().GetProperty("OperatorType")?.GetValue(newExpression)!.ToString();
var operand = newExpression.GetType().GetProperty("OperatorType")!.GetValue(newExpression)!.ToString()!;

var operandExpr = operand switch
{
"Contains"
when left!.Type.Name == "NpgsqlInetTypeMapping" ||
when left.Type.Name == "NpgsqlInetTypeMapping" ||
left.Type.Name == "NpgsqlCidrTypeMapping"
=> ">>",
"ContainedBy"
when left!.Type.Name == "NpgsqlInetTypeMapping" ||
when left.Type.Name == "NpgsqlInetTypeMapping" ||
left.Type.Name == "NpgsqlCidrTypeMapping"
=> "<<",
"Contains" => "@>",
Expand Down Expand Up @@ -674,7 +674,7 @@ private static Expression UnwrapConverted(Expression expr)
if (expr is SqlFunctionExpression func)
{
if (string.Equals(func.Name, "COALESCE", StringComparison.InvariantCultureIgnoreCase) &&
func.Arguments!.Count == 2 && func.Arguments[1].NodeType == ExpressionType.Extension)
func.Arguments?.Count == 2 && func.Arguments[1].NodeType == ExpressionType.Extension)
return UnwrapConverted(func.Arguments[0]);
}

Expand Down
2 changes: 1 addition & 1 deletion Source/LinqToDB.EntityFrameworkCore/EFProviderInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ namespace LinqToDB.EntityFrameworkCore
/// <summary>
/// Required integration information about underlying database provider, extracted from EF.Core.
/// </summary>
public class EFProviderInfo
public sealed class EFProviderInfo
{
/// <summary>
/// Gets or sets database connection instance.
Expand Down
1 change: 0 additions & 1 deletion Source/LinqToDB.EntityFrameworkCore/ILinqToDBForEFTools.cs
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,5 @@ public interface ILinqToDBForEFTools
/// Entities will be attached only if AsNoTracking() is not used in query and DbContext is configured to track entities.
/// </summary>
bool EnableChangeTracker { get; set; }

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ TResult IAsyncQueryProvider.ExecuteAsync<TResult>(Expression expression, Cancell
{
var item = typeof(TResult).GetGenericArguments()[0];
var method = _executeAsyncMethodInfo.MakeGenericMethod(item);
return (TResult) method.Invoke(QueryProvider, new object[] { expression, cancellationToken })!;
return (TResult)method.Invoke(QueryProvider, new object[] { expression, cancellationToken })!;
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ namespace LinqToDB.EntityFrameworkCore
/// <summary>
/// LINQ To DB async extensions adapter to call EF.Core functionality instead of default implementation.
/// </summary>
public class LinqToDBExtensionsAdapter : IExtensionsAdapter
public sealed class LinqToDBExtensionsAdapter : IExtensionsAdapter
{
/// <inheritdoc cref="EntityFrameworkQueryableExtensions.AsAsyncEnumerable{TSource}(IQueryable{TSource})"/>
public IAsyncEnumerable<TSource> AsAsyncEnumerable<TSource>(IQueryable<TSource> source)
Expand Down Expand Up @@ -90,18 +90,14 @@ public Task<TSource> FirstAsync<TSource>(
public Task<TSource?> FirstOrDefaultAsync<TSource>(
IQueryable<TSource> source,
CancellationToken token)
#pragma warning disable CS8619 // Nullability of reference types in value doesn't match target type.
=> EntityFrameworkQueryableExtensions.FirstOrDefaultAsync(source, token);
#pragma warning restore CS8619 // Nullability of reference types in value doesn't match target type.

/// <inheritdoc cref="EntityFrameworkQueryableExtensions.FirstOrDefaultAsync{TSource}(IQueryable{TSource}, Expression{Func{TSource, bool}}, CancellationToken)"/>
public Task<TSource?> FirstOrDefaultAsync<TSource>(
IQueryable<TSource> source,
Expression<Func<TSource,bool>> predicate,
CancellationToken token)
#pragma warning disable CS8619 // Nullability of reference types in value doesn't match target type.
=> EntityFrameworkQueryableExtensions.FirstOrDefaultAsync(source, predicate, token);
#pragma warning restore CS8619 // Nullability of reference types in value doesn't match target type.

/// <inheritdoc cref="EntityFrameworkQueryableExtensions.SingleAsync{TSource}(IQueryable{TSource}, CancellationToken)"/>
public Task<TSource> SingleAsync<TSource>(
Expand All @@ -120,18 +116,14 @@ public Task<TSource> SingleAsync<TSource>(
public Task<TSource?> SingleOrDefaultAsync<TSource>(
IQueryable<TSource> source,
CancellationToken token)
#pragma warning disable CS8619 // Nullability of reference types in value doesn't match target type.
=> EntityFrameworkQueryableExtensions.SingleOrDefaultAsync(source, token);
#pragma warning restore CS8619 // Nullability of reference types in value doesn't match target type.

/// <inheritdoc cref="EntityFrameworkQueryableExtensions.SingleOrDefaultAsync{TSource}(IQueryable{TSource}, Expression{Func{TSource, bool}}, CancellationToken)"/>
public Task<TSource?> SingleOrDefaultAsync<TSource>(
IQueryable<TSource> source,
Expression<Func<TSource,bool>> predicate,
CancellationToken token)
#pragma warning disable CS8619 // Nullability of reference types in value doesn't match target type.
=> EntityFrameworkQueryableExtensions.SingleOrDefaultAsync(source, predicate, token);
#pragma warning restore CS8619 // Nullability of reference types in value doesn't match target type.

/// <inheritdoc cref="EntityFrameworkQueryableExtensions.ContainsAsync{TSource}(IQueryable{TSource}, TSource, CancellationToken)"/>
public Task<bool> ContainsAsync<TSource>(
Expand Down
Loading