Skip to content

Commit eed2ab1

Browse files
authored
Merge pull request #1176 from json-api-dotnet/replace-ef-26779-switch
Remove dependency on EF Core "Issue26779" AppContext switch
2 parents 206ca38 + 6825c83 commit eed2ab1

File tree

6 files changed

+86
-6
lines changed

6 files changed

+86
-6
lines changed

src/JsonApiDotNetCore/Configuration/JsonApiOptions.cs

-6
Original file line numberDiff line numberDiff line change
@@ -102,12 +102,6 @@ public sealed class JsonApiOptions : IJsonApiOptions
102102
}
103103
};
104104

105-
static JsonApiOptions()
106-
{
107-
// Bug workaround for https://github.com/dotnet/efcore/issues/27436
108-
AppContext.SetSwitch("Microsoft.EntityFrameworkCore.Issue26779", true);
109-
}
110-
111105
public JsonApiOptions()
112106
{
113107
_lazySerializerReadOptions =

src/JsonApiDotNetCore/Repositories/EntityFrameworkCoreRepository.cs

+28
Original file line numberDiff line numberDiff line change
@@ -547,6 +547,34 @@ private void MarkRelationshipAsLoaded(TResource leftResource, RelationshipAttrib
547547
EntityEntry<TResource> leftEntry = _dbContext.Entry(leftResource);
548548
CollectionEntry rightCollectionEntry = leftEntry.Collection(relationship.Property.Name);
549549
rightCollectionEntry.IsLoaded = true;
550+
551+
if (rightCollectionEntry.Metadata is ISkipNavigation skipNavigation)
552+
{
553+
MarkManyToManyRelationshipAsLoaded(leftEntry, skipNavigation);
554+
}
555+
}
556+
557+
private void MarkManyToManyRelationshipAsLoaded(EntityEntry<TResource> leftEntry, ISkipNavigation skipNavigation)
558+
{
559+
string[] primaryKeyNames = skipNavigation.ForeignKey.PrincipalKey.Properties.Select(property => property.Name).ToArray();
560+
object?[] primaryKeyValues = GetCurrentKeyValues(leftEntry, primaryKeyNames);
561+
562+
string[] foreignKeyNames = skipNavigation.ForeignKey.Properties.Select(property => property.Name).ToArray();
563+
564+
foreach (EntityEntry joinEntry in _dbContext.ChangeTracker.Entries().Where(entry => entry.Metadata == skipNavigation.JoinEntityType).ToList())
565+
{
566+
object?[] foreignKeyValues = GetCurrentKeyValues(joinEntry, foreignKeyNames);
567+
568+
if (primaryKeyValues.SequenceEqual(foreignKeyValues))
569+
{
570+
joinEntry.State = EntityState.Unchanged;
571+
}
572+
}
573+
}
574+
575+
private static object?[] GetCurrentKeyValues(EntityEntry entry, IEnumerable<string> keyNames)
576+
{
577+
return keyNames.Select(keyName => entry.Property(keyName).CurrentValue).ToArray();
550578
}
551579

552580
protected async Task UpdateRelationshipAsync(RelationshipAttribute relationship, TResource leftResource, object? valueToAssign,

test/JsonApiDotNetCoreTests/IntegrationTests/CompositeKeys/Car.cs

+3
Original file line numberDiff line numberDiff line change
@@ -47,4 +47,7 @@ public override string? Id
4747

4848
[HasOne]
4949
public Dealership? Dealership { get; set; }
50+
51+
[HasMany]
52+
public ISet<Dealership> PreviousDealerships { get; set; } = new HashSet<Dealership>();
5053
}

test/JsonApiDotNetCoreTests/IntegrationTests/CompositeKeys/CompositeDbContext.cs

+4
Original file line numberDiff line numberDiff line change
@@ -34,5 +34,9 @@ protected override void OnModelCreating(ModelBuilder builder)
3434
builder.Entity<Dealership>()
3535
.HasMany(dealership => dealership.Inventory)
3636
.WithOne(car => car.Dealership!);
37+
38+
builder.Entity<Car>()
39+
.HasMany(car => car.PreviousDealerships)
40+
.WithMany(dealership => dealership.SoldCars);
3741
}
3842
}

test/JsonApiDotNetCoreTests/IntegrationTests/CompositeKeys/CompositeKeyTests.cs

+48
Original file line numberDiff line numberDiff line change
@@ -508,4 +508,52 @@ await _testContext.RunOnDatabaseAsync(async dbContext =>
508508
carInDatabase.Should().BeNull();
509509
});
510510
}
511+
512+
[Fact]
513+
public async Task Can_remove_from_ManyToMany_relationship()
514+
{
515+
// Arrange
516+
Dealership existingDealership = _fakers.Dealership.Generate();
517+
existingDealership.SoldCars = _fakers.Car.Generate(2).ToHashSet();
518+
519+
await _testContext.RunOnDatabaseAsync(async dbContext =>
520+
{
521+
await dbContext.ClearTableAsync<Car>();
522+
dbContext.Dealerships.Add(existingDealership);
523+
await dbContext.SaveChangesAsync();
524+
});
525+
526+
var requestBody = new
527+
{
528+
data = new[]
529+
{
530+
new
531+
{
532+
type = "cars",
533+
id = existingDealership.SoldCars.ElementAt(1).StringId
534+
}
535+
}
536+
};
537+
538+
string route = $"/dealerships/{existingDealership.StringId}/relationships/soldCars";
539+
540+
// Act
541+
(HttpResponseMessage httpResponse, string responseDocument) = await _testContext.ExecuteDeleteAsync<string>(route, requestBody);
542+
543+
// Assert
544+
httpResponse.ShouldHaveStatusCode(HttpStatusCode.NoContent);
545+
546+
responseDocument.Should().BeEmpty();
547+
548+
await _testContext.RunOnDatabaseAsync(async dbContext =>
549+
{
550+
Dealership dealershipInDatabase = await dbContext.Dealerships.Include(dealership => dealership.SoldCars).FirstWithIdAsync(existingDealership.Id);
551+
552+
dealershipInDatabase.SoldCars.ShouldHaveCount(1);
553+
dealershipInDatabase.SoldCars.Single().Id.Should().Be(existingDealership.SoldCars.ElementAt(0).Id);
554+
555+
List<Car> carsInDatabase = await dbContext.Cars.ToListAsync();
556+
carsInDatabase.ShouldHaveCount(2);
557+
});
558+
}
511559
}

test/JsonApiDotNetCoreTests/IntegrationTests/CompositeKeys/Dealership.cs

+3
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,7 @@ public sealed class Dealership : Identifiable<int>
1313

1414
[HasMany]
1515
public ISet<Car> Inventory { get; set; } = new HashSet<Car>();
16+
17+
[HasMany]
18+
public ISet<Car> SoldCars { get; set; } = new HashSet<Car>();
1619
}

0 commit comments

Comments
 (0)