Skip to content

Commit 6057540

Browse files
committed
AnyAsync #24
1 parent 095d3e6 commit 6057540

File tree

9 files changed

+452
-0
lines changed

9 files changed

+452
-0
lines changed

System.Linq.Dynamic.Core.sln

+19
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "ConsoleApp1", "src-console\
4848
EndProject
4949
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "System.Linq.Dynamic.Core.Tests", "test\System.Linq.Dynamic.Core.Tests\System.Linq.Dynamic.Core.Tests.xproj", "{62B66830-C2BD-4968-B305-C181CE0BA44B}"
5050
EndProject
51+
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "System.Linq.Dynamic.Core.EF", "src\System.Linq.Dynamic.Core.EF\System.Linq.Dynamic.Core.EF.xproj", "{9DA1FC17-280B-4808-B378-5A5891E5A205}"
52+
EndProject
5153
Global
5254
GlobalSection(SolutionConfigurationPlatforms) = preSolution
5355
Debug|Any CPU = Debug|Any CPU
@@ -208,6 +210,22 @@ Global
208210
{62B66830-C2BD-4968-B305-C181CE0BA44B}.Release|x64.Build.0 = Release|Any CPU
209211
{62B66830-C2BD-4968-B305-C181CE0BA44B}.Release|x86.ActiveCfg = Release|Any CPU
210212
{62B66830-C2BD-4968-B305-C181CE0BA44B}.Release|x86.Build.0 = Release|Any CPU
213+
{9DA1FC17-280B-4808-B378-5A5891E5A205}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
214+
{9DA1FC17-280B-4808-B378-5A5891E5A205}.Debug|Any CPU.Build.0 = Debug|Any CPU
215+
{9DA1FC17-280B-4808-B378-5A5891E5A205}.Debug|ARM.ActiveCfg = Debug|Any CPU
216+
{9DA1FC17-280B-4808-B378-5A5891E5A205}.Debug|ARM.Build.0 = Debug|Any CPU
217+
{9DA1FC17-280B-4808-B378-5A5891E5A205}.Debug|x64.ActiveCfg = Debug|Any CPU
218+
{9DA1FC17-280B-4808-B378-5A5891E5A205}.Debug|x64.Build.0 = Debug|Any CPU
219+
{9DA1FC17-280B-4808-B378-5A5891E5A205}.Debug|x86.ActiveCfg = Debug|Any CPU
220+
{9DA1FC17-280B-4808-B378-5A5891E5A205}.Debug|x86.Build.0 = Debug|Any CPU
221+
{9DA1FC17-280B-4808-B378-5A5891E5A205}.Release|Any CPU.ActiveCfg = Release|Any CPU
222+
{9DA1FC17-280B-4808-B378-5A5891E5A205}.Release|Any CPU.Build.0 = Release|Any CPU
223+
{9DA1FC17-280B-4808-B378-5A5891E5A205}.Release|ARM.ActiveCfg = Release|Any CPU
224+
{9DA1FC17-280B-4808-B378-5A5891E5A205}.Release|ARM.Build.0 = Release|Any CPU
225+
{9DA1FC17-280B-4808-B378-5A5891E5A205}.Release|x64.ActiveCfg = Release|Any CPU
226+
{9DA1FC17-280B-4808-B378-5A5891E5A205}.Release|x64.Build.0 = Release|Any CPU
227+
{9DA1FC17-280B-4808-B378-5A5891E5A205}.Release|x86.ActiveCfg = Release|Any CPU
228+
{9DA1FC17-280B-4808-B378-5A5891E5A205}.Release|x86.Build.0 = Release|Any CPU
211229
EndGlobalSection
212230
GlobalSection(SolutionProperties) = preSolution
213231
HideSolutionNode = FALSE
@@ -222,5 +240,6 @@ Global
222240
{1BBA5141-F273-49A2-8F9E-11C70931D770} = {C98BB543-9BDA-4EE3-927D-0F10CC24A49B}
223241
{E0627580-2B20-4AD8-8D4F-20C96DBC0FC0} = {C98BB543-9BDA-4EE3-927D-0F10CC24A49B}
224242
{62B66830-C2BD-4968-B305-C181CE0BA44B} = {32699374-E6B9-4870-9D4A-C8884D16CEA9}
243+
{9DA1FC17-280B-4808-B378-5A5891E5A205} = {DB9A5C42-3F8B-4546-96F2-DD35BCACA018}
225244
EndGlobalSection
226245
EndGlobal

appveyor.yml

+2
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,14 @@ environment:
2020

2121
build_script:
2222
- appveyor-retry dotnet restore .\src\System.Linq.Dynamic.Core -v Minimal
23+
- appveyor-retry dotnet restore .\src\System.Linq.Dynamic.Core.EF -v Minimal
2324
- appveyor-retry dotnet restore .\test\System.Linq.Dynamic.Core.Tests -v Minimal
2425

2526
- dotnet build src\System.Linq.Dynamic.Core\project.json -c %CONFIGURATION%
2627
- dotnet build test\System.Linq.Dynamic.Core.Tests\project.json -c %CONFIGURATION%
2728

2829
- dotnet pack -c Release --no-build --version-suffix %LABEL% -o .\artifacts .\src\System.Linq.Dynamic.Core\project.json
30+
- dotnet pack -c Release --no-build --version-suffix %LABEL% -o .\artifacts .\src\System.Linq.Dynamic.Core.EF\project.json
2931

3032
test_script:
3133
- dotnet test -c %CONFIGURATION% --no-build .\test\System.Linq.Dynamic.Core.Tests
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
using System.Collections.Generic;
2+
using System.Collections;
3+
using System.Linq.Dynamic.Core;
4+
using System.Linq.Dynamic.Core.Extensions;
5+
using System.Linq.Dynamic.Core.Validation;
6+
using System.Linq.Expressions;
7+
using System.Reflection;
8+
using JetBrains.Annotations;
9+
using System.Threading;
10+
using System.Threading.Tasks;
11+
#if NETSTANDARD
12+
using Microsoft.EntityFrameworkCore.Query.Internal;
13+
#endif
14+
#if WINDOWS_APP
15+
using System;
16+
using System.Linq;
17+
#endif
18+
19+
namespace System.Linq.Dynamic.Core.EF
20+
{
21+
/// <summary>
22+
/// Provides a set of static (Shared in Visual Basic) methods for querying data structures that implement <see cref="IQueryable"/>.
23+
/// It allows dynamic string based querying. Very handy when, at compile time, you don't know the type of queries that will be generated,
24+
/// or when downstream components only return column names to sort and filter by.
25+
/// </summary>
26+
public static class EntityFrameworkDynamicQueryableExtensions
27+
{
28+
#region AnyAsync
29+
private static readonly MethodInfo _any = GetMethod(nameof(Queryable.Any));
30+
31+
/// <summary>
32+
/// Asynchronously determines whether a sequence contains any elements.
33+
/// </summary>
34+
/// <remarks>
35+
/// Multiple active operations on the same context instance are not supported. Use 'await' to ensure
36+
/// that any asynchronous operations have completed before calling another method on this context.
37+
/// </remarks>
38+
/// <param name="source">
39+
/// An <see cref="IQueryable{T}" /> to check for being empty.
40+
/// </param>
41+
/// <param name="cancellationToken">
42+
/// A <see cref="CancellationToken" /> to observe while waiting for the task to complete.
43+
/// </param>
44+
/// <returns>
45+
/// A task that represents the asynchronous operation.
46+
/// The task result contains <c>true</c> if the source sequence contains any elements; otherwise, <c>false</c>.
47+
/// </returns>
48+
public static Task<bool> AnyAsync([NotNull] this IQueryable source, CancellationToken cancellationToken = default(CancellationToken))
49+
{
50+
Check.NotNull(source, nameof(source));
51+
52+
return ExecuteAsync<bool>(_any, source, cancellationToken);
53+
}
54+
55+
private static readonly MethodInfo _anyPredicate = GetMethod(nameof(Queryable.Any), 1);
56+
57+
/// <summary>
58+
/// Asynchronously determines whether any element of a sequence satisfies a condition.
59+
/// </summary>
60+
/// <remarks>
61+
/// Multiple active operations on the same context instance are not supported. Use 'await' to ensure
62+
/// that any asynchronous operations have completed before calling another method on this context.
63+
/// </remarks>
64+
/// <param name="source">
65+
/// An <see cref="IQueryable" /> whose elements to test for a condition.
66+
/// </param>
67+
/// <param name="predicate"> A function to test each element for a condition.</param>
68+
/// <param name="args">An object array that contains zero or more objects to insert into the predicate as parameters. Similar to the way String.Format formats strings.</param>
69+
/// <param name="cancellationToken">
70+
/// A <see cref="CancellationToken" /> to observe while waiting for the task to complete.
71+
/// </param>
72+
/// <returns>
73+
/// A task that represents the asynchronous operation.
74+
/// The task result contains <c>true</c> if any elements in the source sequence pass the test in the specified
75+
/// predicate; otherwise, <c>false</c>.
76+
/// </returns>
77+
public static Task<bool> AnyAsync([NotNull] this IQueryable source, [NotNull] string predicate, [CanBeNull] object[] args = null, CancellationToken cancellationToken = default(CancellationToken))
78+
{
79+
Check.NotNull(source, nameof(source));
80+
Check.NotEmpty(predicate, nameof(predicate));
81+
82+
bool createParameterCtor = source.IsLinqToObjects();
83+
LambdaExpression lambda = DynamicExpression.ParseLambda(createParameterCtor, source.ElementType, null, predicate, args);
84+
85+
return ExecuteAsync<bool>(_anyPredicate, source, Expression.Quote(lambda));
86+
}
87+
#endregion AnyAsync
88+
89+
#region Private Helpers
90+
// Copied from https://github.com/aspnet/EntityFramework/blob/9186d0b78a3176587eeb0f557c331f635760fe92/src/Microsoft.EntityFrameworkCore/EntityFrameworkQueryableExtensions.cs
91+
private static Task<TResult> ExecuteAsync<TResult>(MethodInfo operatorMethodInfo, IQueryable source, CancellationToken cancellationToken = default(CancellationToken))
92+
{
93+
var provider = source.Provider as IAsyncQueryProvider;
94+
95+
if (provider != null)
96+
{
97+
if (operatorMethodInfo.IsGenericMethod)
98+
{
99+
operatorMethodInfo = operatorMethodInfo.MakeGenericMethod(source.ElementType);
100+
}
101+
102+
return provider.ExecuteAsync<TResult>(
103+
Expression.Call(null, operatorMethodInfo, source.Expression),
104+
cancellationToken);
105+
}
106+
107+
throw new InvalidOperationException(Res.IQueryableProviderNotAsync);
108+
}
109+
110+
private static Task<TResult> ExecuteAsync<TResult>(MethodInfo operatorMethodInfo, IQueryable source, LambdaExpression expression, CancellationToken cancellationToken = default(CancellationToken))
111+
=> ExecuteAsync<TResult>(operatorMethodInfo, source, Expression.Quote(expression), cancellationToken);
112+
113+
private static Task<TResult> ExecuteAsync<TResult>(MethodInfo operatorMethodInfo, IQueryable source, Expression expression, CancellationToken cancellationToken = default(CancellationToken))
114+
{
115+
var provider = source.Provider as IAsyncQueryProvider;
116+
117+
if (provider != null)
118+
{
119+
operatorMethodInfo
120+
= operatorMethodInfo.GetGenericArguments().Length == 2
121+
? operatorMethodInfo.MakeGenericMethod(source.ElementType, typeof(TResult))
122+
: operatorMethodInfo.MakeGenericMethod(source.ElementType);
123+
124+
return provider.ExecuteAsync<TResult>(
125+
Expression.Call(
126+
null,
127+
operatorMethodInfo,
128+
new[] { source.Expression, expression }),
129+
cancellationToken);
130+
}
131+
132+
throw new InvalidOperationException(Res.IQueryableProviderNotAsync);
133+
}
134+
135+
private static MethodInfo GetMethod<TResult>(string name, int parameterCount = 0, Func<MethodInfo, bool> predicate = null) =>
136+
GetMethod(name, parameterCount, mi => (mi.ReturnType == typeof(TResult)) && ((predicate == null) || predicate(mi)));
137+
138+
private static MethodInfo GetMethod(string name, int parameterCount = 0, Func<MethodInfo, bool> predicate = null) =>
139+
typeof(Queryable).GetTypeInfo().GetDeclaredMethods(name).Single(mi => (mi.GetParameters().Length == parameterCount + 1) && ((predicate == null) || predicate(mi)));
140+
#endregion Private Helpers
141+
}
142+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
using System.Reflection;
2+
3+
// General Information about an assembly is controlled through the following
4+
// set of attributes. Change these attribute values to modify the information
5+
// associated with an assembly.
6+
[assembly: AssemblyTitle("System.Linq.Dynamic.Core.EF")]
7+
[assembly: AssemblyDescription("")]
8+
[assembly: AssemblyConfiguration("")]
9+
[assembly: AssemblyCompany("")]
10+
[assembly: AssemblyProduct("System.Linq.Dynamic.Core.EF")]
11+
[assembly: AssemblyCopyright("Copyright © Stef Heyenrath 2016")]
12+
[assembly: AssemblyTrademark("")]
13+
[assembly: AssemblyCulture("")]
14+
#if !(NETSTANDARD || WINDOWS_APP)
15+
[assembly: System.Resources.NeutralResourcesLanguage("en")]
16+
#endif
17+
18+
// Version information for an assembly consists of the following four values:
19+
//
20+
// Major Version
21+
// Minor Version
22+
// Build Number
23+
// Revision
24+
//
25+
// You can specify all the values or you can default the Build and Revision Numbers
26+
// by using the '*' as shown below:
27+
// [assembly: AssemblyVersion("1.0.*")]
28+
[assembly: AssemblyVersion("1.0.0.0")]
29+
[assembly: AssemblyFileVersion("1.0.0.0")]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
3+
<PropertyGroup>
4+
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
5+
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
6+
</PropertyGroup>
7+
8+
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.Props" Condition="'$(VSToolsPath)' != ''" />
9+
<PropertyGroup Label="Globals">
10+
<ProjectGuid>9da1fc17-280b-4808-b378-5a5891e5a205</ProjectGuid>
11+
<RootNamespace>System.Linq.Dynamic.Core.EF</RootNamespace>
12+
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">.\obj</BaseIntermediateOutputPath>
13+
<OutputPath Condition="'$(OutputPath)'=='' ">.\bin\</OutputPath>
14+
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
15+
</PropertyGroup>
16+
17+
<PropertyGroup>
18+
<SchemaVersion>2.0</SchemaVersion>
19+
</PropertyGroup>
20+
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.targets" Condition="'$(VSToolsPath)' != ''" />
21+
</Project>

0 commit comments

Comments
 (0)