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

Commit c00e44a

Browse files
authored
Merge pull request #119 from linq2db/master
Release 5.1.6
2 parents 925055c + cb041ee commit c00e44a

File tree

10 files changed

+218
-64
lines changed

10 files changed

+218
-64
lines changed

.editorconfig

Lines changed: 74 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -40,12 +40,6 @@ end_of_line = lf
4040
dotnet_sort_system_directives_first = true
4141
dotnet_separate_import_directive_groups = false
4242

43-
# Avoid "this." and "Me." if not necessary
44-
dotnet_style_qualification_for_field = false:refactoring
45-
dotnet_style_qualification_for_property = false:refactoring
46-
dotnet_style_qualification_for_method = false:refactoring
47-
dotnet_style_qualification_for_event = false:refactoring
48-
4943
# Use language keywords instead of framework type names for type references
5044
dotnet_style_predefined_type_for_locals_parameters_members = true:error
5145
dotnet_style_predefined_type_for_member_access = true:suggestion
@@ -159,6 +153,9 @@ dotnet_diagnostic.CA2231.severity = none # CA2231: Overload operator equals on o
159153
# disabled, as NETFX doesn't have string.Contains overloads with comparison type
160154
dotnet_diagnostic.CA2249.severity = none # CA2249: Consider using String.Contains instead of String.IndexOf
161155
dotnet_diagnostic.CA3075.severity = error # CA3075: Insecure DTD Processing
156+
# very slow (https://github.com/dotnet/roslyn-analyzers/issues/4754)
157+
# also we don't have code that could be targeted by it
158+
dotnet_diagnostic.CA3076.severity = none # CA3076: Insecure XSLT Script Execution
162159

163160
#########################################################################################################
164161
# inactive diagnostics (not reviewed yet => disabled to not fail build, basically TODO list for future) #
@@ -186,39 +183,78 @@ dotnet_diagnostic.CA1806.severity = none # CA1806: Do not ignore method results
186183
dotnet_diagnostic.CA1822.severity = none # CA1822: Mark members as static
187184
dotnet_diagnostic.CA2211.severity = none # CA2211: Non-constant fields should not be visible
188185

189-
################
190-
# VS analyzers #
191-
################
192-
# IDE0051: Remove unused private members
193-
dotnet_diagnostic.IDE0051.severity = warning
194-
195-
# IDE0055: Fix whitespace formatting
196-
dotnet_diagnostic.IDE0055.severity = none
197-
198-
# IDE0057: Use range operators (requires .net core 3 or higher)
199-
dotnet_diagnostic.IDE0056.severity = none
200-
dotnet_diagnostic.IDE0057.severity = none
201-
202-
# IDE0060: unused parameters should be removed or have a discard symbol name
203-
dotnet_diagnostic.IDE0060.severity = none
186+
###################################################################################
187+
# VS analyzers #
188+
# https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ #
189+
###################################################################################
190+
dotnet_diagnostic.IDE0001.severity = error # IDE0001: Simplify name
191+
dotnet_diagnostic.IDE0002.severity = error # IDE0002: Simplify member access
204192

205-
# CS1998: Async method lacks 'await' operators and will run synchronously
206-
dotnet_diagnostic.CS1998.severity = error
207-
208-
# CS8618: Non-nullable field is uninitialized. Consider declaring as nullable.
209-
dotnet_diagnostic.CS8618.severity = error
210-
211-
# CS4014: Because this call is not awaited, execution of the current method continues before the call is completed
212-
dotnet_diagnostic.CS4014.severity = error
193+
# IDE0003: Remove this or Me qualification
194+
# IDE0009: Add this or Me qualification
195+
dotnet_diagnostic.IDE0003.severity = error
196+
dotnet_style_qualification_for_field = false:refactoring
197+
dotnet_style_qualification_for_property = false:refactoring
198+
dotnet_style_qualification_for_method = false:refactoring
199+
dotnet_style_qualification_for_event = false:refactoring
213200

214-
dotnet_diagnostic.IDE0058.severity = none # IDE0058: Expression value is never used
201+
dotnet_diagnostic.IDE0004.severity = error # IDE0004: Remove unnecessary cast
202+
dotnet_diagnostic.IDE0005.severity = error # IDE0005: Remove unnecessary import
203+
204+
# not reviewed yet rules
205+
dotnet_diagnostic.IDE0007.severity = none # IDE0007: Use 'var' instead of explicit type
206+
dotnet_diagnostic.IDE0010.severity = none # IDE0010: Add missing cases to switch statement
207+
dotnet_diagnostic.IDE0011.severity = none # IDE0011: Add braces
208+
dotnet_diagnostic.IDE0016.severity = none # IDE0016: Use throw expression
209+
dotnet_diagnostic.IDE0017.severity = none # IDE0017: Use object initializers
210+
dotnet_diagnostic.IDE0018.severity = none # IDE0018: Inline variable declaration
211+
dotnet_diagnostic.IDE0019.severity = none # IDE0019: Use pattern matching to avoid 'as' followed by a 'null' check
212+
dotnet_diagnostic.IDE0025.severity = none # IDE0025: Use expression body for properties
213+
dotnet_diagnostic.IDE0026.severity = none # IDE0026: Use expression body for indexers
214+
dotnet_diagnostic.IDE0027.severity = none # IDE0027: Use expression body for accessors
215+
dotnet_diagnostic.IDE0028.severity = none # IDE0028: Use collection initializers
216+
dotnet_diagnostic.IDE0029.severity = none # IDE0029: Use coalesce expression (non-nullable types)
217+
dotnet_diagnostic.IDE0030.severity = none # IDE0030: Use coalesce expression
218+
dotnet_diagnostic.IDE0031.severity = none # IDE0031: Use null propagation
219+
dotnet_diagnostic.IDE0032.severity = none # IDE0032: Use auto property
220+
dotnet_diagnostic.IDE0034.severity = none # IDE0034: Simplify 'default' expression
221+
dotnet_diagnostic.IDE0036.severity = none # IDE0036: Order modifiers
222+
dotnet_diagnostic.IDE0037.severity = none # IDE0037: Use inferred member name
223+
dotnet_diagnostic.IDE0038.severity = none # IDE0038: Use pattern matching to avoid 'is' check followed by a cast
215224
dotnet_diagnostic.IDE0040.severity = none # IDE0040: Add accessibility modifiers
216-
dotnet_diagnostic.IDE0046.severity = suggestion # IDE0046: Convert to conditional expression
225+
dotnet_diagnostic.IDE0041.severity = none # IDE0041: Use is null check
226+
dotnet_diagnostic.IDE0044.severity = none # IDE0044: Add readonly modifier
227+
dotnet_diagnostic.IDE0045.severity = none # IDE0045: Use conditional expression for assignment
228+
dotnet_diagnostic.IDE0046.severity = none # IDE0046: Convert to conditional expression
229+
dotnet_diagnostic.IDE0047.severity = none # IDE0047: Remove unnecessary parentheses
230+
dotnet_diagnostic.IDE0048.severity = none # IDE0048: Add parentheses for clarity
231+
dotnet_diagnostic.IDE0049.severity = none # IDE0049: Use language keywords instead of framework type names for type references
232+
dotnet_diagnostic.IDE0050.severity = none # IDE0050: Convert to tuple
233+
dotnet_diagnostic.IDE0051.severity = none # IDE0051: Remove unused private members
234+
dotnet_diagnostic.IDE0052.severity = none # IDE0052: Remove unread private member
235+
dotnet_diagnostic.IDE0054.severity = none # IDE0054: Use compound assignment
236+
dotnet_diagnostic.IDE0055.severity = none # IDE0055: Formatting rules
237+
dotnet_diagnostic.IDE0056.severity = none # IDE0056: Use index operator
238+
dotnet_diagnostic.IDE0057.severity = none # IDE0057: Use range operator
239+
dotnet_diagnostic.IDE0058.severity = none # IDE0058: computed value is never used
240+
dotnet_diagnostic.IDE0059.severity = none # IDE0059: Remove unnecessary value assignment
241+
dotnet_diagnostic.IDE0060.severity = none # IDE0060: Remove unused parameter
242+
dotnet_diagnostic.IDE0061.severity = none # IDE0061: Use expression body for local functions
243+
dotnet_diagnostic.IDE0062.severity = none # IDE0062: Make local function static
217244
dotnet_diagnostic.IDE0065.severity = none # IDE0065: Misplaced using directive
218-
dotnet_diagnostic.IDE0011.severity = suggestion # IDE0011: Add braces
219-
dotnet_diagnostic.IDE0050.severity = suggestion # IDE0050: Convert to tuple
220-
dotnet_diagnostic.IDE0072.severity = suggestion # IDE0072: Add missing cases
221-
dotnet_diagnostic.IDE0054.severity = suggestion # IDE0054: Use compound assignment
222-
dotnet_diagnostic.IDE0010.severity = suggestion # IDE0010: Add missing cases
223-
dotnet_diagnostic.IDE0066.severity = suggestion # IDE0066: Convert switch statement to expression
224-
dotnet_diagnostic.IDE0007.severity = suggestion # IDE0007: Use implicit type
245+
dotnet_diagnostic.IDE0066.severity = none # IDE0066: Convert switch statement to expression
246+
dotnet_diagnostic.IDE0070.severity = none # IDE0070: Use 'System.HashCode.Combine'
247+
dotnet_diagnostic.IDE0072.severity = none # IDE0072: Add missing cases
248+
dotnet_diagnostic.IDE0075.severity = none # IDE0075: Simplify conditional expression
249+
dotnet_diagnostic.IDE0078.severity = none # IDE0078: Use pattern matching
250+
dotnet_diagnostic.IDE0079.severity = none # IDE0079: Remove unnecessary suppression
251+
dotnet_diagnostic.IDE0080.severity = none # IDE0080: Remove unnecessary suppression operator
252+
dotnet_diagnostic.IDE0081.severity = none # IDE0081: Remove ByVal
253+
dotnet_diagnostic.IDE0083.severity = none # IDE0083: Use pattern matching (not operator)
254+
dotnet_diagnostic.IDE1006.severity = none # IDE1006: Naming rule violation
255+
256+
dotnet_diagnostic.CS1998.severity = error # CS1998: Async method lacks 'await' operators and will run synchronously
257+
dotnet_diagnostic.CS8618.severity = error # CS8618: Non-nullable field is uninitialized. Consider declaring as nullable.
258+
dotnet_diagnostic.CS4014.severity = error # CS4014: Because this call is not awaited, execution of the current method continues before the call is completed
259+
260+
#

Build/linq2db.Default.props

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<Project>
22
<PropertyGroup>
3-
<Version>5.1.5</Version>
3+
<Version>5.1.6</Version>
44

55
<Authors>Svyatoslav Danyliv, Igor Tkachev, Dmitry Lukashenko, Ilya Chudin</Authors>
66
<Product>Linq to DB</Product>

Source/LinqToDB.EntityFrameworkCore/EFCoreMetadataReader.cs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -201,25 +201,25 @@ public T[] GetAttributes<T>(Type type, MemberInfo memberInfo, bool inherit = tru
201201
var fk = navigation.ForeignKey;
202202
if (!navigation.IsOnDependent)
203203
{
204-
var thisKey = string.Join(",", fk.PrincipalKey.Properties.Select(p => p.Name));
204+
var thisKey = string.Join(",", fk.PrincipalKey.Properties.Select(p => p.Name));
205205
var otherKey = string.Join(",", fk.Properties.Select(p => p.Name));
206206
associations.Add(new AssociationAttribute
207207
{
208-
ThisKey = thisKey,
209-
OtherKey = otherKey,
210-
CanBeNull = !fk.IsRequired,
208+
ThisKey = thisKey,
209+
OtherKey = otherKey,
210+
CanBeNull = !fk.IsRequiredDependent,
211211
IsBackReference = false
212212
});
213213
}
214214
else
215215
{
216-
var thisKey = string.Join(",", fk.Properties.Select(p => p.Name));
216+
var thisKey = string.Join(",", fk.Properties.Select(p => p.Name));
217217
var otherKey = string.Join(",", fk.PrincipalKey.Properties.Select(p => p.Name));
218218
associations.Add(new AssociationAttribute
219219
{
220-
ThisKey = thisKey,
221-
OtherKey = otherKey,
222-
CanBeNull = !fk.IsRequired,
220+
ThisKey = thisKey,
221+
OtherKey = otherKey,
222+
CanBeNull = !fk.IsRequired,
223223
IsBackReference = true
224224
});
225225
}

Tests/LinqToDB.EntityFrameworkCore.PostgreSQL.Tests/SampleTests/AAA.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -102,15 +102,15 @@ public static async Task Assert<T, TMock>(this Task<ActResult<T, TMock>> act, Fu
102102
public readonly struct ArrangeResult<T, TMock>
103103
where TMock : notnull
104104
{
105-
internal ArrangeResult(T @object, TMock? mock) => (Object, Mock) = (@object, mock);
105+
internal ArrangeResult(T @object, TMock mock) => (Object, Mock) = (@object, mock);
106106
internal T Object { get; }
107107
internal TMock Mock { get; }
108108
}
109109

110110
public readonly struct ActResult<T, TMock>
111111
where T: notnull
112112
{
113-
internal ActResult(T? @object, TMock mock, Exception? exception)
113+
internal ActResult(T @object, TMock mock, Exception? exception)
114114
=> (Object, Mock, Exception) = (@object, mock, exception);
115115
internal T Object { get; }
116116
internal TMock Mock { get; }

Tests/LinqToDB.EntityFrameworkCore.SqlServer.Tests/IssueTests.cs

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,25 @@
11
using System.Linq;
22
using LinqToDB.EntityFrameworkCore.BaseTests;
3-
using LinqToDB.EntityFrameworkCore.SqlServer.Tests.Models.Northwind;
3+
using LinqToDB.EntityFrameworkCore.SqlServer.Tests.Models.IssueModel;
44
using Microsoft.EntityFrameworkCore;
55
using NUnit.Framework;
6-
using NUnit.Framework.Constraints;
76

87
namespace LinqToDB.EntityFrameworkCore.SqlServer.Tests
98
{
109
[TestFixture]
1110
public class IssueTests : TestsBase
1211
{
13-
private DbContextOptions<IssueContext> _options;
12+
private DbContextOptions<IssueContext>? _options;
1413
private bool _created;
1514

1615
public IssueTests()
16+
{
17+
InitOptions();
18+
}
19+
20+
private void InitOptions()
1721
{
1822
var optionsBuilder = new DbContextOptionsBuilder<IssueContext>();
19-
//new SqlServerDbContextOptionsBuilder(optionsBuilder);
2023

2124
optionsBuilder.UseSqlServer("Server=.;Database=IssuesEFCore;Integrated Security=SSPI");
2225
optionsBuilder.UseLoggerFactory(TestUtils.LoggerFactory);
@@ -34,6 +37,7 @@ private IssueContext CreateContext()
3437
ctx.Database.EnsureCreated();
3538
_created = true;
3639
}
40+
3741
return ctx;
3842
}
3943

@@ -53,5 +57,26 @@ public void Issue73Test()
5357
AreEqual(efItems, linq2dbItems);
5458
}
5559

60+
[Test]
61+
public void Issue117Test()
62+
{
63+
using var ctx = CreateContext();
64+
65+
var userId = 1;
66+
67+
var query =
68+
from p in ctx.Patents.Include(p => p.Assessment)
69+
where p.Assessment == null || (p.Assessment.TechnicalReviewerId != userId)
70+
select new { PatentId = p.Id, UserId = userId };
71+
72+
var resultEF = query.ToArray();
73+
74+
using var db = ctx.CreateLinqToDbConnection();
75+
76+
_ = query.ToLinqToDB(db).ToArray();
77+
78+
Assert.That(db.LastQuery, Does.Not.Contain("INNER"));
79+
}
80+
5681
}
5782
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
using System.ComponentModel.DataAnnotations;
2+
3+
namespace LinqToDB.EntityFrameworkCore.SqlServer.Tests.Models.IssueModel
4+
{
5+
public class Patent
6+
{
7+
[Key]
8+
public int Id { get; set; }
9+
public PatentAssessment? Assessment { get; set; }
10+
}
11+
12+
public class PatentAssessment
13+
{
14+
[Key]
15+
public int PatentId { get; set; }
16+
public Patent Patent { get; set; } = null!;
17+
public int? TechnicalReviewerId { get; set; }
18+
}
19+
}

Tests/LinqToDB.EntityFrameworkCore.SqlServer.Tests/Models/IssueModel/IssueContext.cs

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,13 @@
1-
using System.Reflection;
2-
using LinqToDB.EntityFrameworkCore.BaseTests.Models.Northwind;
3-
using LinqToDB.EntityFrameworkCore.SqlServer.Tests.Models.IssueModel;
4-
using LinqToDB.EntityFrameworkCore.SqlServer.Tests.Models.Northwind.Mapping;
5-
using LinqToDB.Expressions;
6-
using LinqToDB.Extensions;
7-
using Microsoft.EntityFrameworkCore;
8-
9-
namespace LinqToDB.EntityFrameworkCore.SqlServer.Tests.Models.Northwind
1+
using Microsoft.EntityFrameworkCore;
2+
3+
namespace LinqToDB.EntityFrameworkCore.SqlServer.Tests.Models.IssueModel
104
{
115
public class IssueContext : DbContext
126
{
137
public DbSet<Issue73Entity> Issue73Entities { get; set; } = null!;
148

9+
public DbSet<Patent> Patents { get; set; } = null!;
10+
1511
public IssueContext(DbContextOptions options) : base(options)
1612
{
1713

@@ -43,6 +39,14 @@ protected override void OnModelCreating(ModelBuilder modelBuilder)
4339
},
4440
});
4541
});
42+
43+
modelBuilder
44+
.Entity<Patent>()
45+
.HasOne(p => p.Assessment)
46+
.WithOne(pa => pa.Patent)
47+
.HasForeignKey<PatentAssessment>(pa => pa.PatentId)
48+
.OnDelete(DeleteBehavior.Restrict);
49+
4650
}
4751
}
4852
}

Tests/LinqToDB.EntityFrameworkCore.SqlServer.Tests/QueryableExtensions.cs

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,5 +48,62 @@ public static IIncludableQueryable<TEntity, TProp> IncludeInline<TEntity, TProp,
4848

4949
return includable;
5050
}
51+
52+
public static Expression<Func<T, bool>> MakePropertiesPredicate<T, TValue>(Expression<Func<TValue, TValue, bool>> pattern, TValue searchValue, bool isOr)
53+
{
54+
var parameter = Expression.Parameter(typeof(T), "e");
55+
var searchExpr = Expression.Constant(searchValue);
56+
57+
var predicateBody = typeof(T).GetProperties()
58+
.Where(p => p.PropertyType == typeof(TValue))
59+
.Select(p =>
60+
ExpressionReplacer.GetBody(pattern, Expression.MakeMemberAccess(
61+
parameter, p), searchExpr))
62+
.Aggregate(isOr ? Expression.OrElse : Expression.AndAlso);
63+
64+
return Expression.Lambda<Func<T, bool>>(predicateBody, parameter);
65+
}
66+
67+
public static IQueryable<T> FilterByProperties<T, TValue>(this IQueryable<T> query, TValue searchValue,
68+
Expression<Func<TValue, TValue, bool>> pattern, bool isOr)
69+
{
70+
return query.Where(MakePropertiesPredicate<T, TValue>(pattern, searchValue, isOr));
71+
}
72+
73+
class ExpressionReplacer : ExpressionVisitor
74+
{
75+
readonly IDictionary<Expression, Expression> _replaceMap;
76+
77+
public ExpressionReplacer(IDictionary<Expression, Expression> replaceMap)
78+
{
79+
_replaceMap = replaceMap ?? throw new ArgumentNullException(nameof(replaceMap));
80+
}
81+
82+
public override Expression Visit(Expression node)
83+
{
84+
if (node != null && _replaceMap.TryGetValue(node, out var replacement))
85+
return replacement;
86+
return base.Visit(node);
87+
}
88+
89+
public static Expression Replace(Expression expr, Expression toReplace, Expression toExpr)
90+
{
91+
return new ExpressionReplacer(new Dictionary<Expression, Expression> { { toReplace, toExpr } }).Visit(expr);
92+
}
93+
94+
public static Expression Replace(Expression expr, IDictionary<Expression, Expression> replaceMap)
95+
{
96+
return new ExpressionReplacer(replaceMap).Visit(expr);
97+
}
98+
99+
public static Expression GetBody(LambdaExpression lambda, params Expression[] toReplace)
100+
{
101+
if (lambda.Parameters.Count != toReplace.Length)
102+
throw new InvalidOperationException();
103+
104+
return new ExpressionReplacer(Enumerable.Range(0, lambda.Parameters.Count)
105+
.ToDictionary(i => (Expression)lambda.Parameters[i], i => toReplace[i])).Visit(lambda.Body);
106+
}
107+
}
51108
}
52109
}

Tests/LinqToDB.EntityFrameworkCore.SqlServer.Tests/ToolsTests.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -814,6 +814,19 @@ public void TestCreateTempTable([Values(true, false)] bool enableFilter)
814814
}
815815

816816

817+
[Test]
818+
public void TestForeignKey([Values(true, false)] bool enableFilter)
819+
{
820+
using (var ctx = CreateContext(enableFilter))
821+
{
822+
var resultEF = ctx.Employees.Include(e => e.ReportsToNavigation).ToArray();
823+
var result = ctx.Employees.Include(e => e.ReportsToNavigation).ToLinqToDB().ToArray();
824+
825+
AreEqual(resultEF, result);
826+
}
827+
}
828+
829+
817830
[Test]
818831
public void TestCommandTimeout()
819832
{

0 commit comments

Comments
 (0)