Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,9 @@
</PropertyGroup>

<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<IncludeSource>True</IncludeSource>
<!--<IncludeSource>True</IncludeSource>
<IncludeSymbols>True</IncludeSymbols>
<PathMap>$(MSBuildProjectDirectory)=/</PathMap>
<PathMap>$(MSBuildProjectDirectory)=/</PathMap>-->
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
</PropertyGroup>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -693,6 +693,93 @@ public static Task<dynamic> LastOrDefaultAsync([NotNull] this IQueryable source,
}
#endregion LastOrDefault

#region LongCount
private static readonly MethodInfo _longCount = GetMethod(nameof(Queryable.LongCount));

/// <summary>
/// Asynchronously returns the number of elements in a sequence.
/// </summary>
/// <remarks>
/// Multiple active operations on the same context instance are not supported. Use 'await' to ensure
/// that any asynchronous operations have completed before calling another method on this context.
/// </remarks>
/// <param name="source">
/// An <see cref="IQueryable" /> that contains the elements to be counted.
/// </param>
/// <param name="cancellationToken">
/// A <see cref="CancellationToken" /> to observe while waiting for the task to complete.
/// </param>
/// <returns>
/// A task that represents the asynchronous operation.
/// The task result contains the number of elements in the input sequence.
/// </returns>
[PublicAPI]
public static Task<long> LongCountAsync([NotNull] this IQueryable source, CancellationToken cancellationToken = default(CancellationToken))
{
Check.NotNull(source, nameof(source));
Check.NotNull(cancellationToken, nameof(cancellationToken));

return ExecuteAsync<long>(_longCount, source, cancellationToken);
}

private static readonly MethodInfo _longCountPredicate = GetMethod(nameof(Queryable.LongCount), 1);

/// <summary>
/// Asynchronously returns the number of elements in a sequence that satisfy a condition.
/// </summary>
/// <remarks>
/// Multiple active operations on the same context instance are not supported. Use 'await' to ensure
/// that any asynchronous operations have completed before calling another method on this context.
/// </remarks>
/// <param name="source">
/// An <see cref="IQueryable" /> that contains the elements to be counted.
/// </param>
/// <param name="predicate"> A function to test each element for a condition. </param>
/// <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>
/// <returns>
/// A task that represents the asynchronous operation.
/// The task result contains the number of elements in the sequence that satisfy the condition in the predicate
/// function.
/// </returns>
[PublicAPI]
public static Task<long> LongCountAsync([NotNull] this IQueryable source, [NotNull] string predicate, [CanBeNull] params object[] args)
{
return LongCountAsync(source, default(CancellationToken), predicate, args);
}

/// <summary>
/// Asynchronously returns the number of elements in a sequence that satisfy a condition.
/// </summary>
/// <remarks>
/// Multiple active operations on the same context instance are not supported. Use 'await' to ensure
/// that any asynchronous operations have completed before calling another method on this context.
/// </remarks>
/// <param name="source">
/// An <see cref="IQueryable" /> that contains the elements to be counted.
/// </param>
/// <param name="predicate"> A function to test each element for a condition. </param>
/// <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>
/// <param name="cancellationToken">
/// A <see cref="CancellationToken" /> to observe while waiting for the task to complete.
/// </param>
/// <returns>
/// A task that represents the asynchronous operation.
/// The task result contains the number of elements in the sequence that satisfy the condition in the predicate
/// function.
/// </returns>
[PublicAPI]
public static Task<long> LongCountAsync([NotNull] this IQueryable source, CancellationToken cancellationToken, [NotNull] string predicate, [CanBeNull] params object[] args)
{
Check.NotNull(source, nameof(source));
Check.NotNull(predicate, nameof(predicate));
Check.NotNull(cancellationToken, nameof(cancellationToken));

LambdaExpression lambda = DynamicExpressionParser.ParseLambda(false, source.ElementType, null, predicate, args);

return ExecuteAsync<long>(_longCountPredicate, source, Expression.Quote(lambda), cancellationToken);
}
#endregion LongCount

#region SingleOrDefaultAsync
private static readonly MethodInfo _singleOrDefault = GetMethod(nameof(Queryable.SingleOrDefault));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,9 @@
</PropertyGroup>

<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<IncludeSource>True</IncludeSource>
<!--<IncludeSource>True</IncludeSource>
<IncludeSymbols>True</IncludeSymbols>
<PathMap>$(MSBuildProjectDirectory)=/</PathMap>
<PathMap>$(MSBuildProjectDirectory)=/</PathMap>-->
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
</PropertyGroup>

Expand Down
73 changes: 73 additions & 0 deletions src/System.Linq.Dynamic.Core/DynamicQueryableExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1120,6 +1120,79 @@ public static dynamic LastOrDefault([NotNull] this IQueryable source, [NotNull]
}
#endregion LastOrDefault

#region LongCount
private static readonly MethodInfo _longCount = GetMethod(nameof(Queryable.LongCount));

/// <summary>
/// Returns the number of elements in a sequence.
/// </summary>
/// <param name="source">The <see cref="IQueryable"/> that contains the elements to be counted.</param>
/// <example>
/// <code language="cs">
/// IQueryable queryable = employees.AsQueryable();
/// var result = queryable.LongCount();
/// </code>
/// </example>
/// <returns>The number of elements in the input sequence.</returns>
public static long LongCount([NotNull] this IQueryable source)
{
Check.NotNull(source, nameof(source));

return Execute<long>(_longCount, source);
}

private static readonly MethodInfo _longCountPredicate = GetMethod(nameof(Queryable.LongCount), 1);

/// <summary>
/// Returns the number of elements in a sequence.
/// </summary>
/// <param name="source">The <see cref="IQueryable"/> that contains the elements to be counted.</param>
/// <param name="config">The <see cref="ParsingConfig"/>.</param>
/// <param name="predicate">A function to test each element for a condition.</param>
/// <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>
/// <example>
/// <code language="cs">
/// IQueryable queryable = employees.AsQueryable();
/// var result1 = queryable.LongCount("Income > 50");
/// var result2 = queryable.LongCount("Income > @0", 50);
/// var result3 = queryable.Select("Roles.LongCount()");
/// </code>
/// </example>
/// <returns>The number of elements in the specified sequence that satisfies a condition.</returns>
[PublicAPI]
public static long LongCount([NotNull] this IQueryable source, [NotNull] ParsingConfig config, [NotNull] string predicate, params object[] args)
{
Check.NotNull(source, nameof(source));
Check.NotNull(config, nameof(config));
Check.NotEmpty(predicate, nameof(predicate));

bool createParameterCtor = SupportsLinqToObjects(config, source);
LambdaExpression lambda = DynamicExpressionParser.ParseLambda(config, createParameterCtor, source.ElementType, null, predicate, args);

return Execute<long>(_longCountPredicate, source, lambda);
}

/// <inheritdoc cref="LongCount(IQueryable, ParsingConfig, string, object[])"/>
public static long LongCount([NotNull] this IQueryable source, [NotNull] string predicate, params object[] args)
{
return LongCount(source, ParsingConfig.Default, predicate, args);
}

/// <summary>
/// Returns the number of elements in a sequence.
/// </summary>
/// <param name="source">The <see cref="IQueryable"/> that contains the elements to be counted.</param>
/// <param name="lambda">A cached Lambda Expression.</param>
/// <returns>The number of elements in the specified sequence that satisfies a condition.</returns>
public static long LongCount([NotNull] this IQueryable source, [NotNull] LambdaExpression lambda)
{
Check.NotNull(source, nameof(source));
Check.NotNull(lambda, nameof(lambda));

return Execute<long>(_longCountPredicate, source, lambda);
}
#endregion LongCount

#region OfType
private static readonly MethodInfo _ofType = GetGenericMethod(nameof(Queryable.OfType));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ internal interface IEnumerableSignatures
void Last(bool predicate);
void LastOrDefault();
void LastOrDefault(bool predicate);
void LongCount();
void LongCount(bool predicate);
void Max(object selector);
void Min(object selector);
void OfType(string type);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ internal interface IQueryableSignatures
void Last(bool predicate);
void LastOrDefault();
void LastOrDefault(bool predicate);
void LongCount();
void LongCount(bool predicate);
void Max(object selector);
void Min(object selector);
void OfType(string type);
Expand Down
4 changes: 2 additions & 2 deletions src/System.Linq.Dynamic.Core/System.Linq.Dynamic.Core.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@
</PropertyGroup>

<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<IncludeSource>True</IncludeSource>
<!--<IncludeSource>True</IncludeSource>
<IncludeSymbols>True</IncludeSymbols>
<PathMap>$(MSBuildProjectDirectory)=/</PathMap>
<PathMap>$(MSBuildProjectDirectory)=/</PathMap>-->
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
</PropertyGroup>

Expand Down
28 changes: 28 additions & 0 deletions test/System.Linq.Dynamic.Core.Tests/EntitiesTests.LongCount.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using System.Linq.Dynamic.Core.Tests.Helpers.Entities;
using Xunit;

namespace System.Linq.Dynamic.Core.Tests
{
public partial class EntitiesTests
{
[Fact]
public void Entities_LongCount_Predicate()
{
const string search = "a";

//Arrange
var blog1 = new Blog { Name = "blog a", BlogId = 1000, Created = DateTime.Now };
var blog2 = new Blog { Name = "blog b", BlogId = 3000, Created = DateTime.Now };
_context.Blogs.Add(blog1);
_context.Blogs.Add(blog2);
_context.SaveChanges();

//Act
long expected = _context.Blogs.LongCount(b => b.Name.Contains(search));
long result = _context.Blogs.LongCount("Name.Contains(@0)", search);

//Assert
Assert.Equal(expected, result);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#if EFCORE
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.DynamicLinq;
#else
using System.Data.Entity;
using EntityFramework.DynamicLinq;
#endif
using System.Linq.Expressions;
using System.Threading.Tasks;
using System.Linq.Dynamic.Core.Tests.Helpers.Entities;
using Xunit;

namespace System.Linq.Dynamic.Core.Tests
{
public partial class EntitiesTests
{
[Fact]
public async Task Entities_LongCountAsync_Predicate_Args()
{
const string search = "a";

//Arrange
var blog1 = new Blog { Name = "blog a", BlogId = 1000, Created = DateTime.Now };
var blog2 = new Blog { Name = "blog b", BlogId = 3000, Created = DateTime.Now };
_context.Blogs.Add(blog1);
_context.Blogs.Add(blog2);
_context.SaveChanges();

Expression<Func <Blog, bool>> predicate = b => b.Name.Contains(search);

#if EFCORE
var expected = await EntityFrameworkQueryableExtensions.LongCountAsync(_context.Blogs, predicate);
#else
var expected = await QueryableExtensions.LongCountAsync(_context.Blogs, predicate);
#endif

//Act
long result = await (_context.Blogs as IQueryable).LongCountAsync("Name.Contains(@0)", search);

//Assert
Assert.Equal(expected, result);
}
}
}
32 changes: 31 additions & 1 deletion test/System.Linq.Dynamic.Core.Tests/QueryableTests.Count.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,5 +53,35 @@ public void Count_Predicate_WithArgs()
//Assert
Assert.Equal(expected, result);
}

[Fact]
public void Count_Dynamic_Select()
{
// Arrange
IQueryable<User> queryable = User.GenerateSampleModels(1).AsQueryable();

// Act
var expected = queryable.Select(x => x.Roles.Count()).ToArray();
var result = queryable.Select("Roles.Count()").ToDynamicArray<int>();

// Assert
Assert.Equal(expected, result);
}

[Fact]
public void Count_Dynamic_Where()
{
const string search = "e";

// Arrange
var testList = User.GenerateSampleModels(10);
var queryable = testList.AsQueryable();

// Act
var expected = queryable.Where(u => u.Roles.Count(r => r.Name.Contains(search)) > 0).ToArray();
var result = queryable.Where("Roles.Count(Name.Contains(@0)) > 0", search).ToArray();

Assert.Equal(expected, result);
}
}
}
}
Loading