Skip to content

Commit 4a171d6

Browse files
authored
LongCount (#321)
* LongCount & LongCountAsync * remove symbols
1 parent 82c81e8 commit 4a171d6

11 files changed

Lines changed: 360 additions & 7 deletions

File tree

src/EntityFramework.DynamicLinq/EntityFramework.DynamicLinq.csproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,9 @@
3131
</PropertyGroup>
3232

3333
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
34-
<IncludeSource>True</IncludeSource>
34+
<!--<IncludeSource>True</IncludeSource>
3535
<IncludeSymbols>True</IncludeSymbols>
36-
<PathMap>$(MSBuildProjectDirectory)=/</PathMap>
36+
<PathMap>$(MSBuildProjectDirectory)=/</PathMap>-->
3737
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
3838
</PropertyGroup>
3939

src/Microsoft.EntityFrameworkCore.DynamicLinq/EFDynamicQueryableExtensions.cs

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -693,6 +693,93 @@ public static Task<dynamic> LastOrDefaultAsync([NotNull] this IQueryable source,
693693
}
694694
#endregion LastOrDefault
695695

696+
#region LongCount
697+
private static readonly MethodInfo _longCount = GetMethod(nameof(Queryable.LongCount));
698+
699+
/// <summary>
700+
/// Asynchronously returns the number of elements in a sequence.
701+
/// </summary>
702+
/// <remarks>
703+
/// Multiple active operations on the same context instance are not supported. Use 'await' to ensure
704+
/// that any asynchronous operations have completed before calling another method on this context.
705+
/// </remarks>
706+
/// <param name="source">
707+
/// An <see cref="IQueryable" /> that contains the elements to be counted.
708+
/// </param>
709+
/// <param name="cancellationToken">
710+
/// A <see cref="CancellationToken" /> to observe while waiting for the task to complete.
711+
/// </param>
712+
/// <returns>
713+
/// A task that represents the asynchronous operation.
714+
/// The task result contains the number of elements in the input sequence.
715+
/// </returns>
716+
[PublicAPI]
717+
public static Task<long> LongCountAsync([NotNull] this IQueryable source, CancellationToken cancellationToken = default(CancellationToken))
718+
{
719+
Check.NotNull(source, nameof(source));
720+
Check.NotNull(cancellationToken, nameof(cancellationToken));
721+
722+
return ExecuteAsync<long>(_longCount, source, cancellationToken);
723+
}
724+
725+
private static readonly MethodInfo _longCountPredicate = GetMethod(nameof(Queryable.LongCount), 1);
726+
727+
/// <summary>
728+
/// Asynchronously returns the number of elements in a sequence that satisfy a condition.
729+
/// </summary>
730+
/// <remarks>
731+
/// Multiple active operations on the same context instance are not supported. Use 'await' to ensure
732+
/// that any asynchronous operations have completed before calling another method on this context.
733+
/// </remarks>
734+
/// <param name="source">
735+
/// An <see cref="IQueryable" /> that contains the elements to be counted.
736+
/// </param>
737+
/// <param name="predicate"> A function to test each element for a condition. </param>
738+
/// <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>
739+
/// <returns>
740+
/// A task that represents the asynchronous operation.
741+
/// The task result contains the number of elements in the sequence that satisfy the condition in the predicate
742+
/// function.
743+
/// </returns>
744+
[PublicAPI]
745+
public static Task<long> LongCountAsync([NotNull] this IQueryable source, [NotNull] string predicate, [CanBeNull] params object[] args)
746+
{
747+
return LongCountAsync(source, default(CancellationToken), predicate, args);
748+
}
749+
750+
/// <summary>
751+
/// Asynchronously returns the number of elements in a sequence that satisfy a condition.
752+
/// </summary>
753+
/// <remarks>
754+
/// Multiple active operations on the same context instance are not supported. Use 'await' to ensure
755+
/// that any asynchronous operations have completed before calling another method on this context.
756+
/// </remarks>
757+
/// <param name="source">
758+
/// An <see cref="IQueryable" /> that contains the elements to be counted.
759+
/// </param>
760+
/// <param name="predicate"> A function to test each element for a condition. </param>
761+
/// <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>
762+
/// <param name="cancellationToken">
763+
/// A <see cref="CancellationToken" /> to observe while waiting for the task to complete.
764+
/// </param>
765+
/// <returns>
766+
/// A task that represents the asynchronous operation.
767+
/// The task result contains the number of elements in the sequence that satisfy the condition in the predicate
768+
/// function.
769+
/// </returns>
770+
[PublicAPI]
771+
public static Task<long> LongCountAsync([NotNull] this IQueryable source, CancellationToken cancellationToken, [NotNull] string predicate, [CanBeNull] params object[] args)
772+
{
773+
Check.NotNull(source, nameof(source));
774+
Check.NotNull(predicate, nameof(predicate));
775+
Check.NotNull(cancellationToken, nameof(cancellationToken));
776+
777+
LambdaExpression lambda = DynamicExpressionParser.ParseLambda(false, source.ElementType, null, predicate, args);
778+
779+
return ExecuteAsync<long>(_longCountPredicate, source, Expression.Quote(lambda), cancellationToken);
780+
}
781+
#endregion LongCount
782+
696783
#region SingleOrDefaultAsync
697784
private static readonly MethodInfo _singleOrDefault = GetMethod(nameof(Queryable.SingleOrDefault));
698785

src/Microsoft.EntityFrameworkCore.DynamicLinq/Microsoft.EntityFrameworkCore.DynamicLinq.csproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,9 @@
3535
</PropertyGroup>
3636

3737
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
38-
<IncludeSource>True</IncludeSource>
38+
<!--<IncludeSource>True</IncludeSource>
3939
<IncludeSymbols>True</IncludeSymbols>
40-
<PathMap>$(MSBuildProjectDirectory)=/</PathMap>
40+
<PathMap>$(MSBuildProjectDirectory)=/</PathMap>-->
4141
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
4242
</PropertyGroup>
4343

src/System.Linq.Dynamic.Core/DynamicQueryableExtensions.cs

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1120,6 +1120,79 @@ public static dynamic LastOrDefault([NotNull] this IQueryable source, [NotNull]
11201120
}
11211121
#endregion LastOrDefault
11221122

1123+
#region LongCount
1124+
private static readonly MethodInfo _longCount = GetMethod(nameof(Queryable.LongCount));
1125+
1126+
/// <summary>
1127+
/// Returns the number of elements in a sequence.
1128+
/// </summary>
1129+
/// <param name="source">The <see cref="IQueryable"/> that contains the elements to be counted.</param>
1130+
/// <example>
1131+
/// <code language="cs">
1132+
/// IQueryable queryable = employees.AsQueryable();
1133+
/// var result = queryable.LongCount();
1134+
/// </code>
1135+
/// </example>
1136+
/// <returns>The number of elements in the input sequence.</returns>
1137+
public static long LongCount([NotNull] this IQueryable source)
1138+
{
1139+
Check.NotNull(source, nameof(source));
1140+
1141+
return Execute<long>(_longCount, source);
1142+
}
1143+
1144+
private static readonly MethodInfo _longCountPredicate = GetMethod(nameof(Queryable.LongCount), 1);
1145+
1146+
/// <summary>
1147+
/// Returns the number of elements in a sequence.
1148+
/// </summary>
1149+
/// <param name="source">The <see cref="IQueryable"/> that contains the elements to be counted.</param>
1150+
/// <param name="config">The <see cref="ParsingConfig"/>.</param>
1151+
/// <param name="predicate">A function to test each element for a condition.</param>
1152+
/// <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>
1153+
/// <example>
1154+
/// <code language="cs">
1155+
/// IQueryable queryable = employees.AsQueryable();
1156+
/// var result1 = queryable.LongCount("Income > 50");
1157+
/// var result2 = queryable.LongCount("Income > @0", 50);
1158+
/// var result3 = queryable.Select("Roles.LongCount()");
1159+
/// </code>
1160+
/// </example>
1161+
/// <returns>The number of elements in the specified sequence that satisfies a condition.</returns>
1162+
[PublicAPI]
1163+
public static long LongCount([NotNull] this IQueryable source, [NotNull] ParsingConfig config, [NotNull] string predicate, params object[] args)
1164+
{
1165+
Check.NotNull(source, nameof(source));
1166+
Check.NotNull(config, nameof(config));
1167+
Check.NotEmpty(predicate, nameof(predicate));
1168+
1169+
bool createParameterCtor = SupportsLinqToObjects(config, source);
1170+
LambdaExpression lambda = DynamicExpressionParser.ParseLambda(config, createParameterCtor, source.ElementType, null, predicate, args);
1171+
1172+
return Execute<long>(_longCountPredicate, source, lambda);
1173+
}
1174+
1175+
/// <inheritdoc cref="LongCount(IQueryable, ParsingConfig, string, object[])"/>
1176+
public static long LongCount([NotNull] this IQueryable source, [NotNull] string predicate, params object[] args)
1177+
{
1178+
return LongCount(source, ParsingConfig.Default, predicate, args);
1179+
}
1180+
1181+
/// <summary>
1182+
/// Returns the number of elements in a sequence.
1183+
/// </summary>
1184+
/// <param name="source">The <see cref="IQueryable"/> that contains the elements to be counted.</param>
1185+
/// <param name="lambda">A cached Lambda Expression.</param>
1186+
/// <returns>The number of elements in the specified sequence that satisfies a condition.</returns>
1187+
public static long LongCount([NotNull] this IQueryable source, [NotNull] LambdaExpression lambda)
1188+
{
1189+
Check.NotNull(source, nameof(source));
1190+
Check.NotNull(lambda, nameof(lambda));
1191+
1192+
return Execute<long>(_longCountPredicate, source, lambda);
1193+
}
1194+
#endregion LongCount
1195+
11231196
#region OfType
11241197
private static readonly MethodInfo _ofType = GetGenericMethod(nameof(Queryable.OfType));
11251198

src/System.Linq.Dynamic.Core/Parser/SupportedMethods/IEnumerableSignatures.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ internal interface IEnumerableSignatures
3232
void Last(bool predicate);
3333
void LastOrDefault();
3434
void LastOrDefault(bool predicate);
35+
void LongCount();
36+
void LongCount(bool predicate);
3537
void Max(object selector);
3638
void Min(object selector);
3739
void OfType(string type);

src/System.Linq.Dynamic.Core/Parser/SupportedMethods/IQueryableSignatures.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ internal interface IQueryableSignatures
3131
void Last(bool predicate);
3232
void LastOrDefault();
3333
void LastOrDefault(bool predicate);
34+
void LongCount();
35+
void LongCount(bool predicate);
3436
void Max(object selector);
3537
void Min(object selector);
3638
void OfType(string type);

src/System.Linq.Dynamic.Core/System.Linq.Dynamic.Core.csproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,9 @@
2828
</PropertyGroup>
2929

3030
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
31-
<IncludeSource>True</IncludeSource>
31+
<!--<IncludeSource>True</IncludeSource>
3232
<IncludeSymbols>True</IncludeSymbols>
33-
<PathMap>$(MSBuildProjectDirectory)=/</PathMap>
33+
<PathMap>$(MSBuildProjectDirectory)=/</PathMap>-->
3434
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
3535
</PropertyGroup>
3636

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
using System.Linq.Dynamic.Core.Tests.Helpers.Entities;
2+
using Xunit;
3+
4+
namespace System.Linq.Dynamic.Core.Tests
5+
{
6+
public partial class EntitiesTests
7+
{
8+
[Fact]
9+
public void Entities_LongCount_Predicate()
10+
{
11+
const string search = "a";
12+
13+
//Arrange
14+
var blog1 = new Blog { Name = "blog a", BlogId = 1000, Created = DateTime.Now };
15+
var blog2 = new Blog { Name = "blog b", BlogId = 3000, Created = DateTime.Now };
16+
_context.Blogs.Add(blog1);
17+
_context.Blogs.Add(blog2);
18+
_context.SaveChanges();
19+
20+
//Act
21+
long expected = _context.Blogs.LongCount(b => b.Name.Contains(search));
22+
long result = _context.Blogs.LongCount("Name.Contains(@0)", search);
23+
24+
//Assert
25+
Assert.Equal(expected, result);
26+
}
27+
}
28+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
#if EFCORE
2+
using Microsoft.EntityFrameworkCore;
3+
using Microsoft.EntityFrameworkCore.DynamicLinq;
4+
#else
5+
using System.Data.Entity;
6+
using EntityFramework.DynamicLinq;
7+
#endif
8+
using System.Linq.Expressions;
9+
using System.Threading.Tasks;
10+
using System.Linq.Dynamic.Core.Tests.Helpers.Entities;
11+
using Xunit;
12+
13+
namespace System.Linq.Dynamic.Core.Tests
14+
{
15+
public partial class EntitiesTests
16+
{
17+
[Fact]
18+
public async Task Entities_LongCountAsync_Predicate_Args()
19+
{
20+
const string search = "a";
21+
22+
//Arrange
23+
var blog1 = new Blog { Name = "blog a", BlogId = 1000, Created = DateTime.Now };
24+
var blog2 = new Blog { Name = "blog b", BlogId = 3000, Created = DateTime.Now };
25+
_context.Blogs.Add(blog1);
26+
_context.Blogs.Add(blog2);
27+
_context.SaveChanges();
28+
29+
Expression<Func <Blog, bool>> predicate = b => b.Name.Contains(search);
30+
31+
#if EFCORE
32+
var expected = await EntityFrameworkQueryableExtensions.LongCountAsync(_context.Blogs, predicate);
33+
#else
34+
var expected = await QueryableExtensions.LongCountAsync(_context.Blogs, predicate);
35+
#endif
36+
37+
//Act
38+
long result = await (_context.Blogs as IQueryable).LongCountAsync("Name.Contains(@0)", search);
39+
40+
//Assert
41+
Assert.Equal(expected, result);
42+
}
43+
}
44+
}

test/System.Linq.Dynamic.Core.Tests/QueryableTests.Count.cs

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,5 +53,35 @@ public void Count_Predicate_WithArgs()
5353
//Assert
5454
Assert.Equal(expected, result);
5555
}
56+
57+
[Fact]
58+
public void Count_Dynamic_Select()
59+
{
60+
// Arrange
61+
IQueryable<User> queryable = User.GenerateSampleModels(1).AsQueryable();
62+
63+
// Act
64+
var expected = queryable.Select(x => x.Roles.Count()).ToArray();
65+
var result = queryable.Select("Roles.Count()").ToDynamicArray<int>();
66+
67+
// Assert
68+
Assert.Equal(expected, result);
69+
}
70+
71+
[Fact]
72+
public void Count_Dynamic_Where()
73+
{
74+
const string search = "e";
75+
76+
// Arrange
77+
var testList = User.GenerateSampleModels(10);
78+
var queryable = testList.AsQueryable();
79+
80+
// Act
81+
var expected = queryable.Where(u => u.Roles.Count(r => r.Name.Contains(search)) > 0).ToArray();
82+
var result = queryable.Where("Roles.Count(Name.Contains(@0)) > 0", search).ToArray();
83+
84+
Assert.Equal(expected, result);
85+
}
5686
}
57-
}
87+
}

0 commit comments

Comments
 (0)