Skip to content
This repository has been archived by the owner on May 23, 2024. It is now read-only.

Commit

Permalink
Added possibility to chain includes.
Browse files Browse the repository at this point in the history
  • Loading branch information
mrukas committed Dec 3, 2019
1 parent 29d1497 commit c3193b1
Show file tree
Hide file tree
Showing 6 changed files with 131 additions and 2 deletions.
20 changes: 20 additions & 0 deletions src/ApplicationCore/Helpers/Query/IncludeAggregator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Linq.Expressions;

namespace Microsoft.eShopWeb.ApplicationCore.Helpers.Query
{
public class IncludeAggregator<TEntity>
{
public IncludeQuery<TEntity, TProperty> Include<TProperty>(Expression<Func<TEntity, TProperty>> selector)
{
var visitor = new IncludeVisitor();
visitor.Visit(selector);

var id = Guid.NewGuid();
var pathMap = new Dictionary<Guid, string>() { { id, visitor.Path } };

return new IncludeQuery<TEntity, TProperty>(id, pathMap);
}
}
}
67 changes: 67 additions & 0 deletions src/ApplicationCore/Helpers/Query/IncludeQuery.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
using Microsoft.eShopWeb.ApplicationCore.Interfaces;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;

namespace Microsoft.eShopWeb.ApplicationCore.Helpers.Query
{
public class IncludeQuery<TEntity> : IIncludeQuery<TEntity>
{
public Guid Id { get; }
public Dictionary<Guid, string> PathMap { get; } = new Dictionary<Guid, string>();
public IncludeVisitor Visitor { get; } = new IncludeVisitor();

public IncludeQuery(Guid id, Dictionary<Guid, string> pathMap)
{
Id = id;
PathMap = pathMap;
}

public IEnumerable<string> Paths => PathMap.Select(x => x.Value).Distinct().ToList();
}

public class IncludeQuery<TEntity, TPreviousProperty> : IncludeQuery<TEntity>, IIncludeQuery<TEntity, TPreviousProperty>
{
public IncludeQuery(Guid id, Dictionary<Guid, string> pathMap) : base(id, pathMap) { }
}

public static class IncludeableQueryExtensions
{
public static IIncludeQuery<TEntity, TNewProperty> Include<TEntity, TPreviousProperty, TNewProperty>(
this IIncludeQuery<TEntity, TPreviousProperty> query,
Expression<Func<TEntity, TNewProperty>> selector)
{
query.Visitor.Visit(selector);

var id = Guid.NewGuid();
query.PathMap[id] = query.Visitor.Path;

return new IncludeQuery<TEntity, TNewProperty>(id, query.PathMap);
}

public static IIncludeQuery<TEntity, TNewProperty> ThenInclude<TEntity, TPreviousProperty, TNewProperty>(
this IIncludeQuery<TEntity, TPreviousProperty> query,
Expression<Func<TPreviousProperty, TNewProperty>> selector)
{
query.Visitor.Visit(selector);

var existingPath = query.PathMap[query.Id];
query.PathMap[query.Id] = $"{existingPath}.{query.Visitor.Path}";

return new IncludeQuery<TEntity, TNewProperty>(query.Id, query.PathMap);
}

public static IIncludeQuery<TEntity, TNewProperty> ThenInclude<TEntity, TPreviousProperty, TNewProperty>(
this IIncludeQuery<TEntity, IEnumerable<TPreviousProperty>> query,
Expression<Func<TPreviousProperty, TNewProperty>> selector)
{
query.Visitor.Visit(selector);

var existingPath = query.PathMap[query.Id];
query.PathMap[query.Id] = $"{existingPath}.{query.Visitor.Path}";

return new IncludeQuery<TEntity, TNewProperty>(query.Id, query.PathMap);
}
}
}
16 changes: 16 additions & 0 deletions src/ApplicationCore/Helpers/Query/IncludeVisitor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using System.Linq.Expressions;

namespace Microsoft.eShopWeb.ApplicationCore.Helpers.Query
{
public class IncludeVisitor : ExpressionVisitor
{
public string Path { get; private set; } = string.Empty;

protected override Expression VisitMember(MemberExpression node)
{
Path = string.IsNullOrEmpty(Path) ? node.Member.Name : $"{Path}.{node.Member.Name}";

return base.VisitMember(node);
}
}
}
18 changes: 18 additions & 0 deletions src/ApplicationCore/Interfaces/IIncludeQuery.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using Microsoft.eShopWeb.ApplicationCore.Helpers.Query;
using System;
using System.Collections.Generic;

namespace Microsoft.eShopWeb.ApplicationCore.Interfaces
{
public interface IIncludeQuery<TEntity>
{
Guid Id { get; }
Dictionary<Guid, string> PathMap { get; }
IncludeVisitor Visitor { get; }
IEnumerable<string> Paths { get; }
}

public interface IIncludeQuery<TEntity, out TPreviousProperty> : IIncludeQuery<TEntity>
{
}
}
8 changes: 8 additions & 0 deletions src/ApplicationCore/Specifications/BaseSpecification.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System;
using System.Linq.Expressions;
using System.Collections.Generic;
using Microsoft.eShopWeb.ApplicationCore.Helpers.Query;

namespace Microsoft.eShopWeb.ApplicationCore.Specifications
{
Expand All @@ -26,6 +27,13 @@ protected virtual void AddInclude(Expression<Func<T, object>> includeExpression)
{
Includes.Add(includeExpression);
}

protected virtual void AddIncludes(Func<IncludeAggregator<T>, IIncludeQuery<T>> includeGenerator)
{
var includeQuery = includeGenerator(new IncludeAggregator<T>());
IncludeStrings.AddRange(includeQuery.Paths);
}

protected virtual void AddInclude(string includeString)
{
IncludeStrings.Add(includeString);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Microsoft.eShopWeb.ApplicationCore.Entities.OrderAggregate;
using Microsoft.eShopWeb.ApplicationCore.Helpers.Query;

namespace Microsoft.eShopWeb.ApplicationCore.Specifications
{
Expand All @@ -7,8 +8,7 @@ public class CustomerOrdersWithItemsSpecification : BaseSpecification<Order>
public CustomerOrdersWithItemsSpecification(string buyerId)
: base(o => o.BuyerId == buyerId)
{
AddInclude(o => o.OrderItems);
AddInclude($"{nameof(Order.OrderItems)}.{nameof(OrderItem.ItemOrdered)}");
AddIncludes(query => query.Include(o => o.OrderItems).ThenInclude(i => i.ItemOrdered));
}
}
}

0 comments on commit c3193b1

Please sign in to comment.