Skip to content

Commit a257c08

Browse files
committed
Make CollectionConverter a singleton
1 parent 55f671c commit a257c08

File tree

16 files changed

+64
-56
lines changed

16 files changed

+64
-56
lines changed

src/Examples/DapperExample/Repositories/DapperRepository.cs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,6 @@ public sealed partial class DapperRepository<TResource, TId> : IResourceReposito
103103
private readonly SqlCaptureStore _captureStore;
104104
private readonly ILoggerFactory _loggerFactory;
105105
private readonly ILogger<DapperRepository<TResource, TId>> _logger;
106-
private readonly CollectionConverter _collectionConverter = new();
107106
private readonly ParameterFormatter _parameterFormatter = new();
108107
private readonly DapperFacade _dapperFacade;
109108

@@ -270,12 +269,12 @@ private async Task ApplyTargetedFieldsAsync(TResource resourceFromRequest, TReso
270269

271270
if (relationship is HasManyAttribute hasManyRelationship)
272271
{
273-
HashSet<IIdentifiable> rightResourceIds = _collectionConverter.ExtractResources(rightValue).ToHashSet(IdentifiableComparer.Instance);
272+
HashSet<IIdentifiable> rightResourceIds = CollectionConverter.Instance.ExtractResources(rightValue).ToHashSet(IdentifiableComparer.Instance);
274273

275274
await _resourceDefinitionAccessor.OnSetToManyRelationshipAsync(leftResource, hasManyRelationship, rightResourceIds, writeOperation,
276275
cancellationToken);
277276

278-
return _collectionConverter.CopyToTypedCollection(rightResourceIds, relationship.Property.PropertyType);
277+
return CollectionConverter.Instance.CopyToTypedCollection(rightResourceIds, relationship.Property.PropertyType);
279278
}
280279

281280
return rightValue;
@@ -464,7 +463,9 @@ public async Task AddToToManyRelationshipAsync(TResource? leftResource, [Disallo
464463
leftPlaceholderResource.Id = leftId;
465464

466465
await _resourceDefinitionAccessor.OnAddToRelationshipAsync(leftPlaceholderResource, relationship, rightResourceIds, cancellationToken);
467-
relationship.SetValue(leftPlaceholderResource, _collectionConverter.CopyToTypedCollection(rightResourceIds, relationship.Property.PropertyType));
466+
467+
relationship.SetValue(leftPlaceholderResource,
468+
CollectionConverter.Instance.CopyToTypedCollection(rightResourceIds, relationship.Property.PropertyType));
468469

469470
await _resourceDefinitionAccessor.OnWritingAsync(leftPlaceholderResource, WriteOperationKind.AddToRelationship, cancellationToken);
470471

@@ -500,7 +501,7 @@ public async Task RemoveFromToManyRelationshipAsync(TResource leftResource, ISet
500501
var relationship = (HasManyAttribute)_targetedFields.Relationships.Single();
501502

502503
await _resourceDefinitionAccessor.OnRemoveFromRelationshipAsync(leftResource, relationship, rightResourceIds, cancellationToken);
503-
relationship.SetValue(leftResource, _collectionConverter.CopyToTypedCollection(rightResourceIds, relationship.Property.PropertyType));
504+
relationship.SetValue(leftResource, CollectionConverter.Instance.CopyToTypedCollection(rightResourceIds, relationship.Property.PropertyType));
504505

505506
await _resourceDefinitionAccessor.OnWritingAsync(leftResource, WriteOperationKind.RemoveFromRelationship, cancellationToken);
506507

src/Examples/DapperExample/Repositories/ResourceChangeDetector.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ namespace DapperExample.Repositories;
1212
/// </summary>
1313
internal sealed class ResourceChangeDetector
1414
{
15-
private readonly CollectionConverter _collectionConverter = new();
1615
private readonly IDataModelService _dataModelService;
1716

1817
private Dictionary<string, object?> _currentColumnValues = [];
@@ -69,7 +68,7 @@ private Dictionary<RelationshipAttribute, HashSet<IIdentifiable>> CaptureRightRe
6968
foreach (RelationshipAttribute relationship in ResourceType.Relationships)
7069
{
7170
object? rightValue = relationship.GetValue(resource);
72-
HashSet<IIdentifiable> rightResources = _collectionConverter.ExtractResources(rightValue).ToHashSet(IdentifiableComparer.Instance);
71+
HashSet<IIdentifiable> rightResources = CollectionConverter.Instance.ExtractResources(rightValue).ToHashSet(IdentifiableComparer.Instance);
7372

7473
relationshipValues[relationship] = rightResources;
7574
}

src/JsonApiDotNetCore.Annotations/CollectionConverter.cs

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,24 @@ internal sealed class CollectionConverter
1515
typeof(IEnumerable<>)
1616
];
1717

18+
public static CollectionConverter Instance { get; } = new();
19+
20+
private CollectionConverter()
21+
{
22+
}
23+
1824
/// <summary>
1925
/// Creates a collection instance based on the specified collection type and copies the specified elements into it.
2026
/// </summary>
2127
/// <param name="source">
2228
/// Source to copy from.
2329
/// </param>
2430
/// <param name="collectionType">
25-
/// Target collection type, for example: typeof(List{Article}) or typeof(ISet{Person}).
31+
/// Target collection type, for example: <code><![CDATA[
32+
/// typeof(List<Article>)
33+
/// ]]></code> or <code><![CDATA[
34+
/// typeof(ISet<Person>)
35+
/// ]]></code>.
2636
/// </param>
2737
public IEnumerable CopyToTypedCollection(IEnumerable source, Type collectionType)
2838
{
@@ -41,7 +51,12 @@ public IEnumerable CopyToTypedCollection(IEnumerable source, Type collectionType
4151
}
4252

4353
/// <summary>
44-
/// Returns a compatible collection type that can be instantiated, for example IList{Article} -> List{Article} or ISet{Article} -> HashSet{Article}
54+
/// Returns a compatible collection type that can be instantiated, for example: <code><![CDATA[
55+
/// IList<Article> -> List<Article>
56+
/// ]]></code> or
57+
/// <code><![CDATA[
58+
/// ISet<Article> -> HashSet<Article>
59+
/// ]]></code>.
4560
/// </summary>
4661
private Type ToConcreteCollectionType(Type collectionType)
4762
{
@@ -80,7 +95,12 @@ public IReadOnlyCollection<IIdentifiable> ExtractResources(object? value)
8095
}
8196

8297
/// <summary>
83-
/// Returns the element type if the specified type is a generic collection, for example: IList{string} -> string or IList -> null.
98+
/// Returns the element type if the specified type is a generic collection, for example: <code><![CDATA[
99+
/// IList<string> -> string
100+
/// ]]></code> or
101+
/// <code><![CDATA[
102+
/// IList -> null
103+
/// ]]></code>.
84104
/// </summary>
85105
public Type? FindCollectionElementType(Type? type)
86106
{
@@ -96,8 +116,12 @@ public IReadOnlyCollection<IIdentifiable> ExtractResources(object? value)
96116
}
97117

98118
/// <summary>
99-
/// Indicates whether a <see cref="HashSet{T}" /> instance can be assigned to the specified type, for example IList{Article} -> false or ISet{Article} ->
100-
/// true.
119+
/// Indicates whether a <see cref="HashSet{T}" /> instance can be assigned to the specified type, for example:
120+
/// <code><![CDATA[
121+
/// IList<Article> -> false
122+
/// ]]></code> or <code><![CDATA[
123+
/// ISet<Article> -> true
124+
/// ]]></code>.
101125
/// </summary>
102126
public bool TypeCanContainHashSet(Type collectionType)
103127
{

src/JsonApiDotNetCore.Annotations/Resources/Annotations/HasManyAttribute.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ private bool EvaluateIsManyToMany()
5959
{
6060
if (InverseNavigationProperty != null)
6161
{
62-
Type? elementType = CollectionConverter.FindCollectionElementType(InverseNavigationProperty.PropertyType);
62+
Type? elementType = CollectionConverter.Instance.FindCollectionElementType(InverseNavigationProperty.PropertyType);
6363
return elementType != null;
6464
}
6565

@@ -103,14 +103,14 @@ public void AddValue(object resource, IIdentifiable resourceToAdd)
103103
ArgumentGuard.NotNull(resourceToAdd);
104104

105105
object? rightValue = GetValue(resource);
106-
List<IIdentifiable> rightResources = CollectionConverter.ExtractResources(rightValue).ToList();
106+
List<IIdentifiable> rightResources = CollectionConverter.Instance.ExtractResources(rightValue).ToList();
107107

108108
if (!rightResources.Exists(nextResource => nextResource == resourceToAdd))
109109
{
110110
rightResources.Add(resourceToAdd);
111111

112112
Type collectionType = rightValue?.GetType() ?? Property.PropertyType;
113-
IEnumerable typedCollection = CollectionConverter.CopyToTypedCollection(rightResources, collectionType);
113+
IEnumerable typedCollection = CollectionConverter.Instance.CopyToTypedCollection(rightResources, collectionType);
114114
base.SetValue(resource, typedCollection);
115115
}
116116
}

src/JsonApiDotNetCore.Annotations/Resources/Annotations/HasOneAttribute.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ private bool EvaluateIsOneToOne()
5757
{
5858
if (InverseNavigationProperty != null)
5959
{
60-
Type? elementType = CollectionConverter.FindCollectionElementType(InverseNavigationProperty.PropertyType);
60+
Type? elementType = CollectionConverter.Instance.FindCollectionElementType(InverseNavigationProperty.PropertyType);
6161
return elementType == null;
6262
}
6363

src/JsonApiDotNetCore.Annotations/Resources/Annotations/RelationshipAttribute.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,6 @@ namespace JsonApiDotNetCore.Resources.Annotations;
1212
[PublicAPI]
1313
public abstract class RelationshipAttribute : ResourceFieldAttribute
1414
{
15-
private protected static readonly CollectionConverter CollectionConverter = new();
16-
1715
// This field is definitely assigned after building the resource graph, which is why its public equivalent is declared as non-nullable.
1816
private ResourceType? _rightType;
1917

src/JsonApiDotNetCore/AtomicOperations/Processors/SetRelationshipProcessor.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ namespace JsonApiDotNetCore.AtomicOperations.Processors;
1010
public class SetRelationshipProcessor<TResource, TId> : ISetRelationshipProcessor<TResource, TId>
1111
where TResource : class, IIdentifiable<TId>
1212
{
13-
private readonly CollectionConverter _collectionConverter = new();
1413
private readonly ISetRelationshipService<TResource, TId> _service;
1514

1615
public SetRelationshipProcessor(ISetRelationshipService<TResource, TId> service)
@@ -40,7 +39,7 @@ public SetRelationshipProcessor(ISetRelationshipService<TResource, TId> service)
4039

4140
if (relationship is HasManyAttribute)
4241
{
43-
IReadOnlyCollection<IIdentifiable> rightResources = _collectionConverter.ExtractResources(rightValue);
42+
IReadOnlyCollection<IIdentifiable> rightResources = CollectionConverter.Instance.ExtractResources(rightValue);
4443
return rightResources.ToHashSet(IdentifiableComparer.Instance);
4544
}
4645

src/JsonApiDotNetCore/Errors/InvalidModelStateException.cs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -319,8 +319,6 @@ private sealed class ArrayIndexerSegment(
319319
Func<Type, int, Type?>? getCollectionElementTypeCallback)
320320
: ModelStateKeySegment(modelType, isInComplexType, nextKey, sourcePointer, parent, getCollectionElementTypeCallback)
321321
{
322-
private static readonly CollectionConverter CollectionConverter = new();
323-
324322
public int ArrayIndex { get; } = arrayIndex;
325323

326324
public Type GetCollectionElementType()
@@ -333,7 +331,7 @@ private Type GetDeclaredCollectionElementType()
333331
{
334332
if (ModelType != typeof(string))
335333
{
336-
Type? elementType = CollectionConverter.FindCollectionElementType(ModelType);
334+
Type? elementType = CollectionConverter.Instance.FindCollectionElementType(ModelType);
337335

338336
if (elementType != null)
339337
{

src/JsonApiDotNetCore/Queries/QueryLayerComposer.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ namespace JsonApiDotNetCore.Queries;
1414
[PublicAPI]
1515
public class QueryLayerComposer : IQueryLayerComposer
1616
{
17-
private readonly CollectionConverter _collectionConverter = new();
1817
private readonly IQueryConstraintProvider[] _constraintProviders;
1918
private readonly IResourceDefinitionAccessor _resourceDefinitionAccessor;
2019
private readonly IJsonApiOptions _options;
@@ -413,7 +412,7 @@ public QueryLayer ComposeForUpdate<TId>([DisallowNull] TId id, ResourceType prim
413412
foreach (RelationshipAttribute relationship in _targetedFields.Relationships)
414413
{
415414
object? rightValue = relationship.GetValue(primaryResource);
416-
HashSet<IIdentifiable> rightResourceIds = _collectionConverter.ExtractResources(rightValue).ToHashSet(IdentifiableComparer.Instance);
415+
HashSet<IIdentifiable> rightResourceIds = CollectionConverter.Instance.ExtractResources(rightValue).ToHashSet(IdentifiableComparer.Instance);
417416

418417
if (rightResourceIds.Count > 0)
419418
{

src/JsonApiDotNetCore/Queries/QueryableBuilding/SelectClauseBuilder.cs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ public class SelectClauseBuilder : QueryClauseBuilder, ISelectClauseBuilder
1414
{
1515
private static readonly MethodInfo TypeGetTypeMethod = typeof(object).GetMethod("GetType")!;
1616
private static readonly MethodInfo TypeOpEqualityMethod = typeof(Type).GetMethod("op_Equality")!;
17-
private static readonly CollectionConverter CollectionConverter = new();
1817
private static readonly ConstantExpression NullConstant = Expression.Constant(null);
1918

2019
private readonly IResourceFactory _resourceFactory;
@@ -206,7 +205,7 @@ private MemberAssignment CreatePropertyAssignment(PropertySelector propertySelec
206205
private Expression CreateAssignmentRightHandSideForLayer(QueryLayer layer, LambdaScope outerLambdaScope, MemberExpression propertyAccess,
207206
PropertyInfo selectorPropertyInfo, QueryClauseBuilderContext context)
208207
{
209-
Type? collectionElementType = CollectionConverter.FindCollectionElementType(selectorPropertyInfo.PropertyType);
208+
Type? collectionElementType = CollectionConverter.Instance.FindCollectionElementType(selectorPropertyInfo.PropertyType);
210209
Type bodyElementType = collectionElementType ?? selectorPropertyInfo.PropertyType;
211210

212211
if (collectionElementType != null)
@@ -233,7 +232,7 @@ private static MethodCallExpression CreateCollectionInitializer(LambdaScope lamb
233232

234233
Expression layerExpression = context.QueryableBuilder.ApplyQuery(layer, nestedContext);
235234

236-
string operationName = CollectionConverter.TypeCanContainHashSet(collectionProperty.PropertyType) ? "ToHashSet" : "ToList";
235+
string operationName = CollectionConverter.Instance.TypeCanContainHashSet(collectionProperty.PropertyType) ? "ToHashSet" : "ToList";
237236
return CopyCollectionExtensionMethodCall(layerExpression, operationName, elementType);
238237
}
239238

0 commit comments

Comments
 (0)