Closed syaylev-minbox closed 3 years ago
For history. I have found one method to solve this that makes no pleasure at all.
I have copy-pasted BatchDataLoader<TKey, T>
, DataLoaderBase<TKey, T>
, DataLoaderBase<TKey, T>.DataLoaderList
and DataLoaderPair<TKey, T>
classes and changed theirs signatures little bit.
Modified dataLoader must return IDataLoaderResult<NonNull<List<T>>>
, but it also must fetch dictionary of List<T>
. In this case ExecutionStrategy
will be satisfied and gql-schema will treat TestClasses
as [TestClass]!
Here is fragments of code just to illustrate an idea.
public IDataLoaderResult<**NonNull<List<TestClass>>**> TestClasses2()
{
var loader = dataLoaderContext.GetOrAddNonNullBatchLoader<int, **List<TestClass>**>(
"Test",
async (ids, ct) =>
{
// same list for all ids for simplicity
return ids.ToDictionary(id => id, value => testClasses);
}
);
return loader.LoadAsync(1);
}
public sealed class NonNullDataLoaderPair<TKey, T> : IDataLoaderResult<**NonNull**<T>> where T : class
{
public T Result => ...;
public void SetResult(T value) => ...;
public async Task<NonNull<T>> GetResultAsync(CancellationToken cancellationToken = default)
{
if (!IsResultSet)
await Loader.DispatchAsync(cancellationToken).ConfigureAwait(false);
return new NonNull<T>(Result);
}
async Task<object> IDataLoaderResult.GetResultAsync(CancellationToken cancellationToken)
{
if (!IsResultSet)
await Loader.DispatchAsync(cancellationToken).ConfigureAwait(false);
return Result;
}
}
public static IDataLoader<TKey, NonNull<T>> GetOrAddNonNullBatchLoader<TKey, T>(this DataLoaderContext context, string loaderKey, Func<IEnumerable<TKey>, CancellationToken, Task<IDictionary<TKey, T>>> fetchFunc,
...)
where T : class
{
...
return context.GetOrAdd(loaderKey, () => new NonNullBatchDataLoader<TKey, T>(fetchFunc, keyComparer, defaultValue, maxBatchSize));
}
public partial class NonNullBatchDataLoader<TKey, T> : IDataLoader, IDataLoader<TKey, NonNull<T>>
where T : class
{
private readonly Func<IEnumerable<TKey>, CancellationToken, Task<IDictionary<TKey, T>>> _loader;
public NonNullBatchDataLoader(Func<IEnumerable<TKey>, CancellationToken, Task<IDictionary<TKey, T>>> fetchDelegate,
...) : this(keyComparer, maxBatchSize)
{
_loader = fetchDelegate ?? throw new ArgumentNullException(nameof(fetchDelegate));
_defaultValue = defaultValue;
}
**public IDataLoaderResult<NonNull<T>> LoadAsync(TKey key)** => ...;
**protected async Task FetchAsync(IEnumerable<NonNullDataLoaderPair<TKey, T>> list, CancellationToken cancellationToken)** => ...;
private class DataLoaderList : Dictionary<TKey, NonNullDataLoaderPair<TKey, T>>, IDataLoader
{
...
}
}
Will bump the version in a bit. Thanks @Shane32 , and thanks for reporting @syaylev-minbox
New package is up btw :)
Seems that "conventions" missing some additional
unwrapper
for that case. Please see unittest below.I get an exception because ExecutionStrategy expects to see IEnumerable but it gets NonNull<List>. Seems that at some point graphql-dotnet decided to require IEnumerable in situations where NonNull is declared.
If you try to get NonNull<List> you will be succeeded. Also if you try to get IDataLoaderResult<NonNull> you will be succeeded too.
This is quite critical issue because this gives no ability to define type [X]! and use dataload at the same time.
Exception details:
Unittest that fails (sorry for verbose):