-
-
Notifications
You must be signed in to change notification settings - Fork 749
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Forked Feature Collection from ASP.NET Core (#7067)
- Loading branch information
1 parent
1c80423
commit 723733e
Showing
13 changed files
with
767 additions
and
0 deletions.
There are no files selected for viewing
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
161 changes: 161 additions & 0 deletions
161
src/HotChocolate/Core/src/Features/FeatureCollection.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,161 @@ | ||
// This code was originally forked of https://github.com/dotnet/aspnetcore/tree/c7aae8ff34dce81132d0fb3a976349dcc01ff903/src/Extensions/Features/src | ||
|
||
// ReSharper disable NonAtomicCompoundOperator | ||
using System.Collections; | ||
|
||
namespace HotChocolate.Features; | ||
|
||
/// <summary> | ||
/// Default implementation for <see cref="IFeatureCollection"/>. | ||
/// </summary> | ||
public class FeatureCollection : IFeatureCollection | ||
{ | ||
private static readonly KeyComparer _featureKeyComparer = new(); | ||
private readonly IFeatureCollection? _defaults; | ||
private readonly int _initialCapacity; | ||
private Dictionary<Type, object>? _features; | ||
private volatile int _containerRevision; | ||
|
||
/// <summary> | ||
/// Initializes a new instance of <see cref="FeatureCollection"/>. | ||
/// </summary> | ||
public FeatureCollection() | ||
{ | ||
} | ||
|
||
/// <summary> | ||
/// Initializes a new instance of <see cref="FeatureCollection"/> with the specified initial capacity. | ||
/// </summary> | ||
/// <param name="initialCapacity"> | ||
/// The initial number of elements that the collection can contain. | ||
/// </param> | ||
/// <exception cref="System.ArgumentOutOfRangeException"> | ||
/// <paramref name="initialCapacity"/> is less than 0 | ||
/// </exception> | ||
public FeatureCollection(int initialCapacity) | ||
{ | ||
if (initialCapacity < 0) | ||
{ | ||
throw new ArgumentOutOfRangeException(nameof(initialCapacity)); | ||
} | ||
|
||
_initialCapacity = initialCapacity; | ||
} | ||
|
||
/// <summary> | ||
/// Initializes a new instance of <see cref="FeatureCollection"/> with the specified defaults. | ||
/// </summary> | ||
/// <param name="defaults"> | ||
/// The feature defaults. | ||
/// </param> | ||
public FeatureCollection(IFeatureCollection defaults) | ||
{ | ||
_defaults = defaults; | ||
} | ||
|
||
/// <inheritdoc /> | ||
public virtual int Revision | ||
{ | ||
get { return _containerRevision + (_defaults?.Revision ?? 0); } | ||
} | ||
|
||
/// <inheritdoc /> | ||
public bool IsReadOnly { get { return false; } } | ||
|
||
/// <inheritdoc /> | ||
public object? this[Type key] | ||
{ | ||
get | ||
{ | ||
if (key is null) | ||
{ | ||
throw new ArgumentNullException(nameof(key)); | ||
} | ||
|
||
return _features != null && _features.TryGetValue(key, out var result) ? result : _defaults?[key]; | ||
} | ||
set | ||
{ | ||
if (key is null) | ||
{ | ||
throw new ArgumentNullException(nameof(key)); | ||
} | ||
|
||
if (value == null) | ||
{ | ||
if (_features != null && _features.Remove(key)) | ||
{ | ||
_containerRevision++; | ||
} | ||
return; | ||
} | ||
|
||
if (_features == null) | ||
{ | ||
_features = new Dictionary<Type, object>(_initialCapacity); | ||
} | ||
_features[key] = value; | ||
_containerRevision++; | ||
} | ||
} | ||
|
||
IEnumerator IEnumerable.GetEnumerator() | ||
{ | ||
return GetEnumerator(); | ||
} | ||
|
||
/// <inheritdoc /> | ||
public IEnumerator<KeyValuePair<Type, object>> GetEnumerator() | ||
{ | ||
if (_features != null) | ||
{ | ||
foreach (var pair in _features) | ||
{ | ||
yield return pair; | ||
} | ||
} | ||
|
||
if (_defaults != null) | ||
{ | ||
// Don't return features masked by the wrapper. | ||
foreach (var pair in _features == null ? _defaults : _defaults.Except(_features, _featureKeyComparer)) | ||
{ | ||
yield return pair; | ||
} | ||
} | ||
} | ||
|
||
/// <inheritdoc /> | ||
public TFeature? Get<TFeature>() | ||
{ | ||
if (typeof(TFeature).IsValueType) | ||
{ | ||
var feature = this[typeof(TFeature)]; | ||
if (feature is null && Nullable.GetUnderlyingType(typeof(TFeature)) is null) | ||
{ | ||
throw new InvalidOperationException( | ||
$"{typeof(TFeature).FullName} does not exist in the feature collection " + | ||
$"and because it is a struct the method can't return null. " + | ||
$"Use 'featureCollection[typeof({typeof(TFeature).FullName})] is not null' " + | ||
$"to check if the feature exists."); | ||
} | ||
return (TFeature?)feature; | ||
} | ||
return (TFeature?)this[typeof(TFeature)]; | ||
} | ||
|
||
/// <inheritdoc /> | ||
public void Set<TFeature>(TFeature? instance) | ||
{ | ||
this[typeof(TFeature)] = instance; | ||
} | ||
|
||
private sealed class KeyComparer : IEqualityComparer<KeyValuePair<Type, object>> | ||
{ | ||
public bool Equals(KeyValuePair<Type, object> x, KeyValuePair<Type, object> y) => | ||
x.Key.Equals(y.Key); | ||
|
||
public int GetHashCode(KeyValuePair<Type, object> obj) => | ||
obj.Key.GetHashCode(); | ||
} | ||
} |
73 changes: 73 additions & 0 deletions
73
src/HotChocolate/Core/src/Features/FeatureCollectionExtensions.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,73 @@ | ||
// This code was originally forked of https://github.com/dotnet/aspnetcore/tree/c7aae8ff34dce81132d0fb3a976349dcc01ff903/src/Extensions/Features/src | ||
|
||
namespace HotChocolate.Features; | ||
|
||
/// <summary> | ||
/// Extension methods for getting feature from <see cref="IFeatureCollection"/> | ||
/// </summary> | ||
public static class FeatureCollectionExtensions | ||
{ | ||
/// <summary> | ||
/// Retrieves the requested feature from the collection. | ||
/// Throws an <see cref="InvalidOperationException"/> if the feature is not present. | ||
/// </summary> | ||
/// <param name="featureCollection">The <see cref="IFeatureCollection"/>.</param> | ||
/// <typeparam name="TFeature">The feature key.</typeparam> | ||
/// <returns>The requested feature.</returns> | ||
public static TFeature GetRequiredFeature<TFeature>(this IFeatureCollection featureCollection) | ||
where TFeature : notnull | ||
{ | ||
if(featureCollection is null) | ||
{ | ||
throw new ArgumentNullException(nameof(featureCollection)); | ||
} | ||
|
||
return featureCollection.Get<TFeature>() ?? | ||
throw new InvalidOperationException($"Feature '{typeof(TFeature)}' is not present."); | ||
} | ||
|
||
/// <summary> | ||
/// Retrieves the requested feature from the collection. | ||
/// Throws an <see cref="InvalidOperationException"/> if the feature is not present. | ||
/// </summary> | ||
/// <param name="featureCollection">feature collection</param> | ||
/// <param name="key">The feature key.</param> | ||
/// <returns>The requested feature.</returns> | ||
public static object GetRequiredFeature(this IFeatureCollection featureCollection, Type key) | ||
{ | ||
if(featureCollection is null) | ||
{ | ||
throw new ArgumentNullException(nameof(featureCollection)); | ||
} | ||
|
||
if(key is null) | ||
{ | ||
throw new ArgumentNullException(nameof(key)); | ||
} | ||
|
||
return featureCollection[key] ?? | ||
throw new InvalidOperationException($"Feature '{key}' is not present."); | ||
} | ||
|
||
/// <summary> | ||
/// Creates a readonly collection of features. | ||
/// </summary> | ||
/// <param name="featureCollection"> | ||
/// The <see cref="IFeatureCollection"/> to make readonly. | ||
/// </param> | ||
/// <returns> | ||
/// A readonly <see cref="IFeatureCollection"/>. | ||
/// </returns> | ||
/// <exception cref="ArgumentNullException"> | ||
/// <paramref name="featureCollection"/> is <c>null</c>. | ||
/// </exception> | ||
public static IFeatureCollection ToReadOnly(this IFeatureCollection featureCollection) | ||
{ | ||
if(featureCollection is null) | ||
{ | ||
throw new ArgumentNullException(nameof(featureCollection)); | ||
} | ||
|
||
return new ReadOnlyFeatureCollection(featureCollection); | ||
} | ||
} |
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,54 @@ | ||
// This code was originally forked of https://github.com/dotnet/aspnetcore/tree/c7aae8ff34dce81132d0fb3a976349dcc01ff903/src/Extensions/Features/src | ||
|
||
namespace HotChocolate.Features; | ||
|
||
/// <summary> | ||
/// A cached reference to a feature. | ||
/// </summary> | ||
/// <typeparam name="T">The feature type.</typeparam> | ||
public struct FeatureReference<T> | ||
{ | ||
private T? _feature; | ||
private int _revision; | ||
|
||
private FeatureReference(T? feature, int revision) | ||
{ | ||
_feature = feature; | ||
_revision = revision; | ||
} | ||
|
||
/// <summary> | ||
/// Gets the default <see cref="FeatureReference{T}"/>. | ||
/// </summary> | ||
public static readonly FeatureReference<T> Default = new(default, -1); | ||
|
||
/// <summary> | ||
/// Gets the feature of type <typeparamref name="T"/> from <paramref name="features"/>. | ||
/// </summary> | ||
/// <param name="features">The <see cref="IFeatureCollection"/>.</param> | ||
/// <returns>The feature.</returns> | ||
public T? Fetch(IFeatureCollection features) | ||
{ | ||
if (_revision == features.Revision) | ||
{ | ||
return _feature; | ||
} | ||
_feature = (T?)features[typeof(T)]; | ||
_revision = features.Revision; | ||
return _feature; | ||
} | ||
|
||
/// <summary> | ||
/// Updates the reference to the feature. | ||
/// </summary> | ||
/// <param name="features">The <see cref="IFeatureCollection"/> to update.</param> | ||
/// <param name="feature">The instance of the feature.</param> | ||
/// <returns>A reference to <paramref name="feature"/> after the operation has completed.</returns> | ||
public T Update(IFeatureCollection features, T feature) | ||
{ | ||
features[typeof(T)] = feature; | ||
_feature = feature; | ||
_revision = features.Revision; | ||
return feature; | ||
} | ||
} |
Oops, something went wrong.