Skip to content
This repository was archived by the owner on Feb 1, 2025. It is now read-only.

Commit 65022b4

Browse files
authored
Merge pull request #92 from linq2db/master
Release 5.1.0
2 parents 760653c + 7901e1e commit 65022b4

File tree

89 files changed

+2123
-2889
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

89 files changed

+2123
-2889
lines changed

Build/linq2db.Default.props

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,18 @@
22
<PropertyGroup>
33
<Version>5.0.2</Version>
44

5-
<Description>Allows to execute Linq to DB (linq2db) queries in Entity Framework Core DbContext.</Description>
6-
<Title>Linq to DB (linq2db) extensions for Entity Framework Core</Title>
7-
<AssemblyTitle>$(Title)</AssemblyTitle>
85
<Authors>Svyatoslav Danyliv, Igor Tkachev, Dmitry Lukashenko, Ilya Chudin</Authors>
96
<Product>Linq to DB</Product>
107
<Company>linq2db.net</Company>
118
<Copyright>2002-2020 linq2db.net</Copyright>
129
<RepositoryUrl>https://github.com/linq2db/linq2db.EntityFrameworkCore</RepositoryUrl>
1310
<RepositoryType>git</RepositoryType>
1411

15-
<AppDesignerFolder>Properties</AppDesignerFolder>
16-
<LangVersion>8.0</LangVersion>
17-
<Nullable>disable</Nullable>
12+
<LangVersion>9.0</LangVersion>
13+
<Nullable>enable</Nullable>
1814
<WarningLevel>4</WarningLevel>
1915
<ErrorReport>prompt</ErrorReport>
20-
<NoWarn>1701;1591</NoWarn>
16+
<AnalysisLevel>preview</AnalysisLevel>
2117

2218
<AutoGenerateBindingRedirects>false</AutoGenerateBindingRedirects>
2319
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>

Build/linq2db.Tests.props

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
<Project>
2+
<Import Project="linq2db.Default.props" />
3+
24
<PropertyGroup>
35
<!--<TargetFrameworks>net5.0</TargetFrameworks>-->
46
<TargetFrameworks>netcoreapp3.1</TargetFrameworks>

NuGet/linq2db.EntityFrameworkCore.nuspec

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@
1515
<license type="file">MIT-LICENSE.txt</license>
1616
<dependencies>
1717
<group targetFramework=".NETStandard2.1">
18-
<dependency id="Microsoft.EntityFrameworkCore.Relational" version="5.0.0" />
19-
<dependency id="linq2db" version="3.1.6" />
18+
<dependency id="Microsoft.EntityFrameworkCore.Relational" version="5.0.2" />
19+
<dependency id="linq2db" version="3.2.3" />
2020
</group>
2121
</dependencies>
2222
</metadata>

Source/LinqToDB.EntityFrameworkCore/EFConnectionInfo.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,11 @@ public class EFConnectionInfo
1010
/// <summary>
1111
/// Gets or sets database connection instance.
1212
/// </summary>
13-
public DbConnection Connection { get; set; }
13+
public DbConnection? Connection { get; set; }
1414

1515
/// <summary>
1616
/// Gets or sets database connection string.
1717
/// </summary>
18-
public string ConnectionString { get; set; }
18+
public string? ConnectionString { get; set; }
1919
}
2020
}

Source/LinqToDB.EntityFrameworkCore/EFCoreMetadataReader.cs

Lines changed: 139 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using System.Linq;
55
using System.Linq.Expressions;
66
using System.Reflection;
7+
using System.Runtime.CompilerServices;
78
using LinqToDB.Expressions;
89
using LinqToDB.Reflection;
910
using Microsoft.EntityFrameworkCore;
@@ -29,17 +30,17 @@ namespace LinqToDB.EntityFrameworkCore
2930
/// </summary>
3031
internal class EFCoreMetadataReader : IMetadataReader
3132
{
32-
readonly IModel _model;
33-
private readonly RelationalSqlTranslatingExpressionVisitorDependencies _dependencies;
34-
private readonly IRelationalTypeMappingSource _mappingSource;
35-
private readonly ConcurrentDictionary<MemberInfo, EFCoreExpressionAttribute> _calculatedExtensions = new ConcurrentDictionary<MemberInfo, EFCoreExpressionAttribute>();
36-
private readonly IDiagnosticsLogger<DbLoggerCategory.Query> _logger;
37-
38-
public EFCoreMetadataReader(IModel model,
39-
RelationalSqlTranslatingExpressionVisitorDependencies dependencies,
40-
IRelationalTypeMappingSource mappingSource,
41-
IDiagnosticsLogger<DbLoggerCategory.Query> logger
42-
)
33+
readonly IModel? _model;
34+
private readonly RelationalSqlTranslatingExpressionVisitorDependencies? _dependencies;
35+
private readonly IRelationalTypeMappingSource? _mappingSource;
36+
private readonly ConcurrentDictionary<MemberInfo, EFCoreExpressionAttribute?> _calculatedExtensions = new ConcurrentDictionary<MemberInfo, EFCoreExpressionAttribute?>();
37+
private readonly IDiagnosticsLogger<DbLoggerCategory.Query>? _logger;
38+
39+
public EFCoreMetadataReader(
40+
IModel? model,
41+
RelationalSqlTranslatingExpressionVisitorDependencies? dependencies,
42+
IRelationalTypeMappingSource? mappingSource,
43+
IDiagnosticsLogger<DbLoggerCategory.Query>? logger)
4344
{
4445
_model = model;
4546
_dependencies = dependencies;
@@ -54,7 +55,8 @@ public T[] GetAttributes<T>(Type type, bool inherit = true) where T : Attribute
5455
{
5556
if (typeof(T) == typeof(TableAttribute))
5657
{
57-
return new[] { (T)(Attribute)new TableAttribute(et.GetTableName()) { Schema = et.GetSchema() } };
58+
var storeObjectId = GetStoreObjectIdentifier(et);
59+
return new[] { (T)(Attribute)new TableAttribute(storeObjectId!.Value.Name) { Schema = storeObjectId!.Value.Schema } };
5860
}
5961
if (typeof(T) == typeof(QueryFilterAttribute))
6062
{
@@ -67,19 +69,12 @@ public T[] GetAttributes<T>(Type type, bool inherit = true) where T : Attribute
6769
var contextProp = Expression.Property(Expression.Convert(dcParam, typeof(LinqToDBForEFToolsDataConnection)), "Context");
6870
var filterBody = filter.Body.Transform(e =>
6971
{
70-
switch (e)
72+
if (typeof(DbContext).IsSameOrParentOf(e.Type))
7173
{
72-
case ConstantExpression cnt:
73-
{
74-
if (typeof(DbContext).IsSameOrParentOf(cnt.Type))
75-
{
76-
Expression newExpr = contextProp;
77-
if (newExpr.Type != cnt.Type)
78-
newExpr = Expression.Convert(newExpr, cnt.Type);
79-
return newExpr;
80-
}
81-
break;
82-
}
74+
Expression newExpr = contextProp;
75+
if (newExpr.Type != e.Type)
76+
newExpr = Expression.Convert(newExpr, e.Type);
77+
return newExpr;
8378
}
8479

8580
return e;
@@ -114,7 +109,7 @@ public T[] GetAttributes<T>(Type type, bool inherit = true) where T : Attribute
114109
return Array.Empty<T>();
115110
}
116111

117-
static bool CompareProperty(MemberInfo property, MemberInfo memberInfo)
112+
static bool CompareProperty(MemberInfo? property, MemberInfo memberInfo)
118113
{
119114
if (property == memberInfo)
120115
return true;
@@ -133,13 +128,11 @@ static bool CompareProperty(MemberInfo property, MemberInfo memberInfo)
133128
return false;
134129
}
135130

136-
[System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "EF1001:Internal EF Core API usage.", Justification = "<Pending>")]
137131
static bool CompareProperty(IProperty property, MemberInfo memberInfo)
138132
{
139133
return CompareProperty(property.GetIdentifyingMemberInfo(), memberInfo);
140134
}
141135

142-
[System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "EF1001:Internal EF Core API usage.", Justification = "<Pending>")]
143136
public T[] GetAttributes<T>(Type type, MemberInfo memberInfo, bool inherit = true) where T : Attribute
144137
{
145138
if (typeof(Expression).IsSameOrParentOf(type))
@@ -164,9 +157,11 @@ public T[] GetAttributes<T>(Type type, MemberInfo memberInfo, bool inherit = tru
164157
.FirstOrDefault(v => CompareProperty(v.p, memberInfo))?.index ?? 0;
165158
}
166159

160+
var storeObjectId = GetStoreObjectIdentifier(et);
161+
167162
return new T[]{(T)(Attribute) new ColumnAttribute
168163
{
169-
Name = prop.GetColumnName(),
164+
Name = prop.GetColumnName(storeObjectId!.Value),
170165
Length = prop.GetMaxLength() ?? 0,
171166
CanBeNull = prop.IsNullable,
172167
DbType = prop.GetColumnType(),
@@ -314,7 +309,7 @@ class SqlTransparentExpression : SqlExpression
314309
{
315310
public Expression Expression { get; }
316311

317-
public SqlTransparentExpression(Expression expression, RelationalTypeMapping typeMapping) : base(expression.Type, typeMapping)
312+
public SqlTransparentExpression(Expression expression, RelationalTypeMapping? typeMapping) : base(expression.Type, typeMapping)
318313
{
319314
Expression = expression;
320315
}
@@ -323,18 +318,45 @@ protected override void Print(ExpressionPrinter expressionPrinter)
323318
{
324319
expressionPrinter.Print(Expression);
325320
}
321+
322+
protected bool Equals(SqlTransparentExpression other)
323+
{
324+
return ReferenceEquals(this, other);
325+
}
326+
327+
public override bool Equals(object obj)
328+
{
329+
if (ReferenceEquals(null, obj)) return false;
330+
if (ReferenceEquals(this, obj)) return true;
331+
if (obj.GetType() != this.GetType()) return false;
332+
return Equals((SqlTransparentExpression) obj);
333+
}
334+
335+
public override int GetHashCode()
336+
{
337+
return RuntimeHelpers.GetHashCode(this);
338+
}
339+
}
340+
341+
private StoreObjectIdentifier? GetStoreObjectIdentifier(IEntityType entityType)
342+
{
343+
return entityType.GetTableName() switch
344+
{
345+
not null => StoreObjectIdentifier.Create(entityType, StoreObjectType.Table),
346+
null => StoreObjectIdentifier.Create(entityType, StoreObjectType.View),
347+
};
326348
}
327349

328-
private Sql.ExpressionAttribute GetDbFunctionFromMethodCall(Type type, MethodInfo methodInfo)
350+
private Sql.ExpressionAttribute? GetDbFunctionFromMethodCall(Type type, MethodInfo methodInfo)
329351
{
330352
if (_dependencies == null || _model == null)
331353
return null;
332354

333-
methodInfo = (MethodInfo) type.GetMemberEx(methodInfo) ?? methodInfo;
355+
methodInfo = (MethodInfo?) type.GetMemberEx(methodInfo) ?? methodInfo;
334356

335357
var found = _calculatedExtensions.GetOrAdd(methodInfo, mi =>
336358
{
337-
EFCoreExpressionAttribute result = null;
359+
EFCoreExpressionAttribute? result = null;
338360

339361
if (!methodInfo.IsGenericMethodDefinition && !mi.GetCustomAttributes<Sql.ExpressionAttribute>().Any())
340362
{
@@ -364,19 +386,19 @@ private Sql.ExpressionAttribute GetDbFunctionFromMethodCall(Type type, MethodInf
364386
return found;
365387
}
366388

367-
private Sql.ExpressionAttribute GetDbFunctionFromProperty(Type type, PropertyInfo propInfo)
389+
private Sql.ExpressionAttribute? GetDbFunctionFromProperty(Type type, PropertyInfo propInfo)
368390
{
369391
if (_dependencies == null || _model == null)
370392
return null;
371393

372-
propInfo = (PropertyInfo) type.GetMemberEx(propInfo) ?? propInfo;
394+
propInfo = (PropertyInfo?) type.GetMemberEx(propInfo) ?? propInfo;
373395

374396
var found = _calculatedExtensions.GetOrAdd(propInfo, mi =>
375397
{
376-
EFCoreExpressionAttribute result = null;
398+
EFCoreExpressionAttribute? result = null;
377399

378-
if ((propInfo.GetMethod?.IsStatic != true)
379-
&& !(mi is DynamicColumnInfo)
400+
if ((propInfo.GetMethod?.IsStatic != true)
401+
&& !(mi is DynamicColumnInfo)
380402
&& !mi.GetCustomAttributes<Sql.ExpressionAttribute>().Any())
381403
{
382404
var objExpr = new SqlTransparentExpression(Expression.Constant(DefaultValue.GetValue(type), type), _mappingSource?.FindMapping(propInfo));
@@ -397,9 +419,34 @@ private Sql.ExpressionAttribute GetDbFunctionFromProperty(Type type, PropertyInf
397419

398420
private static EFCoreExpressionAttribute ConvertToExpressionAttribute(MemberInfo memberInfo, Expression newExpression, Expression[] parameters)
399421
{
400-
string PrepareExpressionText(Expression expr)
422+
string PrepareExpressionText(Expression? expr)
401423
{
402-
var idx = Array.IndexOf(parameters, expr);
424+
var idx = -1;
425+
426+
for (var index = 0; index < parameters.Length; index++)
427+
{
428+
var param = parameters[index];
429+
var found = ReferenceEquals(expr, param);
430+
if (!found)
431+
{
432+
if (param is SqlTransparentExpression transparent)
433+
{
434+
if (transparent.Expression is ConstantExpression constantExpr &&
435+
expr is SqlConstantExpression sqlConstantExpr)
436+
{
437+
//found = sqlConstantExpr.Value.Equals(constantExpr.Value);
438+
found = true;
439+
}
440+
}
441+
}
442+
443+
if (found)
444+
{
445+
idx = index;
446+
break;
447+
}
448+
}
449+
403450
if (idx >= 0)
404451
return $"{{{idx}}}";
405452

@@ -429,18 +476,63 @@ string PrepareExpressionText(Expression expr)
429476
return text;
430477
}
431478

432-
if (newExpression.GetType().GetProperty("Left") != null &&
433-
newExpression.GetType().GetProperty("Right") != null &&
434-
newExpression.GetType().GetProperty("Operator") != null)
479+
if (newExpression.GetType().Name == "PostgresBinaryExpression")
435480
{
436-
// Handling NpgSql's CustomBinaryExpression
481+
// Handling NpgSql's PostgresBinaryExpression
437482

438-
var left = newExpression.GetType().GetProperty("Left")?.GetValue(newExpression) as Expression;
483+
var left = newExpression.GetType().GetProperty("Left")?.GetValue(newExpression) as Expression;
439484
var right = newExpression.GetType().GetProperty("Right")?.GetValue(newExpression) as Expression;
440485

441-
var operand = newExpression.GetType().GetProperty("Operator")?.GetValue(newExpression) as string;
486+
var operand = newExpression.GetType().GetProperty("OperatorType")?.GetValue(newExpression).ToString();
487+
488+
var operandExpr = operand;
489+
490+
operandExpr = operand switch
491+
{
492+
"Contains"
493+
when left!.Type.Name == "NpgsqlInetTypeMapping" ||
494+
left.Type.Name == "NpgsqlCidrTypeMapping"
495+
=> ">>",
496+
"ContainedBy"
497+
when left!.Type.Name == "NpgsqlInetTypeMapping" ||
498+
left.Type.Name == "NpgsqlCidrTypeMapping"
499+
=> "<<",
500+
"Contains" => "@>",
501+
"ContainedBy" => "<@",
502+
"Overlaps" => "&&",
503+
"AtTimeZone" => "AT TIME ZONE",
504+
"NetworkContainedByOrEqual" => "<<=",
505+
"NetworkContainsOrEqual" => ">>=",
506+
"NetworkContainsOrContainedBy" => "&&",
507+
"RangeIsStrictlyLeftOf" => "<<",
508+
"RangeIsStrictlyRightOf" => ">>",
509+
"RangeDoesNotExtendRightOf" => "&<",
510+
"RangeDoesNotExtendLeftOf" => "&>",
511+
"RangeIsAdjacentTo" => "-|-",
512+
"RangeUnion" => "+",
513+
"RangeIntersect" => "*",
514+
"RangeExcept" => "-",
515+
"TextSearchMatch" => "@@",
516+
"TextSearchAnd" => "&&",
517+
"TextSearchOr" => "||",
518+
"JsonExists" => "?",
519+
"JsonExistsAny" => "?|",
520+
"JsonExistsAll" => "?&",
521+
_ => throw new InvalidOperationException(
522+
$"Unknown PostgresBinaryExpression.OperatorType: '{operand}'")
523+
};
524+
525+
switch (operand)
526+
{
527+
case "Contains":
528+
operandExpr = "@>"; break;
529+
case "ContainedBy":
530+
operandExpr = "<@"; break;
531+
case "Overlaps":
532+
operandExpr = "&&"; break;
533+
}
442534

443-
var text = $"{PrepareExpressionText(left)} {operand} {PrepareExpressionText(right)}";
535+
var text = $"{PrepareExpressionText(left)} {operandExpr} {PrepareExpressionText(right)}";
444536

445537
return text;
446538
}
@@ -464,7 +556,7 @@ private static Expression UnwrapConverted(Expression expr)
464556
if (expr is SqlFunctionExpression func)
465557
{
466558
if (string.Equals(func.Name, "COALESCE", StringComparison.InvariantCultureIgnoreCase) &&
467-
func.Arguments.Count == 2 && func.Arguments[1].NodeType == ExpressionType.Default)
559+
func.Arguments.Count == 2 && func.Arguments[1].NodeType == ExpressionType.Extension)
468560
return UnwrapConverted(func.Arguments[0]);
469561
}
470562

Source/LinqToDB.EntityFrameworkCore/EFProviderInfo.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,16 @@ public class EFProviderInfo
1313
/// <summary>
1414
/// Gets or sets database connection instance.
1515
/// </summary>
16-
public DbConnection Connection { get; set; }
16+
public DbConnection? Connection { get; set; }
1717

1818
/// <summary>
1919
/// Gets or sets EF.Core context instance.
2020
/// </summary>
21-
public DbContext Context { get; set; }
21+
public DbContext? Context { get; set; }
2222

2323
/// <summary>
2424
/// Gets or sets EF.Core context options instance.
2525
/// </summary>
26-
public IDbContextOptions Options { get; set; }
26+
public IDbContextOptions? Options { get; set; }
2727
}
2828
}

0 commit comments

Comments
 (0)