Skip to content

Commit 4a44dc4

Browse files
author
Bart Koelman
committed
Removed non-generic Identifiable and throw for resource classes that only implement IIdentifiable (without ID) when building the resource graph
1 parent 29e5c84 commit 4a44dc4

File tree

121 files changed

+181
-179
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

121 files changed

+181
-179
lines changed

benchmarks/BenchmarkResource.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
namespace Benchmarks
66
{
77
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
8-
public sealed class BenchmarkResource : Identifiable
8+
public sealed class BenchmarkResource : Identifiable<int>
99
{
1010
[Attr(PublicName = BenchmarkResourcePublicNames.NameAttr)]
1111
public string Name { get; set; }

benchmarks/Deserialization/DeserializationBenchmarkBase.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ protected DeserializationBenchmarkBase()
5656
protected abstract JsonApiRequest CreateJsonApiRequest(IResourceGraph resourceGraph);
5757

5858
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
59-
public sealed class ResourceA : Identifiable
59+
public sealed class ResourceA : Identifiable<int>
6060
{
6161
[Attr]
6262
public bool Attribute01 { get; set; }

benchmarks/Serialization/SerializationBenchmarkBase.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ protected SerializationBenchmarkBase()
6464
protected abstract IEvaluatedIncludeCache CreateEvaluatedIncludeCache(IResourceGraph resourceGraph);
6565

6666
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
67-
public sealed class ResourceA : Identifiable
67+
public sealed class ResourceA : Identifiable<int>
6868
{
6969
[Attr]
7070
public bool Attribute01 { get; set; }

benchmarks/SubResource.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
namespace Benchmarks
66
{
77
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
8-
public sealed class SubResource : Identifiable
8+
public sealed class SubResource : Identifiable<int>
99
{
1010
[Attr]
1111
public string Value { get; set; }

src/Examples/GettingStarted/Models/Book.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
namespace GettingStarted.Models
66
{
77
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
8-
public sealed class Book : Identifiable
8+
public sealed class Book : Identifiable<int>
99
{
1010
[Attr]
1111
public string Title { get; set; }

src/Examples/GettingStarted/Models/Person.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
namespace GettingStarted.Models
77
{
88
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
9-
public sealed class Person : Identifiable
9+
public sealed class Person : Identifiable<int>
1010
{
1111
[Attr]
1212
public string Name { get; set; }

src/Examples/JsonApiDotNetCoreExample/Models/Person.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
namespace JsonApiDotNetCoreExample.Models
77
{
88
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
9-
public sealed class Person : Identifiable
9+
public sealed class Person : Identifiable<int>
1010
{
1111
[Attr]
1212
public string FirstName { get; set; }

src/Examples/JsonApiDotNetCoreExample/Models/Tag.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
namespace JsonApiDotNetCoreExample.Models
88
{
99
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
10-
public sealed class Tag : Identifiable
10+
public sealed class Tag : Identifiable<int>
1111
{
1212
[Required]
1313
[MinLength(1)]

src/Examples/JsonApiDotNetCoreExample/Models/TodoItem.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
namespace JsonApiDotNetCoreExample.Models
88
{
99
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
10-
public sealed class TodoItem : Identifiable
10+
public sealed class TodoItem : Identifiable<int>
1111
{
1212
[Attr]
1313
public string Description { get; set; }

src/Examples/MultiDbContextExample/Models/ResourceA.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
namespace MultiDbContextExample.Models
66
{
77
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
8-
public sealed class ResourceA : Identifiable
8+
public sealed class ResourceA : Identifiable<int>
99
{
1010
[Attr]
1111
public string NameA { get; set; }

src/Examples/MultiDbContextExample/Models/ResourceB.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
namespace MultiDbContextExample.Models
66
{
77
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
8-
public sealed class ResourceB : Identifiable
8+
public sealed class ResourceB : Identifiable<int>
99
{
1010
[Attr]
1111
public string NameB { get; set; }

src/Examples/NoEntityFrameworkExample/Models/WorkItem.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
namespace NoEntityFrameworkExample.Models
77
{
88
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
9-
public sealed class WorkItem : Identifiable
9+
public sealed class WorkItem : Identifiable<int>
1010
{
1111
[Attr]
1212
public bool IsBlocked { get; set; }

src/Examples/ReportsExample/Models/Report.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
namespace ReportsExample.Models
66
{
77
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
8-
public sealed class Report : Identifiable
8+
public sealed class Report : Identifiable<int>
99
{
1010
[Attr]
1111
public string Title { get; set; }

src/JsonApiDotNetCore/Configuration/JsonApiValidationFilter.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ private IServiceProvider GetScopedServiceProvider()
6464

6565
private static bool IsId(string key)
6666
{
67-
return key == nameof(Identifiable.Id) || key.EndsWith($".{nameof(Identifiable.Id)}", StringComparison.Ordinal);
67+
return key == nameof(Identifiable<object>.Id) || key.EndsWith($".{nameof(Identifiable<object>.Id)}", StringComparison.Ordinal);
6868
}
6969

7070
private static bool IsAtPrimaryEndpoint(IJsonApiRequest request)

src/JsonApiDotNetCore/Configuration/ResourceGraphBuilder.cs

+9-3
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using System.Linq;
44
using System.Reflection;
55
using JetBrains.Annotations;
6+
using JsonApiDotNetCore.Errors;
67
using JsonApiDotNetCore.Resources;
78
using JsonApiDotNetCore.Resources.Annotations;
89
using Microsoft.EntityFrameworkCore;
@@ -130,14 +131,19 @@ public ResourceGraphBuilder Add(Type resourceClrType, Type idClrType = null, str
130131
if (resourceClrType.IsOrImplementsInterface(typeof(IIdentifiable)))
131132
{
132133
string effectivePublicName = publicName ?? FormatResourceName(resourceClrType);
133-
Type effectiveIdType = idClrType ?? _typeLocator.TryGetIdType(resourceClrType) ?? typeof(int);
134+
Type effectiveIdType = idClrType ?? _typeLocator.TryGetIdType(resourceClrType);
135+
136+
if (effectiveIdType == null)
137+
{
138+
throw new InvalidConfigurationException($"Resource type '{resourceClrType}' implements 'IIdentifiable', but not 'IIdentifiable<TId>'.");
139+
}
134140

135141
ResourceType resourceType = CreateResourceType(effectivePublicName, resourceClrType, effectiveIdType);
136142
_resourceTypes.Add(resourceType);
137143
}
138144
else
139145
{
140-
_logger.LogWarning($"Entity '{resourceClrType}' does not implement '{nameof(IIdentifiable)}'.");
146+
_logger.LogWarning($"Skipping: Type '{resourceClrType}' does not implement '{nameof(IIdentifiable)}'.");
141147
}
142148

143149
return this;
@@ -168,7 +174,7 @@ private IReadOnlyCollection<AttrAttribute> GetAttributes(Type resourceClrType)
168174
// Although strictly not correct, 'id' is added to the list of attributes for convenience.
169175
// For example, it enables to filter on ID, without the need to special-case existing logic.
170176
// And when using sparse fields, it silently adds 'id' to the set of attributes to retrieve.
171-
if (property.Name == nameof(Identifiable.Id) && attribute == null)
177+
if (property.Name == nameof(Identifiable<object>.Id) && attribute == null)
172178
{
173179
var idAttr = new AttrAttribute
174180
{

src/JsonApiDotNetCore/Queries/Internal/Parsing/FilterParser.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ protected ComparisonExpression ParseComparison(string operatorName)
154154

155155
PropertyInfo leftProperty = leftChain.Fields[^1].Property;
156156

157-
if (leftProperty.Name == nameof(Identifiable.Id) && rightTerm is LiteralConstantExpression rightConstant)
157+
if (leftProperty.Name == nameof(Identifiable<object>.Id) && rightTerm is LiteralConstantExpression rightConstant)
158158
{
159159
string id = DeObfuscateStringId(leftProperty.ReflectedType, rightConstant.Value);
160160
rightTerm = new LiteralConstantExpression(id);
@@ -214,7 +214,7 @@ protected AnyExpression ParseAny()
214214

215215
PropertyInfo targetAttributeProperty = targetAttribute.Fields[^1].Property;
216216

217-
if (targetAttributeProperty.Name == nameof(Identifiable.Id))
217+
if (targetAttributeProperty.Name == nameof(Identifiable<object>.Id))
218218
{
219219
constantSet = DeObfuscateIdConstants(constantSet, targetAttributeProperty);
220220
}

src/JsonApiDotNetCore/Queries/Internal/QueryLayerComposer.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -483,7 +483,7 @@ protected virtual IDictionary<ResourceFieldAttribute, QueryLayer> GetProjectionF
483483

484484
private static AttrAttribute GetIdAttribute(ResourceType resourceType)
485485
{
486-
return resourceType.GetAttributeByPropertyName(nameof(Identifiable.Id));
486+
return resourceType.GetAttributeByPropertyName(nameof(Identifiable<object>.Id));
487487
}
488488
}
489489
}

src/JsonApiDotNetCore/Queries/Internal/SparseFieldSetCache.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ public IImmutableSet<AttrAttribute> GetIdAttributeSetForRelationshipQuery(Resour
9696
{
9797
ArgumentGuard.NotNull(resourceType, nameof(resourceType));
9898

99-
AttrAttribute idAttribute = resourceType.GetAttributeByPropertyName(nameof(Identifiable.Id));
99+
AttrAttribute idAttribute = resourceType.GetAttributeByPropertyName(nameof(Identifiable<object>.Id));
100100
var inputExpression = new SparseFieldSetExpression(ImmutableHashSet.Create<ResourceFieldAttribute>(idAttribute));
101101

102102
// Intentionally not cached, as we are fetching ID only (ignoring any sparse fieldset that came from query string).

src/JsonApiDotNetCore/QueryStrings/Internal/SparseFieldSetQueryStringParameterReader.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ private SparseFieldSetExpression GetSparseFieldSet(string parameterValue, Resour
9292
if (sparseFieldSet == null)
9393
{
9494
// We add ID on an incoming empty fieldset, so that callers can distinguish between no fieldset and an empty one.
95-
AttrAttribute idAttribute = resourceType.GetAttributeByPropertyName(nameof(Identifiable.Id));
95+
AttrAttribute idAttribute = resourceType.GetAttributeByPropertyName(nameof(Identifiable<object>.Id));
9696
return new SparseFieldSetExpression(ImmutableHashSet.Create<ResourceFieldAttribute>(idAttribute));
9797
}
9898

src/JsonApiDotNetCore/Resources/IIdentifiable.cs

+2-3
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
namespace JsonApiDotNetCore.Resources
22
{
33
/// <summary>
4-
/// When implemented by a class, indicates to JsonApiDotNetCore that the class represents a JSON:API resource. Note that JsonApiDotNetCore also assumes
5-
/// that a property named 'Id' exists.
4+
/// Defines the basic contract for a JSON:API resource. All resource classes must implement <see cref="IIdentifiable{TId}" />.
65
/// </summary>
76
public interface IIdentifiable
87
{
@@ -26,7 +25,7 @@ public interface IIdentifiable
2625
public interface IIdentifiable<TId> : IIdentifiable
2726
{
2827
/// <summary>
29-
/// The typed identifier as used by the underlying data store (usually numeric).
28+
/// The typed identifier as used by the underlying data store (usually numeric or Guid).
3029
/// </summary>
3130
TId Id { get; set; }
3231
}

src/JsonApiDotNetCore/Resources/Identifiable.cs

+2-6
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,9 @@
44

55
namespace JsonApiDotNetCore.Resources
66
{
7-
/// <inheritdoc />
8-
public abstract class Identifiable : Identifiable<int>
9-
{
10-
}
11-
127
/// <summary>
13-
/// A convenient basic implementation of <see cref="IIdentifiable" /> that provides conversion between <see cref="Id" /> and <see cref="StringId" />.
8+
/// A convenient basic implementation of <see cref="IIdentifiable{TId}" /> that provides conversion between typed <see cref="Id" /> and
9+
/// <see cref="StringId" />.
1410
/// </summary>
1511
/// <typeparam name="TId">
1612
/// The resource identifier type.

src/JsonApiDotNetCore/Resources/IdentifiableExtensions.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ public static object GetTypedId(this IIdentifiable identifiable)
99
{
1010
ArgumentGuard.NotNull(identifiable, nameof(identifiable));
1111

12-
PropertyInfo property = identifiable.GetType().GetProperty(nameof(Identifiable.Id));
12+
PropertyInfo property = identifiable.GetType().GetProperty(nameof(Identifiable<object>.Id));
1313

1414
if (property == null)
1515
{

src/JsonApiDotNetCore/Serialization/JsonConverters/ResourceObjectConverter.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ private static IDictionary<string, object> ReadAttributes(ref Utf8JsonReader rea
183183
{
184184
object attributeValue;
185185

186-
if (property.Name == nameof(Identifiable.Id))
186+
if (property.Name == nameof(Identifiable<object>.Id))
187187
{
188188
attributeValue = JsonInvalidAttributeInfo.Id;
189189
}

src/JsonApiDotNetCore/Serialization/Response/LinkBuilder.cs

+7-3
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,13 @@ public class LinkBuilder : ILinkBuilder
2424
private const string PageSizeParameterName = "page[size]";
2525
private const string PageNumberParameterName = "page[number]";
2626

27-
private static readonly string GetPrimaryControllerActionName = NoAsyncSuffix(nameof(BaseJsonApiController<Identifiable, int>.GetAsync));
28-
private static readonly string GetSecondaryControllerActionName = NoAsyncSuffix(nameof(BaseJsonApiController<Identifiable, int>.GetSecondaryAsync));
29-
private static readonly string GetRelationshipControllerActionName = NoAsyncSuffix(nameof(BaseJsonApiController<Identifiable, int>.GetRelationshipAsync));
27+
private static readonly string GetPrimaryControllerActionName = NoAsyncSuffix(nameof(BaseJsonApiController<Identifiable<int>, int>.GetAsync));
28+
29+
private static readonly string GetSecondaryControllerActionName =
30+
NoAsyncSuffix(nameof(BaseJsonApiController<Identifiable<int>, int>.GetSecondaryAsync));
31+
32+
private static readonly string GetRelationshipControllerActionName =
33+
NoAsyncSuffix(nameof(BaseJsonApiController<Identifiable<int>, int>.GetRelationshipAsync));
3034

3135
private readonly IJsonApiOptions _options;
3236
private readonly IJsonApiRequest _request;

src/JsonApiDotNetCore/Serialization/Response/ResponseModelAdapter.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,7 @@ protected virtual IDictionary<string, object> ConvertAttributes(IIdentifiable re
215215

216216
foreach (AttrAttribute attr in resourceType.Attributes)
217217
{
218-
if (!fieldSet.Contains(attr) || attr.Property.Name == nameof(Identifiable.Id))
218+
if (!fieldSet.Contains(attr) || attr.Property.Name == nameof(Identifiable<object>.Id))
219219
{
220220
continue;
221221
}

test/DiscoveryTests/TestResource.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
namespace DiscoveryTests
55
{
66
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
7-
public sealed class TestResource : Identifiable
7+
public sealed class TestResource : Identifiable<int>
88
{
99
}
1010
}

test/JsonApiDotNetCoreTests/IntegrationTests/Archiving/BroadcastComment.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
namespace JsonApiDotNetCoreTests.IntegrationTests.Archiving
77
{
88
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
9-
public sealed class BroadcastComment : Identifiable
9+
public sealed class BroadcastComment : Identifiable<int>
1010
{
1111
[Attr]
1212
public string Text { get; set; }

test/JsonApiDotNetCoreTests/IntegrationTests/Archiving/TelevisionBroadcast.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
namespace JsonApiDotNetCoreTests.IntegrationTests.Archiving
88
{
99
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
10-
public sealed class TelevisionBroadcast : Identifiable
10+
public sealed class TelevisionBroadcast : Identifiable<int>
1111
{
1212
[Attr]
1313
public string Title { get; set; }

test/JsonApiDotNetCoreTests/IntegrationTests/Archiving/TelevisionNetwork.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
namespace JsonApiDotNetCoreTests.IntegrationTests.Archiving
77
{
88
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
9-
public sealed class TelevisionNetwork : Identifiable
9+
public sealed class TelevisionNetwork : Identifiable<int>
1010
{
1111
[Attr]
1212
public string Name { get; set; }

test/JsonApiDotNetCoreTests/IntegrationTests/Archiving/TelevisionStation.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
namespace JsonApiDotNetCoreTests.IntegrationTests.Archiving
77
{
88
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
9-
public sealed class TelevisionStation : Identifiable
9+
public sealed class TelevisionStation : Identifiable<int>
1010
{
1111
[Attr]
1212
public string Name { get; set; }

test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/Performer.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
namespace JsonApiDotNetCoreTests.IntegrationTests.AtomicOperations
77
{
88
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
9-
public sealed class Performer : Identifiable
9+
public sealed class Performer : Identifiable<int>
1010
{
1111
[Attr]
1212
public string ArtistName { get; set; }

test/JsonApiDotNetCoreTests/IntegrationTests/CompositeKeys/CarExpressionRewriter.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ public override QueryExpression VisitMatchText(MatchTextExpression expression, o
7878

7979
private static bool IsCarId(PropertyInfo property)
8080
{
81-
return property.Name == nameof(Identifiable.Id) && property.DeclaringType == typeof(Car);
81+
return property.Name == nameof(Identifiable<object>.Id) && property.DeclaringType == typeof(Car);
8282
}
8383

8484
private QueryExpression RewriteFilterOnCarStringIds(ResourceFieldChainExpression existingCarIdChain, IEnumerable<string> carStringIds)

test/JsonApiDotNetCoreTests/IntegrationTests/CompositeKeys/Dealership.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
namespace JsonApiDotNetCoreTests.IntegrationTests.CompositeKeys
77
{
88
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
9-
public sealed class Dealership : Identifiable
9+
public sealed class Dealership : Identifiable<int>
1010
{
1111
[Attr]
1212
public string Address { get; set; }

test/JsonApiDotNetCoreTests/IntegrationTests/CompositeKeys/Engine.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
namespace JsonApiDotNetCoreTests.IntegrationTests.CompositeKeys
66
{
77
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
8-
public sealed class Engine : Identifiable
8+
public sealed class Engine : Identifiable<int>
99
{
1010
[Attr]
1111
public string SerialCode { get; set; }

test/JsonApiDotNetCoreTests/IntegrationTests/ContentNegotiation/Policy.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
namespace JsonApiDotNetCoreTests.IntegrationTests.ContentNegotiation
66
{
77
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
8-
public sealed class Policy : Identifiable
8+
public sealed class Policy : Identifiable<int>
99
{
1010
[Attr]
1111
public string Name { get; set; }

test/JsonApiDotNetCoreTests/IntegrationTests/ControllerActionResults/Toothbrush.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
namespace JsonApiDotNetCoreTests.IntegrationTests.ControllerActionResults
66
{
77
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
8-
public sealed class Toothbrush : Identifiable
8+
public sealed class Toothbrush : Identifiable<int>
99
{
1010
[Attr]
1111
public bool IsElectric { get; set; }

test/JsonApiDotNetCoreTests/IntegrationTests/CustomRoutes/Civilian.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
namespace JsonApiDotNetCoreTests.IntegrationTests.CustomRoutes
66
{
77
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
8-
public sealed class Civilian : Identifiable
8+
public sealed class Civilian : Identifiable<int>
99
{
1010
[Attr]
1111
public string Name { get; set; }

0 commit comments

Comments
 (0)