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

Commit 16be67a

Browse files
committed
Fixed navigation properties.
1 parent c0752fa commit 16be67a

File tree

6 files changed

+159
-34
lines changed

6 files changed

+159
-34
lines changed

Source/LinqToDB.EntityFrameworkCore/EFCoreMetadataReader.cs

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -189,16 +189,30 @@ public T[] GetAttributes<T>(Type type, MemberInfo memberInfo, bool inherit = tru
189189
foreach (var navigation in navigations)
190190
{
191191
var fk = navigation.ForeignKey;
192-
193-
var thisKey = string.Join(",", fk.Properties.Select(p => p.Name));
194-
var otherKey = string.Join(",", fk.PrincipalKey.Properties.Select(p => p.Name));
195-
associations.Add(new AssociationAttribute
192+
if (!navigation.IsDependentToPrincipal())
196193
{
197-
ThisKey = thisKey,
198-
OtherKey = otherKey,
199-
CanBeNull = !fk.IsRequired,
200-
IsBackReference = fk.PrincipalEntityType != et
201-
});
194+
var thisKey = string.Join(",", fk.PrincipalKey.Properties.Select(p => p.Name));
195+
var otherKey = string.Join(",", fk.Properties.Select(p => p.Name));
196+
associations.Add(new AssociationAttribute
197+
{
198+
ThisKey = thisKey,
199+
OtherKey = otherKey,
200+
CanBeNull = !fk.IsRequired,
201+
IsBackReference = false
202+
});
203+
}
204+
else
205+
{
206+
var thisKey = string.Join(",", fk.Properties.Select(p => p.Name));
207+
var otherKey = string.Join(",", fk.PrincipalKey.Properties.Select(p => p.Name));
208+
associations.Add(new AssociationAttribute
209+
{
210+
ThisKey = thisKey,
211+
OtherKey = otherKey,
212+
CanBeNull = !fk.IsRequired,
213+
IsBackReference = true
214+
});
215+
}
202216
}
203217

204218
return associations.Select(a => (T)(Attribute)a).ToArray();

Source/LinqToDB.EntityFrameworkCore/LinqToDBForEFToolsImplDefault.cs

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -502,13 +502,9 @@ public virtual IDbContextOptions GetContextOptions(DbContext context)
502502

503503
static readonly MethodInfo GetTableMethodInfo = MemberHelper.MethodOfGeneric<IDataContext>(dc => dc.GetTable<object>());
504504

505-
static readonly MethodInfo EnumerableWhereMethodInfo = MemberHelper.MethodOfGeneric<IEnumerable<object>>(q => q.Where(p => true));
506-
507505
static readonly MethodInfo FromSqlOnQueryableMethodInfo =
508506
typeof(RelationalQueryableExtensions).GetMethods(BindingFlags.Static|BindingFlags.NonPublic).Single(x => x.Name == "FromSqlOnQueryable").GetGenericMethodDefinition();
509507

510-
static readonly MethodInfo EnumerableToListMethodInfo = MemberHelper.MethodOfGeneric<IEnumerable<object>>(q => q.ToList());
511-
512508
static readonly MethodInfo IgnoreQueryFiltersMethodInfo = MemberHelper.MethodOfGeneric<IQueryable<object>>(q => q.IgnoreQueryFilters());
513509

514510
static readonly MethodInfo IncludeMethodInfo = MemberHelper.MethodOfGeneric<IQueryable<object>>(q => q.Include(o => o.ToString()));
@@ -714,17 +710,29 @@ Expression LocalTransform(Expression e)
714710
{
715711
case ExpressionType.Constant:
716712
{
717-
if (typeof(EntityQueryable<>).IsSameOrParentOf(e.Type))
713+
if (typeof(EntityQueryable<>).IsSameOrParentOf(e.Type) || typeof(DbSet<>).IsSameOrParentOf(e.Type))
718714
{
719715
var entityType = e.Type.GenericTypeArguments[0];
720716
var newExpr = Expression.Call(null, Methods.LinqToDB.GetTable.MakeGenericMethod(entityType), Expression.Constant(dc));
721-
722717
return newExpr;
723718
}
724719

725720
break;
726721
}
727722

723+
case ExpressionType.MemberAccess:
724+
{
725+
if (typeof(DbSet<>).IsSameOrParentOf(e.Type))
726+
{
727+
var ma = (MemberExpression)e;
728+
var query = (IQueryable)EvaluateExpression(ma);
729+
730+
return LocalTransform(query.Expression);
731+
}
732+
733+
break;
734+
}
735+
728736
case ExpressionType.Call:
729737
{
730738
var methodCall = (MethodCallExpression) e;
@@ -998,7 +1006,7 @@ private Expression AddFilter(Dictionary<Type, Expression> cachedFilters, Type en
9981006
}
9991007
else if (typeof(IEnumerable<>).MakeGenericType(entityType).IsSameOrParentOf(expression.Type))
10001008
{
1001-
expression = Expression.Call(EnumerableWhereMethodInfo.MakeGenericMethod(entityType), expression,
1009+
expression = Expression.Call(Methods.Enumerable.Where.MakeGenericMethod(entityType), expression,
10021010
filterExpression);
10031011
}
10041012

@@ -1068,7 +1076,7 @@ private Expression ApplyQueryFiltersTODO(Expression expression, HashSet<Expressi
10681076
if (typeof(ICollection<>).IsSameOrParentOf(e.Type))
10691077
{
10701078
filtered = Expression.Call(
1071-
EnumerableToListMethodInfo.MakeGenericMethod(navigationType), filtered);
1079+
Methods.Enumerable.ToList.MakeGenericMethod(navigationType), filtered);
10721080
}
10731081
else if (e.Type.IsArray)
10741082
{

Tests/LinqToDB.EntityFrameworkCore.BaseTests/LinqToDB.EntityFrameworkCore.BaseTests.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
</ItemGroup>
1212

1313
<ItemGroup>
14+
<PackageReference Include="linq2db.Tools" Version="3.1.6" />
1415
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="3.1.10" />
1516
<PackageReference Include="Microsoft.Extensions.Logging" Version="3.1.10" />
1617
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="3.1.10" />

Tests/LinqToDB.EntityFrameworkCore.BaseTests/TestsBase.cs

Lines changed: 79 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,57 +3,103 @@
33
using System.Diagnostics;
44
using System.Linq;
55
using System.Text;
6+
using LinqToDB.Reflection;
7+
using LinqToDB.Tools;
8+
using LinqToDB.Tools.Comparers;
69
using NUnit.Framework;
710

811
namespace LinqToDB.EntityFrameworkCore.BaseTests
912
{
1013
public class TestsBase
1114
{
12-
protected void AreEqual<T>(IEnumerable<T> expected, IEnumerable<T> result)
15+
protected void AreEqual<T>(IEnumerable<T> expected, IEnumerable<T> result, bool allowEmpty = false)
1316
{
14-
AreEqual(t => t, expected, result, EqualityComparer<T>.Default);
17+
AreEqual(t => t, expected, result, EqualityComparer<T>.Default, allowEmpty);
18+
}
19+
20+
protected void AreEqual<T>(IEnumerable<T> expected, IEnumerable<T> result, Func<IEnumerable<T>, IEnumerable<T>> sort)
21+
{
22+
AreEqual(t => t, expected, result, EqualityComparer<T>.Default, sort);
23+
}
24+
25+
protected void AreEqualWithComparer<T>(IEnumerable<T> expected, IEnumerable<T> result)
26+
{
27+
AreEqual(t => t, expected, result, ComparerBuilder.GetEqualityComparer<T>());
28+
}
29+
30+
protected void AreEqualWithComparer<T>(IEnumerable<T> expected, IEnumerable<T> result, Func<MemberAccessor, bool> memberPredicate)
31+
{
32+
AreEqual(t => t, expected, result, ComparerBuilder.GetEqualityComparer<T>(memberPredicate));
1533
}
1634

1735
protected void AreEqual<T>(IEnumerable<T> expected, IEnumerable<T> result, IEqualityComparer<T> comparer)
1836
{
1937
AreEqual(t => t, expected, result, comparer);
2038
}
2139

22-
protected void AreEqual<T>(Func<T,T> fixSelector, IEnumerable<T> expected, IEnumerable<T> result)
40+
protected void AreEqual<T>(IEnumerable<T> expected, IEnumerable<T> result, IEqualityComparer<T> comparer, Func<IEnumerable<T>, IEnumerable<T>> sort)
41+
{
42+
AreEqual(t => t, expected, result, comparer, sort);
43+
}
44+
45+
protected void AreEqual<T>(Func<T, T> fixSelector, IEnumerable<T> expected, IEnumerable<T> result)
2346
{
2447
AreEqual(fixSelector, expected, result, EqualityComparer<T>.Default);
2548
}
2649

27-
protected void AreEqual<T>(Func<T,T> fixSelector, IEnumerable<T> expected, IEnumerable<T> result, IEqualityComparer<T> comparer)
50+
protected void AreEqual<T>(Func<T, T> fixSelector, IEnumerable<T> expected, IEnumerable<T> result, IEqualityComparer<T> comparer, bool allowEmpty = false)
2851
{
29-
var resultList = result. Select(fixSelector).ToList();
52+
AreEqual<T>(fixSelector, expected, result, comparer, null, allowEmpty);
53+
}
54+
55+
protected void AreEqual<T>(
56+
Func<T, T> fixSelector,
57+
IEnumerable<T> expected,
58+
IEnumerable<T> result,
59+
IEqualityComparer<T> comparer,
60+
Func<IEnumerable<T>, IEnumerable<T>>? sort,
61+
bool allowEmpty = false)
62+
{
63+
var resultList = result.Select(fixSelector).ToList();
3064
var expectedList = expected.Select(fixSelector).ToList();
3165

32-
Assert.AreNotEqual(0, expectedList.Count, "Expected list cannot be empty.");
66+
if (sort != null)
67+
{
68+
resultList = sort(resultList).ToList();
69+
expectedList = sort(expectedList).ToList();
70+
}
71+
72+
if (!allowEmpty)
73+
Assert.AreNotEqual(0, expectedList.Count, "Expected list cannot be empty.");
3374
Assert.AreEqual(expectedList.Count, resultList.Count, "Expected and result lists are different. Length: ");
3475

35-
var exceptExpectedList = resultList. Except(expectedList, comparer).ToList();
36-
var exceptResultList = expectedList.Except(resultList, comparer).ToList();
76+
var exceptExpectedList = resultList.Except(expectedList, comparer).ToList();
77+
var exceptResultList = expectedList.Except(resultList, comparer).ToList();
3778

3879
var exceptExpected = exceptExpectedList.Count;
39-
var exceptResult = exceptResultList. Count;
40-
var message = new StringBuilder();
80+
var exceptResult = exceptResultList.Count;
81+
var message = new StringBuilder();
4182

4283
if (exceptResult != 0 || exceptExpected != 0)
84+
{
85+
Debug.WriteLine(resultList.ToDiagnosticString());
86+
Debug.WriteLine(expectedList.ToDiagnosticString());
87+
4388
for (var i = 0; i < resultList.Count; i++)
4489
{
45-
Debug. WriteLine ("{0} {1} --- {2}", comparer.Equals(expectedList[i], resultList[i]) ? " " : "-", expectedList[i], resultList[i]);
90+
Debug.WriteLine("{0} {1} --- {2}", comparer.Equals(expectedList[i], resultList[i]) ? " " : "-", expectedList[i], resultList[i]);
4691
message.AppendFormat("{0} {1} --- {2}", comparer.Equals(expectedList[i], resultList[i]) ? " " : "-", expectedList[i], resultList[i]);
47-
message.AppendLine ();
92+
message.AppendLine();
4893
}
94+
}
4995

5096
Assert.AreEqual(0, exceptExpected, $"Expected Was{Environment.NewLine}{message}");
51-
Assert.AreEqual(0, exceptResult, $"Expect Result{Environment.NewLine}{message}");
97+
Assert.AreEqual(0, exceptResult, $"Expect Result{Environment.NewLine}{message}");
5298
}
5399

54100
protected void AreEqual<T>(IEnumerable<IEnumerable<T>> expected, IEnumerable<IEnumerable<T>> result)
55101
{
56-
var resultList = result. ToList();
102+
var resultList = result.ToList();
57103
var expectedList = expected.ToList();
58104

59105
Assert.AreNotEqual(0, expectedList.Count);
@@ -62,12 +108,29 @@ protected void AreEqual<T>(IEnumerable<IEnumerable<T>> expected, IEnumerable<IEn
62108
for (var i = 0; i < resultList.Count; i++)
63109
{
64110
var elist = expectedList[i].ToList();
65-
var rlist = resultList [i].ToList();
111+
var rlist = resultList[i].ToList();
66112

67113
if (elist.Count > 0 || rlist.Count > 0)
68114
AreEqual(elist, rlist);
69115
}
70116
}
71-
117+
118+
protected void AreSame<T>(IEnumerable<T> expected, IEnumerable<T> result)
119+
{
120+
var resultList = result.ToList();
121+
var expectedList = expected.ToList();
122+
123+
Assert.AreNotEqual(0, expectedList.Count);
124+
Assert.AreEqual(expectedList.Count, resultList.Count);
125+
126+
var b = expectedList.SequenceEqual(resultList);
127+
128+
if (!b)
129+
for (var i = 0; i < resultList.Count; i++)
130+
Debug.WriteLine("{0} {1} --- {2}", Equals(expectedList[i], resultList[i]) ? " " : "-", expectedList[i], resultList[i]);
131+
132+
Assert.IsTrue(b);
133+
}
134+
72135
}
73136
}

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

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,6 @@ public void TestIncludeDetails([Values] bool l2db, [Values] bool tracking)
7676
.Assert(e => e.First().Details.First().Details.Count().Should().Be(2));
7777

7878
[Test]
79-
[Ignore("Association bug")]
8079
public void TestManyToManyIncludeTrackerPoison([Values] bool l2db)
8180
=> _efContext
8281
.Arrange(c => InsertDefaults(CreateLinqToDbContext(c)))

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

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,24 @@ public void TestView([Values(true, false)] bool enableFilter)
273273

274274
[Test]
275275
public void TestTransformation([Values(true, false)] bool enableFilter)
276+
{
277+
using (var ctx = CreateContext(enableFilter))
278+
{
279+
var query =
280+
from p in ctx.Products
281+
from c in ctx.Categories.InnerJoin(c => c.CategoryId == p.CategoryId)
282+
select new
283+
{
284+
Product = p,
285+
Ctegory = c
286+
};
287+
288+
var items = query.ToLinqToDB().ToArray();
289+
}
290+
}
291+
292+
[Test]
293+
public void TestTransformationTable([Values(true, false)] bool enableFilter)
276294
{
277295
using (var ctx = CreateContext(enableFilter))
278296
{
@@ -623,6 +641,28 @@ public async Task TestChangeTrackerDisabled2([Values(true, false)] bool enableFi
623641
}
624642
}
625643

644+
[Test]
645+
public void NavigationProperties()
646+
{
647+
using (var ctx = CreateContext(false))
648+
{
649+
var query =
650+
from o in ctx.Orders
651+
from od in o.OrderDetails
652+
select new
653+
{
654+
ProductOrderDetails = od.Product.OrderDetails.Select(d => new {d.OrderId, d.ProductId, d.Quantity }).ToArray(),
655+
OrderDetail = new { od.OrderId, od.ProductId, od.Quantity },
656+
Product = new { od.Product.ProductId, od.Product.ProductName }
657+
};
658+
659+
var efResult = query.ToArray();
660+
var l2dbResult = query.ToLinqToDB().ToArray();
661+
662+
AreEqualWithComparer(efResult, l2dbResult);
663+
}
664+
}
665+
626666
[Test]
627667
public async Task TestSetUpdate([Values(true, false)] bool enableFilter)
628668
{

0 commit comments

Comments
 (0)