Skip to content

Commit 46efb1b

Browse files
committed
First pass at basic async querying support
Note: This commit removes .NET Standard 2.0 support however that won't be the case in the future - .NET Standard 2.0 support will still exist, this was just to get some basic tests running without needing to do a ton of ifdef statements.
1 parent ac77903 commit 46efb1b

File tree

9 files changed

+164
-4
lines changed

9 files changed

+164
-4
lines changed

src/MongoFramework.Profiling.MiniProfiler/MongoFramework.Profiling.MiniProfiler.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
4-
<TargetFramework>netstandard2.0</TargetFramework>
4+
<TargetFramework>netstandard2.1</TargetFramework>
55
<AssemblyName>MongoFramework.Profiling.MiniProfiler</AssemblyName>
66
<Title>MiniProfiler for MongoFramework</Title>
77
<Description>MongoFramework integration for MiniProfiler</Description>
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq.Expressions;
4+
using System.Text;
5+
6+
namespace MongoFramework.Infrastructure.Linq
7+
{
8+
public static class ExpressionAccumulatorHelper
9+
{
10+
public static LambdaExpression AsyncResultTransformer(LambdaExpression resultTransformer)
11+
{
12+
var resultTransformerType = resultTransformer.Body;
13+
14+
return null;
15+
}
16+
}
17+
}

src/MongoFramework/Infrastructure/Linq/IMongoFrameworkQueryProvider.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,22 @@
33
using System.Collections.Generic;
44
using System.Linq;
55
using System.Linq.Expressions;
6+
using System.Threading;
7+
using System.Threading.Tasks;
68

79
namespace MongoFramework.Infrastructure.Linq
810
{
911
public interface IMongoFrameworkQueryProvider : IQueryProvider
1012
{
1113
IMongoDbConnection Connection { get; }
1214
Expression GetBaseExpression();
15+
object ExecuteAsync(Expression expression, CancellationToken cancellationToken = default);
1316
string ToQuery(Expression expression);
1417
}
1518

1619
public interface IMongoFrameworkQueryProvider<TEntity> : IMongoFrameworkQueryProvider where TEntity : class
1720
{
1821
EntityProcessorCollection<TEntity> EntityProcessors { get; }
22+
IAsyncEnumerable<TResult> ExecuteAsync<TResult>(Expression expression, CancellationToken cancellationToken = default);
1923
}
2024
}

src/MongoFramework/Infrastructure/Linq/IMongoFrameworkQueryable.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
using System.Linq;
1+
using System.Collections.Generic;
2+
using System.Linq;
3+
using System.Threading;
24

35
namespace MongoFramework.Infrastructure.Linq
46
{
@@ -9,5 +11,6 @@ public interface IMongoFrameworkQueryable : IOrderedQueryable
911

1012
public interface IMongoFrameworkQueryable<TOutput> : IMongoFrameworkQueryable, IOrderedQueryable<TOutput>
1113
{
14+
IAsyncEnumerable<TOutput> AsAsyncEnumerable(CancellationToken cancellationToken = default);
1215
}
1316
}

src/MongoFramework/Infrastructure/Linq/MongoFrameworkQueryProvider.cs

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@
99
using System.Linq;
1010
using System.Linq.Expressions;
1111
using System.Reflection;
12+
using System.Runtime.CompilerServices;
13+
using System.Threading;
14+
using System.Threading.Tasks;
1215

1316
namespace MongoFramework.Infrastructure.Linq
1417
{
@@ -81,6 +84,28 @@ public TResult Execute<TResult>(Expression expression)
8184
{
8285
return (TResult)Execute(expression);
8386
}
87+
public object ExecuteAsync(Expression expression, CancellationToken cancellationToken = default)
88+
{
89+
var model = GetExecutionModel(expression);
90+
var outputType = model.Serializer.ValueType;
91+
92+
//aka. ExecuteModelAsync<outputType>(model, cancellationToken)
93+
94+
Expression executor = Expression.Call(
95+
Expression.Constant(this),
96+
nameof(ExecuteModelAsync),
97+
new[] { outputType },
98+
Expression.Constant(model, typeof(AggregateExecutionModel)),
99+
Expression.Constant(cancellationToken));
100+
101+
var lambda = Expression.Lambda(executor);
102+
return lambda.Compile().DynamicInvoke(null);
103+
}
104+
105+
public IAsyncEnumerable<TResult> ExecuteAsync<TResult>(Expression expression, CancellationToken cancellationToken = default)
106+
{
107+
return (IAsyncEnumerable<TResult>)ExecuteAsync(expression, cancellationToken);
108+
}
84109

85110
private IMongoCollection<TEntity> GetCollection()
86111
{
@@ -167,6 +192,48 @@ private IEnumerable<TResult> ExecuteModel<TResult>(AggregateExecutionModel model
167192
}
168193
}
169194

195+
private async IAsyncEnumerable<TResult> ExecuteModelAsync<TResult>(AggregateExecutionModel model, [EnumeratorCancellation] CancellationToken cancellationToken)
196+
{
197+
var serializer = model.Serializer as IBsonSerializer<TResult>;
198+
var pipeline = PipelineDefinition<TEntity, TResult>.Create(model.Stages, serializer);
199+
200+
using (var diagnostics = DiagnosticRunner.Start<TEntity>(Connection, model))
201+
{
202+
IAsyncCursor<TResult> underlyingCursor;
203+
204+
try
205+
{
206+
underlyingCursor = await GetCollection().AggregateAsync(pipeline, cancellationToken: cancellationToken);
207+
}
208+
catch (Exception exception)
209+
{
210+
diagnostics.Error(exception);
211+
throw;
212+
}
213+
214+
var hasFirstResult = false;
215+
while (await underlyingCursor.MoveNextAsync(cancellationToken))
216+
{
217+
if (!hasFirstResult)
218+
{
219+
hasFirstResult = true;
220+
diagnostics.FirstReadResult<TResult>();
221+
}
222+
223+
var resultBatch = underlyingCursor.Current;
224+
foreach (var item in resultBatch)
225+
{
226+
if (item is TEntity entityItem)
227+
{
228+
EntityProcessors.ProcessEntity(entityItem, Connection);
229+
}
230+
231+
yield return item;
232+
}
233+
}
234+
}
235+
}
236+
170237
public string ToQuery(Expression expression)
171238
{
172239
var model = GetExecutionModel(expression);

src/MongoFramework/Infrastructure/Linq/MongoFrameworkQueryable.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using System.Collections.Generic;
77
using System.Linq;
88
using System.Linq.Expressions;
9+
using System.Threading;
910

1011
namespace MongoFramework.Infrastructure.Linq
1112
{
@@ -44,5 +45,10 @@ public string ToQuery()
4445
{
4546
return InternalProvider.ToQuery(Expression);
4647
}
48+
49+
public IAsyncEnumerable<TOutput> AsAsyncEnumerable(CancellationToken cancellationToken = default)
50+
{
51+
return (IAsyncEnumerable<TOutput>)InternalProvider.ExecuteAsync(Expression, cancellationToken);
52+
}
4753
}
4854
}

src/MongoFramework/MongoFramework.csproj

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
4-
<TargetFrameworks>netstandard2.0</TargetFrameworks>
4+
<TargetFrameworks>netstandard2.1</TargetFrameworks>
55
<AssemblyName>MongoFramework</AssemblyName>
66
<Title>MongoFramework</Title>
77
<Description>An "Entity Framework"-like interface for the MongoDB C# Driver</Description>
@@ -13,6 +13,7 @@
1313
<PackageReference Include="MongoDB.Driver" Version="2.9.2" />
1414
<PackageReference Include="System.ComponentModel.Annotations" Version="4.6.0" />
1515
<PackageReference Include="System.Configuration.ConfigurationManager" Version="4.6.0" />
16+
<PackageReference Include="System.Linq.Async" Version="4.0.0" />
1617
</ItemGroup>
1718

1819
</Project>
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using System.Threading.Tasks;
6+
using Microsoft.VisualStudio.TestTools.UnitTesting;
7+
using MongoFramework.Infrastructure;
8+
using MongoFramework.Infrastructure.Linq;
9+
using MongoFramework.Infrastructure.Mapping;
10+
11+
namespace MongoFramework.Tests.Infrastructure.Linq
12+
{
13+
[TestClass]
14+
public class MongoFrameworkQueryProviderTests : TestBase
15+
{
16+
public class MongoFrameworkQueryableModel
17+
{
18+
public string Id { get; set; }
19+
public string Title { get; set; }
20+
}
21+
22+
[TestMethod]
23+
public async Task EnumerateQueryable()
24+
{
25+
EntityMapping.RegisterType(typeof(MongoFrameworkQueryableModel));
26+
27+
var connection = TestConfiguration.GetConnection();
28+
var provider = new MongoFrameworkQueryProvider<MongoFrameworkQueryableModel>(connection);
29+
var queryable = new MongoFrameworkQueryable<MongoFrameworkQueryableModel>(provider);
30+
31+
var entityCollection = new EntityCollection<MongoFrameworkQueryableModel>();
32+
var writerPipeline = new EntityWriterPipeline<MongoFrameworkQueryableModel>(connection);
33+
writerPipeline.AddCollection(entityCollection);
34+
entityCollection.Update(new MongoFrameworkQueryableModel { Title = "EnumerateQueryable" }, EntityEntryState.Added);
35+
writerPipeline.Write();
36+
37+
await foreach (var entity in queryable.AsAsyncEnumerable())
38+
{
39+
Assert.AreEqual("EnumerateQueryable", entity.Title);
40+
}
41+
}
42+
43+
[TestMethod]
44+
public async Task FirstOrDefaultAsyncQuery()
45+
{
46+
EntityMapping.RegisterType(typeof(MongoFrameworkQueryableModel));
47+
48+
var connection = TestConfiguration.GetConnection();
49+
var provider = new MongoFrameworkQueryProvider<MongoFrameworkQueryableModel>(connection);
50+
var queryable = new MongoFrameworkQueryable<MongoFrameworkQueryableModel>(provider);
51+
52+
var entityCollection = new EntityCollection<MongoFrameworkQueryableModel>();
53+
var writerPipeline = new EntityWriterPipeline<MongoFrameworkQueryableModel>(connection);
54+
writerPipeline.AddCollection(entityCollection);
55+
entityCollection.Update(new MongoFrameworkQueryableModel { Title = "FirstOrDefaultAsyncQuery" }, EntityEntryState.Added);
56+
writerPipeline.Write();
57+
58+
var result = await queryable.AsAsyncEnumerable().FirstOrDefaultAsync();
59+
Assert.AreEqual("FirstOrDefaultAsyncQuery", result.Title);
60+
}
61+
}
62+
}

tests/MongoFramework.Tests/MongoFramework.Tests.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
<PropertyGroup>
44
<AssemblyName>MongoFramework.Tests</AssemblyName>
55
<RootNamespace>MongoFramework.Tests</RootNamespace>
6-
<TargetFrameworks>net461;netcoreapp2.1;netcoreapp3.0</TargetFrameworks>
6+
<TargetFrameworks>netcoreapp3.0</TargetFrameworks>
77
<IsPackable>false</IsPackable>
88
</PropertyGroup>
99

0 commit comments

Comments
 (0)