-
Notifications
You must be signed in to change notification settings - Fork 3.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Adding model for Many to Many scenarios
Entities: One, Two, Three - regular CompositeKey - entity with composite key Root, Branch, Leaf - inheritance Navigations (all many to many have corresponding navigation on the other side that is not listed here): One - TwoFullySpecified - ThreeFullySpecifiedWithPayload - TwoSharedType - ThreeSharedType - SelfSharedTypeWithPayload - BranchFullySpecified Two - ThreeFullySpecified - SelfFullySpecified - CompositeSharedType Three - CompositeFullySpecified - RootSharedType Composite - RootSharedType - LeafSharedType For now shared type scenarios are not supported in metadata API so we added linking entities temporarily. Those should be removed once the support is in.
- Loading branch information
Showing
26 changed files
with
1,927 additions
and
0 deletions.
There are no files selected for viewing
12 changes: 12 additions & 0 deletions
12
test/EFCore.InMemory.FunctionalTests/Query/ManyToManyQueryInMemoryFixture.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
// Copyright (c) .NET Foundation. All rights reserved. | ||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. | ||
|
||
using Microsoft.EntityFrameworkCore.TestUtilities; | ||
|
||
namespace Microsoft.EntityFrameworkCore.Query | ||
{ | ||
public class ManyToManyQueryInMemoryFixture : ManyToManyQueryFixtureBase | ||
{ | ||
protected override ITestStoreFactory TestStoreFactory => InMemoryTestStoreFactory.Instance; | ||
} | ||
} |
16 changes: 16 additions & 0 deletions
16
test/EFCore.InMemory.FunctionalTests/Query/ManyToManyQueryInMemoryTest.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
// Copyright (c) .NET Foundation. All rights reserved. | ||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. | ||
|
||
using Xunit.Abstractions; | ||
|
||
namespace Microsoft.EntityFrameworkCore.Query | ||
{ | ||
public class ManyToManyQueryInMemoryTest : ManyToManyQueryTestBase<ManyToManyQueryInMemoryFixture> | ||
{ | ||
public ManyToManyQueryInMemoryTest(ManyToManyQueryInMemoryFixture fixture, ITestOutputHelper testOutputHelper) | ||
: base(fixture) | ||
{ | ||
//TestLoggerFactory.TestOutputHelper = testOutputHelper; | ||
} | ||
} | ||
} |
25 changes: 25 additions & 0 deletions
25
test/EFCore.Relational.Specification.Tests/Query/ManyToManyQueryRelationalFixture.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
// Copyright (c) .NET Foundation. All rights reserved. | ||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. | ||
|
||
using System; | ||
using System.Collections.Generic; | ||
using Microsoft.EntityFrameworkCore.TestModels.ManyToManyModel; | ||
using Microsoft.EntityFrameworkCore.TestUtilities; | ||
|
||
namespace Microsoft.EntityFrameworkCore.Query | ||
{ | ||
public abstract class ManyToManyQueryRelationalFixture : ManyToManyQueryFixtureBase | ||
{ | ||
public TestSqlLoggerFactory TestSqlLoggerFactory => (TestSqlLoggerFactory)ListLoggerFactory; | ||
|
||
protected override QueryAsserter<ManyToManyContext> CreateQueryAsserter( | ||
Dictionary<Type, object> entitySorters, | ||
Dictionary<Type, object> entityAsserters) | ||
=> new RelationalQueryAsserter<ManyToManyContext>( | ||
CreateContext, | ||
new ManyToManyData(), | ||
entitySorters, | ||
entityAsserters, | ||
CanExecuteQueryString); | ||
} | ||
} |
244 changes: 244 additions & 0 deletions
244
test/EFCore.Specification.Tests/Query/ManyToManyQueryFixtureBase.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,244 @@ | ||
// Copyright (c) .NET Foundation. All rights reserved. | ||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. | ||
|
||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using Microsoft.EntityFrameworkCore.TestModels.ManyToManyModel; | ||
using Microsoft.EntityFrameworkCore.TestUtilities; | ||
using Xunit; | ||
|
||
namespace Microsoft.EntityFrameworkCore.Query | ||
{ | ||
public abstract class ManyToManyQueryFixtureBase : SharedStoreFixtureBase<ManyToManyContext>, IQueryFixtureBase | ||
{ | ||
protected override string StoreName { get; } = "ManyToManyQueryTest"; | ||
|
||
public ManyToManyQueryFixtureBase() | ||
{ | ||
var entitySorters = new Dictionary<Type, Func<object, object>> | ||
{ | ||
{ typeof(EntityOne), e => ((EntityOne)e)?.Id }, | ||
{ typeof(EntityTwo), e => ((EntityTwo)e)?.Id }, | ||
{ typeof(EntityThree), e => ((EntityThree)e)?.Id }, | ||
}.ToDictionary(e => e.Key, e => (object)e.Value); ; | ||
|
||
var entityAsserters = new Dictionary<Type, Action<object, object>> | ||
{ | ||
{ | ||
typeof(EntityOne), (e, a) => | ||
{ | ||
Assert.Equal(e == null, a == null); | ||
|
||
if (a != null) | ||
{ | ||
var ee = (EntityOne)e; | ||
var aa = (EntityOne)a; | ||
|
||
Assert.Equal(ee.Id, aa.Id); | ||
Assert.Equal(ee.Name, aa.Name); | ||
} | ||
} | ||
}, | ||
{ | ||
typeof(EntityTwo), (e, a) => | ||
{ | ||
Assert.Equal(e == null, a == null); | ||
|
||
if (a != null) | ||
{ | ||
var ee = (EntityTwo)e; | ||
var aa = (EntityTwo)a; | ||
|
||
Assert.Equal(ee.Id, aa.Id); | ||
Assert.Equal(ee.Name, aa.Name); | ||
} | ||
} | ||
}, | ||
{ | ||
typeof(EntityThree), (e, a) => | ||
{ | ||
Assert.Equal(e == null, a == null); | ||
|
||
if (a != null) | ||
{ | ||
var ee = (EntityThree)e; | ||
var aa = (EntityThree)a; | ||
|
||
Assert.Equal(ee.Id, aa.Id); | ||
Assert.Equal(ee.Name, aa.Name); | ||
} | ||
} | ||
}, | ||
}.ToDictionary(e => e.Key, e => (object)e.Value); ; | ||
|
||
QueryAsserter = CreateQueryAsserter(entitySorters, entityAsserters); | ||
} | ||
|
||
protected virtual QueryAsserter<ManyToManyContext> CreateQueryAsserter( | ||
Dictionary<Type, object> entitySorters, | ||
Dictionary<Type, object> entityAsserters) | ||
=> new QueryAsserter<ManyToManyContext>( | ||
CreateContext, | ||
new ManyToManyData(), | ||
entitySorters, | ||
entityAsserters); | ||
|
||
public QueryAsserterBase QueryAsserter { get; set; } | ||
|
||
protected override void OnModelCreating(ModelBuilder modelBuilder, DbContext context) | ||
{ | ||
modelBuilder.Entity<EntityOne>().Property(e => e.Id).ValueGeneratedNever(); | ||
modelBuilder.Entity<EntityTwo>().Property(e => e.Id).ValueGeneratedNever(); | ||
modelBuilder.Entity<EntityThree>().Property(e => e.Id).ValueGeneratedNever(); | ||
modelBuilder.Entity<EntityCompositeKey>().HasKey(e => new { e.Key1, e.Key2, e.Key3 }); | ||
modelBuilder.Entity<EntityRoot>().Property(e => e.Id).ValueGeneratedNever(); | ||
modelBuilder.Entity<EntityBranch>().HasBaseType<EntityRoot>(); | ||
modelBuilder.Entity<EntityLeaf>().HasBaseType<EntityBranch>(); | ||
|
||
modelBuilder.Entity<EntityOne>() | ||
.HasMany(e => e.Collection) | ||
.WithOne(e => e.CollectionInverse) | ||
.HasForeignKey(e => e.CollectionInverseId); | ||
|
||
modelBuilder.Entity<EntityOne>() | ||
.HasOne(e => e.Reference) | ||
.WithOne(e => e.ReferenceInverse) | ||
.HasForeignKey<EntityTwo>(e => e.ReferenceInverseId); | ||
|
||
modelBuilder.Entity<EntityOne>() | ||
.HasMany(e => e.TwoFullySpecified) | ||
.WithMany(e => e.OneFullySpecified) | ||
.UsingEntity<OneToTwoFullySpecified>( | ||
r => r.HasOne(x => x.Two).WithMany(), | ||
l => l.HasOne(x => x.One).WithMany()) | ||
.HasKey(e => new { e.OneId, e.TwoId }); | ||
|
||
modelBuilder.Entity<EntityOne>() | ||
.HasMany(e => e.ThreeFullySpecifiedWithPayload) | ||
.WithMany(e => e.OneFullySpecifiedWithPayload) | ||
.UsingEntity<OneToThreeFullySpecifiedWithPayload>( | ||
r => r.HasOne(x => x.Three).WithMany(), | ||
l => l.HasOne(x => x.One).WithMany()) | ||
.HasKey(e => new { e.OneId, e.ThreeId }); | ||
|
||
// TODO: convert to shared type | ||
modelBuilder.Entity<EntityOne>() | ||
.HasMany(e => e.TwoSharedType) | ||
.WithMany(e => e.OneSharedType) | ||
.UsingEntity<OneToTwoSharedType>( | ||
r => r.HasOne(x => x.Two).WithMany(), | ||
l => l.HasOne(x => x.One).WithMany()) | ||
.HasKey(e => new { e.OneId, e.TwoId }); | ||
|
||
// TODO: convert to shared type | ||
modelBuilder.Entity<EntityOne>() | ||
.HasMany(e => e.ThreeSharedType) | ||
.WithMany(e => e.OneSharedType) | ||
.UsingEntity<OneToThreeSharedType>( | ||
r => r.HasOne(x => x.Three).WithMany(), | ||
l => l.HasOne(x => x.One).WithMany()) | ||
.HasKey(e => new { e.OneId, e.ThreeId }); | ||
|
||
// TODO: convert to shared type | ||
modelBuilder.Entity<EntityOne>() | ||
.HasMany(e => e.SelfSharedTypeLeftWithPayload) | ||
.WithMany(e => e.SelfSharedTypeRightWithPayload) | ||
.UsingEntity<OneSelfSharedTypeWithPayload>( | ||
r => r.HasOne(x => x.Right).WithMany().OnDelete(DeleteBehavior.NoAction), | ||
l => l.HasOne(x => x.Left).WithMany()) | ||
.HasKey(e => new { e.LeftId, e.RightId }); | ||
|
||
modelBuilder.Entity<EntityOne>() | ||
.HasMany(e => e.BranchFullySpecified) | ||
.WithMany(e => e.OneFullySpecified) | ||
.UsingEntity<OneToBranchFullySpecified>( | ||
r => r.HasOne(x => x.Branch).WithMany(), | ||
l => l.HasOne(x => x.One).WithMany()) | ||
.HasKey(e => new { e.BranchId, e.OneId }); | ||
|
||
modelBuilder.Entity<EntityTwo>() | ||
.HasOne(e => e.Reference) | ||
.WithOne(e => e.ReferenceInverse) | ||
.HasForeignKey<EntityThree>(e => e.ReferenceInverseId); | ||
|
||
modelBuilder.Entity<EntityTwo>() | ||
.HasMany(e => e.Collection) | ||
.WithOne(e => e.CollectionInverse) | ||
.HasForeignKey(e => e.CollectionInverseId); | ||
|
||
modelBuilder.Entity<EntityTwo>() | ||
.HasMany(e => e.ThreeFullySpecified) | ||
.WithMany(e => e.TwoFullySpecified) | ||
.UsingEntity<TwoToThreeFullySpecified>( | ||
r => r.HasOne(x => x.Three).WithMany(), | ||
l => l.HasOne(x => x.Two).WithMany()) | ||
.HasKey(e => new { e.TwoId, e.ThreeId }); | ||
|
||
modelBuilder.Entity<EntityTwo>() | ||
.HasMany(e => e.SelfFullySpecifiedLeft) | ||
.WithMany(e => e.SelfFullySpecifiedRight) | ||
.UsingEntity<TwoSelfFullySpecified>( | ||
r => r.HasOne(x => x.Right).WithMany().OnDelete(DeleteBehavior.NoAction), | ||
l => l.HasOne(x => x.Left).WithMany()) | ||
.HasKey(e => new { e.LeftId, e.RightId }); | ||
|
||
// TODO: convert to shared type | ||
modelBuilder.Entity<EntityTwo>() | ||
.HasMany(e => e.CompositeSharedType) | ||
.WithMany(e => e.TwoSharedType) | ||
.UsingEntity<TwoToCompositeSharedType>( | ||
r => r.HasOne(x => x.Composite).WithMany(), | ||
l => l.HasOne(x => x.Two).WithMany()) | ||
.HasKey(e => new { e.TwoId, e.CompositeId1, e.CompositeId2, e.CompositeId3 }); | ||
|
||
modelBuilder.Entity<EntityThree>() | ||
.HasMany(e => e.CompositeFullySpecified) | ||
.WithMany(e => e.ThreeFullySpecified) | ||
.UsingEntity<ThreeToCompositeFullySpecified>( | ||
l => l.HasOne(x => x.Composite).WithMany(), | ||
r => r.HasOne(x => x.Three).WithMany()) | ||
.HasKey(e => new { e.ThreeId, e.CompositeId1, e.CompositeId2, e.CompositeId3 }); | ||
|
||
// TODO: convert to shared type | ||
modelBuilder.Entity<EntityThree>() | ||
.HasMany(e => e.RootSharedType) | ||
.WithMany(e => e.ThreesSharedType) | ||
.UsingEntity<ThreeToRootSharedType>( | ||
r => r.HasOne(x => x.Root).WithMany(), | ||
l => l.HasOne(x => x.Three).WithMany()) | ||
.HasKey(e => new { e.ThreeId, e.RootId }); | ||
|
||
// TODO: convert to shared type | ||
modelBuilder.Entity<EntityCompositeKey>() | ||
.HasMany(e => e.RootSharedType) | ||
.WithMany(e => e.CompositeKeySharedType) | ||
.UsingEntity<CompositeToRootSharedType>( | ||
r => r.HasOne(x => x.Root).WithMany(), | ||
l => l.HasOne(x => x.Composite).WithMany()) | ||
.HasKey(e => new { e.CompositeId1, e.CompositeId2, e.CompositeId3, e.RootId }); | ||
|
||
modelBuilder.Entity<EntityCompositeKey>() | ||
.HasMany(e => e.LeafFullySpecified) | ||
.WithMany(e => e.CompositeKeyFullySpecified) | ||
.UsingEntity<CompositeToLeafFullySpecified>( | ||
r => r.HasOne(x => x.Leaf).WithMany(), | ||
l => l.HasOne(x => x.Composite).WithMany()) | ||
.HasKey(e => new { e.CompositeId1, e.CompositeId2, e.CompositeId3, e.LeafId }); | ||
} | ||
|
||
protected override void Seed(ManyToManyContext context) => ManyToManyContext.Seed(context); | ||
|
||
public override DbContextOptionsBuilder AddOptions(DbContextOptionsBuilder builder) | ||
=> base.AddOptions(builder); | ||
|
||
public override ManyToManyContext CreateContext() | ||
{ | ||
var context = base.CreateContext(); | ||
context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking; | ||
|
||
return context; | ||
} | ||
} | ||
} | ||
|
34 changes: 34 additions & 0 deletions
34
test/EFCore.Specification.Tests/Query/ManyToManyQueryTestBase.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
// Copyright (c) .NET Foundation. All rights reserved. | ||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. | ||
|
||
using System.Linq; | ||
using System.Threading.Tasks; | ||
using Microsoft.EntityFrameworkCore.TestModels.ManyToManyModel; | ||
using Xunit; | ||
|
||
namespace Microsoft.EntityFrameworkCore.Query | ||
{ | ||
public abstract class ManyToManyQueryTestBase<TFixture> : QueryTestBase<TFixture> | ||
where TFixture : ManyToManyQueryFixtureBase, new() | ||
{ | ||
public ManyToManyQueryTestBase(TFixture fixture) | ||
: base(fixture) | ||
{ | ||
} | ||
|
||
[ConditionalTheory] | ||
[MemberData(nameof(IsAsyncData))] | ||
public virtual Task Dummy_test_remove_later(bool async) | ||
{ | ||
return AssertQuery( | ||
async, | ||
ss => ss.Set<EntityOne>().Where(e => e.Id > 1)); | ||
} | ||
|
||
protected ManyToManyContext CreateContext() => Fixture.CreateContext(); | ||
|
||
protected virtual void ClearLog() | ||
{ | ||
} | ||
} | ||
} |
18 changes: 18 additions & 0 deletions
18
test/EFCore.Specification.Tests/TestModels/ManyToManyModel/CompositeToLeafFullySpecified.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
// Copyright (c) .NET Foundation. All rights reserved. | ||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. | ||
|
||
using System; | ||
|
||
namespace Microsoft.EntityFrameworkCore.TestModels.ManyToManyModel | ||
{ | ||
public class CompositeToLeafFullySpecified | ||
{ | ||
public int CompositeId1 { get; set; } | ||
public string CompositeId2 { get; set; } | ||
public DateTime CompositeId3 { get; set; } | ||
public int LeafId { get; set; } | ||
|
||
public EntityCompositeKey Composite { get; set; } | ||
public EntityLeaf Leaf { get; set; } | ||
} | ||
} |
13 changes: 13 additions & 0 deletions
13
test/EFCore.Specification.Tests/TestModels/ManyToManyModel/EntityBranch.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
// Copyright (c) .NET Foundation. All rights reserved. | ||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. | ||
|
||
using System.Collections.Generic; | ||
|
||
namespace Microsoft.EntityFrameworkCore.TestModels.ManyToManyModel | ||
{ | ||
public class EntityBranch : EntityRoot | ||
{ | ||
public long Number { get; set; } | ||
public List<EntityOne> OneFullySpecified { get; set; } | ||
} | ||
} |
22 changes: 22 additions & 0 deletions
22
test/EFCore.Specification.Tests/TestModels/ManyToManyModel/EntityCompositeKey.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
// Copyright (c) .NET Foundation. All rights reserved. | ||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. | ||
|
||
using System; | ||
using System.Collections.Generic; | ||
|
||
namespace Microsoft.EntityFrameworkCore.TestModels.ManyToManyModel | ||
{ | ||
public class EntityCompositeKey | ||
{ | ||
public int Key1 { get; set; } | ||
public string Key2 { get; set; } | ||
public DateTime Key3 { get; set; } | ||
|
||
public string Name { get; set; } | ||
|
||
public List<EntityTwo> TwoSharedType { get; set; } | ||
public List<EntityThree> ThreeFullySpecified { get; set; } | ||
public List<EntityRoot> RootSharedType { get; set; } | ||
public List<EntityLeaf> LeafFullySpecified { get; set; } | ||
} | ||
} |
Oops, something went wrong.