Skip to content

Commit

Permalink
feat: Redefine query model
Browse files Browse the repository at this point in the history
  • Loading branch information
skarllot committed Aug 20, 2023
1 parent dd24f2d commit 160f0dc
Show file tree
Hide file tree
Showing 47 changed files with 389 additions and 171 deletions.
7 changes: 7 additions & 0 deletions Expressions.sln
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Common.Tests", "tests\Commo
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Expressions.Testing", "src\Expressions.Testing\Expressions.Testing.csproj", "{C981D09E-DFC0-4DB5-AFED-793251F72FE3}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Expressions.Reading.Tests", "tests\Expressions.Reading.Tests\Expressions.Reading.Tests.csproj", "{5E890439-44D5-4332-8269-4CA153834BF3}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand All @@ -64,6 +66,7 @@ Global
{C0BA44CB-2303-4BC0-A4BF-A0195E5CF6DC} = {D47941D6-CDE2-4B95-B20B-7FE8F94F772B}
{81C39727-4C58-4079-98DF-782073DBC61E} = {D47941D6-CDE2-4B95-B20B-7FE8F94F772B}
{C981D09E-DFC0-4DB5-AFED-793251F72FE3} = {4695B427-BD61-4FA2-A3F4-995AC92C9B02}
{5E890439-44D5-4332-8269-4CA153834BF3} = {D47941D6-CDE2-4B95-B20B-7FE8F94F772B}
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{CC54C0A1-1E81-4D00-9EB0-C2D7711A574D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
Expand Down Expand Up @@ -114,5 +117,9 @@ Global
{C981D09E-DFC0-4DB5-AFED-793251F72FE3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C981D09E-DFC0-4DB5-AFED-793251F72FE3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C981D09E-DFC0-4DB5-AFED-793251F72FE3}.Release|Any CPU.Build.0 = Release|Any CPU
{5E890439-44D5-4332-8269-4CA153834BF3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5E890439-44D5-4332-8269-4CA153834BF3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5E890439-44D5-4332-8269-4CA153834BF3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5E890439-44D5-4332-8269-4CA153834BF3}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

namespace Helpdesk.Relational.Incidents.GetDetails;

public class GetIncidentDetailsQueryModel : QueryModel<Incident, IncidentDetails>
public class GetIncidentDetailsQueryModel : EntityQueryModel<Incident, IncidentDetails>
{
public GetIncidentDetailsQueryModel(Guid incidentId) => IncidentId = incidentId;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

namespace Helpdesk.Relational.Incidents.GetShortInfo;

public class GetIncidentShortInfoQueryModel : QueryModel<Incident, IncidentShortInfo>
public class GetIncidentShortInfoQueryModel : EntityQueryModel<Incident, IncidentShortInfo>
{
public GetIncidentShortInfoQueryModel(Guid customerId, int pageNumber, int pageSize)
{
Expand Down
10 changes: 1 addition & 9 deletions src/Expressions.EntityFrameworkCore/Sessions/EfDbSession.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,15 +48,7 @@ await Context
.ConfigureAwait(false);
}

public IQuery<TResult> Query<TEntity, TResult>(IQueryModel<TEntity, TResult> queryModel)
where TEntity : class
{
return new EfQuery<TResult>(
_logger,
DataSourceFactory.GetDbSet<TEntity>(Context, Tracking).Apply(queryModel));
}

public IQuery<TResult> Query<TResult>(IMultiQueryModel<TResult> queryModel)
public IQuery<TResult> Query<TResult>(IQueryModel<TResult> queryModel)
{
return new EfQuery<TResult>(
_logger,
Expand Down
8 changes: 1 addition & 7 deletions src/Expressions.Marten/Sessions/MartenDbQuerySession.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,7 @@ public MartenDbQuerySession(ILogger<MartenDbQuerySession> logger, IQuerySession
_querySource = new MartenQuerySource(session);
}

public IQuery<TResult> Query<TEntity, TResult>(IQueryModel<TEntity, TResult> queryModel)
where TEntity : class
{
return new MartenQuery<TResult>(_logger, _session.Query<TEntity>().Apply(queryModel));
}

public IQuery<TResult> Query<TResult>(IMultiQueryModel<TResult> queryModel)
public IQuery<TResult> Query<TResult>(IQueryModel<TResult> queryModel)
{
return new MartenQuery<TResult>(_logger, queryModel.Execute(_querySource));
}
Expand Down
21 changes: 21 additions & 0 deletions src/Expressions.Reading/Queries/EntityQueryModel.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using Raiqub.Expressions.Queries.Internal;

namespace Raiqub.Expressions.Queries;

public static class EntityQueryModel
{
/// <summary>
/// Returns a query model for the specified type <typeparamref name="T"/> that does nothing.
/// </summary>
/// <typeparam name="T">The type of the elements of query model.</typeparam>
/// <returns>A query model for the specified type <typeparamref name="T"/>.</returns>
public static IEntityQueryModel<T> Create<T>() where T : class =>
AllQueryModel<T>.Instance;

public static IEntityQueryModel<T> Create<T>(Specification<T> specification) where T : class =>
new SpecificationQueryModel<T>(specification);

public static IEntityQueryModel<TSource, TResult> Create<TSource, TResult>(
Func<IQueryable<TSource>, IQueryable<TResult>> queryModel) where TSource : class =>
new AnonymousEntityQueryModel<TSource, TResult>(queryModel);
}
Original file line number Diff line number Diff line change
@@ -1,37 +1,39 @@
namespace Raiqub.Expressions.Queries;

/// <summary>
/// Represents a base implementation of the <see cref="IQueryModel{TSource, TResult}"/> interface that provides
/// Represents a base implementation of the <see cref="IEntityQueryModel{TSource,TResult}"/> interface that provides
/// a mechanism for defining and executing queries.
/// </summary>
/// <typeparam name="TSource">The type of the data source for the query model.</typeparam>
/// <typeparam name="TResult">The type of the query result.</typeparam>
public abstract class QueryModel<TSource, TResult> : IQueryModel<TSource, TResult>
public abstract class EntityQueryModel<TSource, TResult>
: IEntityQueryModel<TSource, TResult>, IQueryModel<TResult>
where TSource : class
{
private readonly IEnumerable<Specification<TSource>> _restrictions;

/// <summary>
/// Initializes a new instance of the <see cref="QueryModel{TSource, TResult}"/> class with no restrictions.
/// Initializes a new instance of the <see cref="EntityQueryModel{TSource,TResult}"/> class with no restrictions.
/// </summary>
protected QueryModel()
protected EntityQueryModel()
{
_restrictions = Enumerable.Empty<Specification<TSource>>();
}

/// <summary>
/// Initializes a new instance of the <see cref="QueryModel{TSource, TResult}"/> class with the specified restrictions.
/// Initializes a new instance of the <see cref="EntityQueryModel{TSource,TResult}"/> class with the specified restrictions.
/// </summary>
/// <param name="restrictions">The restrictions to apply to the query.</param>
protected QueryModel(params Specification<TSource>[] restrictions)
protected EntityQueryModel(params Specification<TSource>[] restrictions)
: this((IEnumerable<Specification<TSource>>)restrictions)
{
}

/// <summary>
/// Initializes a new instance of the <see cref="QueryModel{TSource, TResult}"/> class with the specified restrictions.
/// Initializes a new instance of the <see cref="EntityQueryModel{TSource,TResult}"/> class with the specified restrictions.
/// </summary>
/// <param name="restrictions">The restrictions to apply to the query.</param>
protected QueryModel(IEnumerable<Specification<TSource>> restrictions)
protected EntityQueryModel(IEnumerable<Specification<TSource>> restrictions)
{
_restrictions = restrictions.ToList();
}
Expand Down Expand Up @@ -59,6 +61,11 @@ public IEnumerable<TResult> Execute(IEnumerable<TSource> source)
return Execute(source.AsQueryable());
}

public IQueryable<TResult> Execute(IQuerySource source)
{
return Execute(source.GetSet<TSource>());
}

/// <summary>Gets the mandatory preconditions to execute the query.</summary>
/// <returns>An <see cref="IEnumerable{T}"/> that represents the preconditions for the query model.</returns>
protected virtual IEnumerable<Specification<TSource>> GetPreconditions() =>
Expand Down
7 changes: 7 additions & 0 deletions src/Expressions.Reading/Queries/EntityQueryModel{T}.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace Raiqub.Expressions.Queries;

public abstract class EntityQueryModel<T> : EntityQueryModel<T, T>, IEntityQueryModel<T>
where T : class
{
protected override IQueryable<T> ExecuteCore(IQueryable<T> source) => source;
}
Original file line number Diff line number Diff line change
@@ -1,29 +1,16 @@
namespace Raiqub.Expressions.Queries;
using Raiqub.Expressions.Queries.Internal;

public static class QueryModel
{
/// <summary>
/// Returns a query model for the specified type <typeparamref name="T"/> that does nothing.
/// </summary>
/// <typeparam name="T">The type of the elements of query model.</typeparam>
/// <returns>A query model for the specified type <typeparamref name="T"/>.</returns>
public static IQueryModel<T> Create<T>() =>
AllQueryModel<T>.Instance;

public static IQueryModel<T> Create<T>(Specification<T> specification) =>
new SpecificationQueryModel<T>(specification);

public static IQueryModel<TSource, TResult> Create<TSource, TResult>(
Func<IQueryable<TSource>, IQueryable<TResult>> queryModel) =>
new AnonymousQueryModel<TSource, TResult>(queryModel);
namespace Raiqub.Expressions.Queries;

public static class EntiyQueryModelExtensions
{
/// <summary>Prepares a query model for down-casting.</summary>
/// <typeparam name="TSource">The type of the data source.</typeparam>
/// <typeparam name="TResult">The type of the query result.</typeparam>
/// <param name="queryModel">The query model to downcast.</param>
/// <returns>An instance that allow down-casting the specified query model.</returns>
public static QueryModelDownCasting<TSource, TResult> DownCast<TSource, TResult>(
this IQueryModel<TSource, TResult> queryModel)
this IEntityQueryModel<TSource, TResult> queryModel)
where TSource : class, TResult => new(queryModel);

/// <summary>Prepares a query model for casting the source type.</summary>
Expand All @@ -32,5 +19,16 @@ public static QueryModelDownCasting<TSource, TResult> DownCast<TSource, TResult>
/// <param name="queryModel">The query model to cast.</param>
/// <returns>An instance that allow casting the source type of the specified query model.</returns>
public static QueryModelSourceCasting<TSource, TResult> SourceCast<TSource, TResult>(
this IQueryModel<TSource, TResult> queryModel) => new(queryModel);
this IEntityQueryModel<TSource, TResult> queryModel) => new(queryModel);

/// <summary>
/// Convert a <see cref="IEntityQueryModel{TSource,TResult}"/> to <see cref="IQueryModel{TResult}"/>.
/// </summary>
/// <param name="queryModel">The entity query model to convert.</param>
/// <typeparam name="TSource">The source type of the query model.</typeparam>
/// <typeparam name="TResult">The result type of the query model.</typeparam>
/// <returns>An instance of <see cref="IQueryModel{TResult}"/> representing the exact query model as specified by <paramref name="queryModel"/>.</returns>
public static IQueryModel<TResult> ToQueryModel<TSource, TResult>(
this IEntityQueryModel<TSource, TResult> queryModel) where TSource : class =>
queryModel as IQueryModel<TResult> ?? new EntityQueryModelWrapper<TSource, TResult>(queryModel);
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
/// </summary>
/// <typeparam name="TSource">The type of the data source.</typeparam>
/// <typeparam name="TResult">The type of the query result.</typeparam>
public interface IQueryModel<in TSource, out TResult>
public interface IEntityQueryModel<in TSource, out TResult>
{
/// <summary>
/// Executes the query on the specified data source of type <typeparamref name="TSource"/> and returns a query
Expand All @@ -30,6 +30,6 @@ public interface IQueryModel<in TSource, out TResult>
/// a result of the same type.
/// </summary>
/// <typeparam name="T">The type of the data source and the query result.</typeparam>
public interface IQueryModel<T> : IQueryModel<T, T>
public interface IEntityQueryModel<T> : IEntityQueryModel<T, T>
{
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
/// and return a result of type <typeparamref name="TResult"/>.
/// </summary>
/// <typeparam name="TResult">The type of the query result.</typeparam>
public interface IMultiQueryModel<out TResult>
public interface IQueryModel<out TResult>
{
/// <summary>
/// Executes the query using the specified query source and
Expand Down
13 changes: 13 additions & 0 deletions src/Expressions.Reading/Queries/Internal/AllQueryModel.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
namespace Raiqub.Expressions.Queries.Internal;

internal sealed class AllQueryModel<T> : IEntityQueryModel<T>, IQueryModel<T>
where T : class
{
public static readonly AllQueryModel<T> Instance = new AllQueryModel<T>();

public IQueryable<T> Execute(IQueryable<T> source) => source;

public IEnumerable<T> Execute(IEnumerable<T> source) => source;

public IQueryable<T> Execute(IQuerySource source) => source.GetSet<T>();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
namespace Raiqub.Expressions.Queries.Internal;

internal sealed class AnonymousEntityQueryModel<TSource, TResult>
: IEntityQueryModel<TSource, TResult>, IQueryModel<TResult>
where TSource : class
{
private readonly Func<IQueryable<TSource>, IQueryable<TResult>> _queryModel;

public AnonymousEntityQueryModel(Func<IQueryable<TSource>, IQueryable<TResult>> queryModel)
{
_queryModel = queryModel;
}

public IQueryable<TResult> Execute(IQueryable<TSource> source) => _queryModel(source);

public IEnumerable<TResult> Execute(IEnumerable<TSource> source) => _queryModel(source.AsQueryable());

public IQueryable<TResult> Execute(IQuerySource source) => _queryModel(source.GetSet<TSource>());
}
10 changes: 10 additions & 0 deletions src/Expressions.Reading/Queries/Internal/AnonymousQueryModel.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
namespace Raiqub.Expressions.Queries.Internal;

internal sealed class AnonymousQueryModel<TResult> : IQueryModel<TResult>
{
private readonly Func<IQuerySource, IQueryable<TResult>> _queryModel;

public AnonymousQueryModel(Func<IQuerySource, IQueryable<TResult>> queryModel) => _queryModel = queryModel;

public IQueryable<TResult> Execute(IQuerySource source) => _queryModel(source);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
namespace Raiqub.Expressions.Queries.Internal;

internal sealed class DerivedEntityQueryModel<TParent, TDerived>
: IEntityQueryModel<TDerived>, IQueryModel<TDerived>
where TDerived : class, TParent
{
private readonly IEntityQueryModel<TDerived, TParent> _originalQueryModel;

public DerivedEntityQueryModel(IEntityQueryModel<TDerived, TParent> originalQueryModel) =>
_originalQueryModel = originalQueryModel;

public IQueryable<TDerived> Execute(IQueryable<TDerived> source) =>
_originalQueryModel.Execute(source).OfType<TDerived>();

public IEnumerable<TDerived> Execute(IEnumerable<TDerived> source) =>
_originalQueryModel.Execute(source).OfType<TDerived>();

public IQueryable<TDerived> Execute(IQuerySource source) =>
Execute(source.GetSet<TDerived>());
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
namespace Raiqub.Expressions.Queries.Internal;

internal sealed class EntityQueryModelWrapper<TSource, TResult> : IQueryModel<TResult>
where TSource : class
{
private readonly IEntityQueryModel<TSource, TResult> _entityQueryModel;

public EntityQueryModelWrapper(IEntityQueryModel<TSource, TResult> entityQueryModel)
{
_entityQueryModel = entityQueryModel;
}

public IQueryable<TResult> Execute(IQuerySource source) => source.GetSetUsing(_entityQueryModel);
}
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
using System.Linq.Expressions;

namespace Raiqub.Expressions.Queries;
namespace Raiqub.Expressions.Queries.Internal;

internal sealed class NestingQueryModel<TEntity, TNested, TResult> : IQueryModel<TEntity, TResult>
internal sealed class NestingEntityQueryModel<TEntity, TNested, TResult>
: IEntityQueryModel<TEntity, TResult>, IQueryModel<TResult>
where TEntity : class
{
private readonly Expression<Func<TEntity, IEnumerable<TNested>>> _selector;
private readonly IQueryModel<TNested, TResult> _nestedQueryModel;
private readonly IEntityQueryModel<TNested, TResult> _nestedQueryModel;

public NestingQueryModel(
public NestingEntityQueryModel(
Expression<Func<TEntity, IEnumerable<TNested>>> selector,
IQueryModel<TNested, TResult> nestedQueryModel)
IEntityQueryModel<TNested, TResult> nestedQueryModel)
{
_selector = selector;
_nestedQueryModel = nestedQueryModel;
Expand All @@ -20,4 +22,6 @@ public IQueryable<TResult> Execute(IQueryable<TEntity> source) =>

public IEnumerable<TResult> Execute(IEnumerable<TEntity> source) =>
_nestedQueryModel.Execute(source.SelectMany(_selector.Compile()));

public IQueryable<TResult> Execute(IQuerySource source) => Execute(source.GetSet<TEntity>());
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
namespace Raiqub.Expressions.Queries;
namespace Raiqub.Expressions.Queries.Internal;

internal sealed class SpecificationQueryModel<T> : IQueryModel<T>
internal sealed class SpecificationQueryModel<T> : IEntityQueryModel<T>, IQueryModel<T>
where T : class
{
private readonly Specification<T> _specification;

Expand All @@ -9,4 +10,6 @@ internal sealed class SpecificationQueryModel<T> : IQueryModel<T>
public IQueryable<T> Execute(IQueryable<T> source) => source.Where(_specification);

public IEnumerable<T> Execute(IEnumerable<T> source) => source.Where(_specification);

public IQueryable<T> Execute(IQuerySource source) => source.GetSet<T>().Where(_specification);
}
9 changes: 9 additions & 0 deletions src/Expressions.Reading/Queries/QueryModel.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using Raiqub.Expressions.Queries.Internal;

namespace Raiqub.Expressions.Queries;

public static class QueryModel
{
public static IQueryModel<TResult> Create<TResult>(Func<IQuerySource, IQueryable<TResult>> queryModel) =>
new AnonymousQueryModel<TResult>(queryModel);
}
Loading

0 comments on commit 160f0dc

Please sign in to comment.