Skip to content

Commit 9283c72

Browse files
author
Bart Koelman
committed
Bug repro
1 parent d033166 commit 9283c72

File tree

13 files changed

+461
-1
lines changed

13 files changed

+461
-1
lines changed

src/JsonApiDotNetCore/Queries/Internal/QueryableBuilding/SelectClauseBuilder.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,13 @@ private ICollection<PropertySelector> ToPropertySelectors(IDictionary<ResourceFi
133133
foreach (EagerLoadAttribute eagerLoad in resourceContext.EagerLoads)
134134
{
135135
var propertySelector = new PropertySelector(eagerLoad.Property);
136-
propertySelectors[propertySelector.Property] = propertySelector;
136+
137+
// When an entity navigation property is decorated with both EagerLoadAttribute and RelationshipAttribute,
138+
// it may already exist with a sub-layer. So do not overwrite in that case.
139+
if (!propertySelectors.ContainsKey(propertySelector.Property))
140+
{
141+
propertySelectors[propertySelector.Property] = propertySelector;
142+
}
137143
}
138144

139145
return propertySelectors.Values;
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
using System;
2+
using JetBrains.Annotations;
3+
using JsonApiDotNetCore.Resources;
4+
using JsonApiDotNetCore.Resources.Annotations;
5+
6+
namespace JsonApiDotNetCoreExampleTests.IntegrationTests.Issue988
7+
{
8+
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
9+
public sealed class DocumentType : Identifiable<Guid>
10+
{
11+
[Attr]
12+
public string Description { get; set; }
13+
}
14+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
using System;
2+
using JsonApiDotNetCore.Configuration;
3+
using JsonApiDotNetCore.Controllers;
4+
using JsonApiDotNetCore.Services;
5+
using Microsoft.Extensions.Logging;
6+
7+
namespace JsonApiDotNetCoreExampleTests.IntegrationTests.Issue988
8+
{
9+
public sealed class DocumentTypesController : JsonApiController<DocumentType, Guid>
10+
{
11+
public DocumentTypesController(IJsonApiOptions options, ILoggerFactory loggerFactory, IResourceService<DocumentType, Guid> resourceService)
12+
: base(options, loggerFactory, resourceService)
13+
{
14+
}
15+
}
16+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.ComponentModel.DataAnnotations.Schema;
4+
using System.Linq;
5+
using JetBrains.Annotations;
6+
using JsonApiDotNetCore.Resources.Annotations;
7+
8+
namespace JsonApiDotNetCoreExampleTests.IntegrationTests.Issue988
9+
{
10+
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
11+
public sealed class Engagement : EntityBase<Guid>
12+
{
13+
// Data
14+
15+
[Attr]
16+
public string Name { get; set; }
17+
18+
// Navigation Properties
19+
20+
[HasMany]
21+
public ICollection<DocumentType> DocumentTypes { get; set; } //= new List<DocumentType>();
22+
23+
[HasMany]
24+
[EagerLoad]
25+
public ICollection<EngagementParty> Parties { get; set; } //= new List<EngagementParty>();
26+
27+
[HasMany]
28+
[NotMapped]
29+
public ICollection<EngagementParty> FirstParties =>
30+
Parties.Where(party => party.Role == ModelConstants.FirstPartyRoleName).OrderBy(party => party.ShortName).ToList();
31+
32+
[HasMany]
33+
[NotMapped]
34+
public ICollection<EngagementParty> SecondParties =>
35+
Parties.Where(party => party.Role == ModelConstants.SecondPartyRoleName).OrderBy(party => party.ShortName).ToList();
36+
}
37+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
using System;
2+
using JsonApiDotNetCore.Configuration;
3+
using JsonApiDotNetCore.Controllers;
4+
using JsonApiDotNetCore.Services;
5+
using Microsoft.Extensions.Logging;
6+
7+
namespace JsonApiDotNetCoreExampleTests.IntegrationTests.Issue988
8+
{
9+
public sealed class EngagementPartiesController : JsonApiController<EngagementParty, Guid>
10+
{
11+
public EngagementPartiesController(IJsonApiOptions options, ILoggerFactory loggerFactory, IResourceService<EngagementParty, Guid> resourceService)
12+
: base(options, loggerFactory, resourceService)
13+
{
14+
}
15+
}
16+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
using System;
2+
using JetBrains.Annotations;
3+
using JsonApiDotNetCore.Resources.Annotations;
4+
5+
namespace JsonApiDotNetCoreExampleTests.IntegrationTests.Issue988
6+
{
7+
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
8+
public sealed class EngagementParty : EntityBase<Guid>
9+
{
10+
// Data (simplified)
11+
12+
[Attr]
13+
public string Role { get; set; }
14+
15+
[Attr]
16+
public string ShortName { get; set; }
17+
18+
// Foreign Keys (simplified)
19+
20+
[HasOne]
21+
public Engagement Engagement { get; set; }
22+
23+
//public Guid EngagementId { get; set; }
24+
}
25+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
using System;
2+
using System.ComponentModel;
3+
using JetBrains.Annotations;
4+
using JsonApiDotNetCore.Configuration;
5+
using JsonApiDotNetCore.Queries.Expressions;
6+
using JsonApiDotNetCore.Resources;
7+
8+
namespace JsonApiDotNetCoreExampleTests.IntegrationTests.Issue988
9+
{
10+
[UsedImplicitly(ImplicitUseKindFlags.InstantiatedNoFixedConstructorSignature)]
11+
public sealed class EngagementPartyResourceDefinition : JsonApiResourceDefinition<EngagementParty, Guid>
12+
{
13+
/// <inheritdoc />
14+
public EngagementPartyResourceDefinition(IResourceGraph resourceGraph)
15+
: base(resourceGraph)
16+
{
17+
}
18+
19+
/// <inheritdoc />
20+
public override SortExpression OnApplySort(SortExpression? existingSort)
21+
{
22+
if (existingSort != null)
23+
{
24+
return existingSort;
25+
}
26+
27+
return CreateSortExpressionFromLambda(new PropertySortOrder
28+
{
29+
(ep => ep.Role, ListSortDirection.Ascending),
30+
(ep => ep.ShortName, ListSortDirection.Ascending)
31+
});
32+
}
33+
}
34+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
using System;
2+
using JsonApiDotNetCore.Configuration;
3+
using JsonApiDotNetCore.Controllers;
4+
using JsonApiDotNetCore.Services;
5+
using Microsoft.Extensions.Logging;
6+
7+
namespace JsonApiDotNetCoreExampleTests.IntegrationTests.Issue988
8+
{
9+
public sealed class EngagementsController : JsonApiController<Engagement, Guid>
10+
{
11+
public EngagementsController(IJsonApiOptions options, ILoggerFactory loggerFactory, IResourceService<Engagement, Guid> resourceService)
12+
: base(options, loggerFactory, resourceService)
13+
{
14+
}
15+
}
16+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
using System;
2+
using System.ComponentModel.DataAnnotations;
3+
using JetBrains.Annotations;
4+
using JsonApiDotNetCore.Resources;
5+
6+
namespace JsonApiDotNetCoreExampleTests.IntegrationTests.Issue988
7+
{
8+
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
9+
public abstract class EntityBase<TId> : Identifiable<TId>
10+
{
11+
public DateTimeOffset? DateCreated { get; set; }
12+
13+
public DateTimeOffset? DateModified { get; set; }
14+
15+
[Timestamp]
16+
public byte[] RowVersion { get; set; }
17+
}
18+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
using JetBrains.Annotations;
2+
using Microsoft.EntityFrameworkCore;
3+
4+
namespace JsonApiDotNetCoreExampleTests.IntegrationTests.Issue988
5+
{
6+
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
7+
public sealed class IssueDbContext : DbContext
8+
{
9+
public DbSet<Engagement> Engagements { get; set; }
10+
public DbSet<EngagementParty> EngagementParties { get; set; }
11+
public DbSet<DocumentType> DocumentTypes { get; set; }
12+
13+
public IssueDbContext(DbContextOptions<IssueDbContext> options)
14+
: base(options)
15+
{
16+
}
17+
}
18+
}

0 commit comments

Comments
 (0)