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
26 changes: 0 additions & 26 deletions SubSonic.Tests/DAL/Asynchronous/AsynchronousTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,32 +19,6 @@ namespace SubSonic.Tests.DAL
public class AsynchronousTests
: BaseTestFixture
{
public override void SetupTestFixture()
{
base.SetupTestFixture();

string
people_all = @"SELECT [T1].[ID], [T1].[FirstName], [T1].[MiddleInitial], [T1].[FamilyName], [T1].[FullName]
FROM [dbo].[Person] AS [T1]",
people_all_count = @"SELECT COUNT([T1].[ID])
FROM [dbo].[Person] AS [T1]",
people_equal = @"SELECT [T1].[ID], [T1].[FirstName], [T1].[MiddleInitial], [T1].[FamilyName], [T1].[FullName]
FROM [dbo].[Person] AS [T1]
WHERE ([T1].[ID] = @id_1)",
people_greater_than = @"SELECT [T1].[ID], [T1].[FirstName], [T1].[MiddleInitial], [T1].[FamilyName], [T1].[FullName]
FROM [dbo].[Person] AS [T1]
WHERE ([T1].[ID] > @id_1)",
people_less_than = @"SELECT [T1].[ID], [T1].[FirstName], [T1].[MiddleInitial], [T1].[FamilyName], [T1].[FullName]
FROM [dbo].[Person] AS [T1]
WHERE ([T1].[ID] < @id_1)";

Context.Database.Instance.AddCommandBehavior(people_all, cmd => People.ToDataTable());
Context.Database.Instance.AddCommandBehavior(people_all_count, cmd => People.Count());
Context.Database.Instance.AddCommandBehavior(people_greater_than, cmd => People.Where(x => x.ID > cmd.Parameters["@id_1"].GetValue<int>()).ToDataTable());
Context.Database.Instance.AddCommandBehavior(people_equal, cmd => People.Where(x => x.ID == cmd.Parameters["@id_1"].GetValue<int>()).ToDataTable());
Context.Database.Instance.AddCommandBehavior(people_less_than, cmd => People.Where(x => x.ID < cmd.Parameters["@id_1"].GetValue<int>()).ToDataTable());
}

[Test]
public async Task ShouldBeAbleToGetSingleAsync()
{
Expand Down
29 changes: 29 additions & 0 deletions SubSonic.Tests/DAL/ExtensionMethod/ExtensionMethodTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using FluentAssertions;
using NUnit.Framework;
using SubSonic.Tests.DAL.SUT;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SubSonic.Tests.DAL.ExtensionMethod
{
[TestFixture]
public class ExtensionMethodTests
: BaseTestFixture
{
[Test]
public void TheCountMethodIsSupported()
{
Context.People.Count().Should().BeGreaterThan(0).And.IsOfType<int>();
}

[Test]
public void TheLongCountMethodIsSupported()
{
Context.People.LongCount().Should().BeGreaterThan(0).And.IsOfType<long>();
}
}
}
32 changes: 31 additions & 1 deletion SubSonic.Tests/DAL/SUT/BaseTestFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using System.Collections;
using System.Collections.Generic;
using System.Data.Common;
using System.Linq;

namespace SubSonic.Tests.DAL.SUT
{
Expand Down Expand Up @@ -56,6 +57,8 @@ public virtual void SetupTestFixture()

SetInsertBehaviors();

SetSelectBehaviors();

Statuses = new List<Status>()
{
new Status() { ID = 1, Name = "Vacant", IsAvailableStatus = true },
Expand Down Expand Up @@ -97,7 +100,34 @@ public virtual void SetupTestFixture()
};
}

private void SetInsertBehaviors()
protected virtual void SetSelectBehaviors()
{
string
people_all = @"SELECT [T1].[ID], [T1].[FirstName], [T1].[MiddleInitial], [T1].[FamilyName], [T1].[FullName]
FROM [dbo].[Person] AS [T1]",
people_all_count = @"SELECT COUNT([T1].[ID])
FROM [dbo].[Person] AS [T1]",
people_all_long_count = @"SELECT COUNT_BIG([T1].[ID])
FROM [dbo].[Person] AS [T1]",
people_equal = @"SELECT [T1].[ID], [T1].[FirstName], [T1].[MiddleInitial], [T1].[FamilyName], [T1].[FullName]
FROM [dbo].[Person] AS [T1]
WHERE ([T1].[ID] = @id_1)",
people_greater_than = @"SELECT [T1].[ID], [T1].[FirstName], [T1].[MiddleInitial], [T1].[FamilyName], [T1].[FullName]
FROM [dbo].[Person] AS [T1]
WHERE ([T1].[ID] > @id_1)",
people_less_than = @"SELECT [T1].[ID], [T1].[FirstName], [T1].[MiddleInitial], [T1].[FamilyName], [T1].[FullName]
FROM [dbo].[Person] AS [T1]
WHERE ([T1].[ID] < @id_1)";

Context.Database.Instance.AddCommandBehavior(people_all, cmd => People.ToDataTable());
Context.Database.Instance.AddCommandBehavior(people_all_count, cmd => People.Count());
Context.Database.Instance.AddCommandBehavior(people_all_long_count, cmd => People.LongCount());
Context.Database.Instance.AddCommandBehavior(people_greater_than, cmd => People.Where(x => x.ID > cmd.Parameters["@id_1"].GetValue<int>()).ToDataTable());
Context.Database.Instance.AddCommandBehavior(people_equal, cmd => People.Where(x => x.ID == cmd.Parameters["@id_1"].GetValue<int>()).ToDataTable());
Context.Database.Instance.AddCommandBehavior(people_less_than, cmd => People.Where(x => x.ID < cmd.Parameters["@id_1"].GetValue<int>()).ToDataTable());
}

protected virtual void SetInsertBehaviors()
{
string
insert_person = @"INSERT INTO [dbo].[Person]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,97 @@ public IQueryable<TEntity> CreateQuery<TEntity>(Expression expression)
return new SubSonicCollection<TEntity>(this, BuildQuery(expression));
}

public TResult ExecuteMethod<TResult>(MethodCallExpression call)
{
if (call is null)
{
throw Error.ArgumentNull(nameof(call));
}

DbSelectExpression dbSelect = null;
Expression where = null;

for (int i = 0, n = call.Arguments.Count; i < n; i++)
{
if (call.Arguments[i] is DbSelectExpression select)
{
dbSelect = select;
}
else if (call.Arguments[i] is UnaryExpression unary)
{
if (unary.Operand is LambdaExpression lambda)
{
where = BuildWhere(dbSelect, lambda);
}
}
}

if (call.Method.Name.In(nameof(Queryable.Single), nameof(Queryable.SingleOrDefault), nameof(Queryable.First), nameof(Queryable.FirstOrDefault)))
{
object result = Execute<TResult>(BuildSelect(dbSelect, where));

if (result is TResult matched)
{
return matched;
}
else if (result is IEnumerable<TResult> enumerable)
{
return enumerable.Any() ? enumerable.ElementAt(0) : default(TResult);
}
#if NETSTANDARD2_0
else if (call.Method.Name.Contains("Default"))
#elif NETSTANDARD2_1
else if (call.Method.Name.Contains("Default", StringComparison.CurrentCulture))
#endif
{
return default(TResult);
}
else
{
throw Error.InvalidOperation($"Method {call.Method.Name} expects data.");
}
}
else if (call.Method.Name.In(nameof(Queryable.Count), nameof(Queryable.LongCount)))
{
if (BuildSelect(dbSelect, where) is DbSelectExpression select)
{
if (!Enum.TryParse<AggregateType>(call.Method.Name, out AggregateType aggregateType))
{
throw Error.NotSupported(SubSonicErrorMessages.MethodNotSupported.Format(call.Method.Name));
}

TResult result = Execute<TResult>(DbExpression.DbSelectAggregate(select, new[]
{
DbExpression.DbAggregate(typeof(TResult), aggregateType, select.Columns.First(x => x.Property.IsPrimaryKey).Expression)
}));

if (select.Take is ConstantExpression take)
{
if (result.IsIntGreaterThan(take.Value))
{
return (TResult)Convert.ChangeType(take.Value, typeof(TResult), CultureInfo.InvariantCulture);
}
}

return result;
}
}

throw Error.NotSupported(SubSonicErrorMessages.ExpressionNotSupported.Format(call.Method.Name));
}

public TResult Execute<TResult>(Expression expression)
{
if (expression is null)
{
throw new ArgumentNullException(nameof(expression));
}

if (expression is DbExpression query)
if (expression is MethodCallExpression call)
{ // execution request originates from the System.Linq namespace
return ExecuteMethod<TResult>(call);
}
else if (expression is DbExpression query)
{ // execution request is from the subsonic namespace
using (SharedDbConnectionScope Scope = DbContext.ServiceProvider.GetService<SharedDbConnectionScope>())
{
Expand Down Expand Up @@ -97,75 +180,6 @@ public TResult Execute<TResult>(Expression expression)
}
}
}
else if (expression is MethodCallExpression call)
{ // execution request originates from the System.Linq namespace

DbSelectExpression dbSelect = null;
Expression where = null;

for (int i = 0, n = call.Arguments.Count; i < n; i++)
{
if (call.Arguments[i] is DbSelectExpression select)
{
dbSelect = select;
}
else if (call.Arguments[i] is UnaryExpression unary)
{
if (unary.Operand is LambdaExpression lambda)
{
where = BuildWhere(dbSelect, lambda);
}
}
}

if (call.Method.Name.In(nameof(Queryable.Single), nameof(Queryable.SingleOrDefault), nameof(Queryable.First), nameof(Queryable.FirstOrDefault)))
{
object result = Execute<TResult>(BuildSelect(dbSelect, where));

if (result is TResult matched)
{
return matched;
}
else if (result is IEnumerable<TResult> enumerable)
{
return enumerable.Any() ? enumerable.ElementAt(0) : default(TResult);
}
#if NETSTANDARD2_0
else if (call.Method.Name.Contains("Default"))
#elif NETSTANDARD2_1
else if (call.Method.Name.Contains("Default", StringComparison.CurrentCulture))
#endif
{
return default(TResult);
}
else
{
throw Error.InvalidOperation($"Method {call.Method.Name} expects data.");
}
}
else if (call.Method.Name.In(nameof(Queryable.Count)))
{
if (BuildSelect(dbSelect, where) is DbSelectExpression select)
{
TResult result = Execute<TResult>(DbExpression.DbSelectAggregate(select, new[]
{
DbExpression.DbAggregate(typeof(TResult), AggregateType.Count, select.Columns.First(x => x.Property.IsPrimaryKey).Expression)
}));

if (select.Take is ConstantExpression take)
{
if (result.IsIntGreaterThan(take.Value))
{
return (TResult)Convert.ChangeType(take.Value, typeof(TResult), CultureInfo.InvariantCulture);
}
}

return result;
}
}

throw Error.NotSupported(SubSonicErrorMessages.ExpressionNotSupported.Format(call.Method.Name));
}

throw new NotSupportedException(expression.ToString());
}
Expand Down
Loading