Added possibility to chain includes. (#331)
* Added possibility to chain includes. * Removed interface. * Removed the need for generating GUIDs as ids and added tests.
This commit is contained in:
26
src/ApplicationCore/Helpers/Query/IncludeAggregator.cs
Normal file
26
src/ApplicationCore/Helpers/Query/IncludeAggregator.cs
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
using Microsoft.eShopWeb.ApplicationCore.Interfaces;
|
||||||
|
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 pathMap = new Dictionary<IIncludeQuery, string>();
|
||||||
|
var query = new IncludeQuery<TEntity, TProperty>(pathMap);
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(visitor.Path))
|
||||||
|
{
|
||||||
|
pathMap[query] = visitor.Path;
|
||||||
|
}
|
||||||
|
|
||||||
|
return query;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
19
src/ApplicationCore/Helpers/Query/IncludeQuery.cs
Normal file
19
src/ApplicationCore/Helpers/Query/IncludeQuery.cs
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
using Microsoft.eShopWeb.ApplicationCore.Interfaces;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace Microsoft.eShopWeb.ApplicationCore.Helpers.Query
|
||||||
|
{
|
||||||
|
public class IncludeQuery<TEntity, TPreviousProperty> : IIncludeQuery<TEntity, TPreviousProperty>
|
||||||
|
{
|
||||||
|
public Dictionary<IIncludeQuery, string> PathMap { get; } = new Dictionary<IIncludeQuery, string>();
|
||||||
|
public IncludeVisitor Visitor { get; } = new IncludeVisitor();
|
||||||
|
|
||||||
|
public IncludeQuery(Dictionary<IIncludeQuery, string> pathMap)
|
||||||
|
{
|
||||||
|
PathMap = pathMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
public HashSet<string> Paths => PathMap.Select(x => x.Value).ToHashSet();
|
||||||
|
}
|
||||||
|
}
|
||||||
66
src/ApplicationCore/Helpers/Query/IncludeQueryExtensions.cs
Normal file
66
src/ApplicationCore/Helpers/Query/IncludeQueryExtensions.cs
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
using Microsoft.eShopWeb.ApplicationCore.Interfaces;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq.Expressions;
|
||||||
|
|
||||||
|
namespace Microsoft.eShopWeb.ApplicationCore.Helpers.Query
|
||||||
|
{
|
||||||
|
public static class IncludeQueryExtensions
|
||||||
|
{
|
||||||
|
public static IIncludeQuery<TEntity, TNewProperty> Include<TEntity, TPreviousProperty, TNewProperty>(
|
||||||
|
this IIncludeQuery<TEntity, TPreviousProperty> query,
|
||||||
|
Expression<Func<TEntity, TNewProperty>> selector)
|
||||||
|
{
|
||||||
|
query.Visitor.Visit(selector);
|
||||||
|
|
||||||
|
var includeQuery = new IncludeQuery<TEntity, TNewProperty>(query.PathMap);
|
||||||
|
query.PathMap[includeQuery] = query.Visitor.Path;
|
||||||
|
|
||||||
|
return includeQuery;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IIncludeQuery<TEntity, TNewProperty> ThenInclude<TEntity, TPreviousProperty, TNewProperty>(
|
||||||
|
this IIncludeQuery<TEntity, TPreviousProperty> query,
|
||||||
|
Expression<Func<TPreviousProperty, TNewProperty>> selector)
|
||||||
|
{
|
||||||
|
query.Visitor.Visit(selector);
|
||||||
|
|
||||||
|
// If the visitor did not generated a path, return a new IncludeQuery with an unmodified PathMap.
|
||||||
|
if (string.IsNullOrEmpty(query.Visitor.Path))
|
||||||
|
{
|
||||||
|
return new IncludeQuery<TEntity, TNewProperty>(query.PathMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
var pathMap = query.PathMap;
|
||||||
|
var existingPath = pathMap[query];
|
||||||
|
pathMap.Remove(query);
|
||||||
|
|
||||||
|
var includeQuery = new IncludeQuery<TEntity, TNewProperty>(query.PathMap);
|
||||||
|
pathMap[includeQuery] = $"{existingPath}.{query.Visitor.Path}";
|
||||||
|
|
||||||
|
return includeQuery;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IIncludeQuery<TEntity, TNewProperty> ThenInclude<TEntity, TPreviousProperty, TNewProperty>(
|
||||||
|
this IIncludeQuery<TEntity, IEnumerable<TPreviousProperty>> query,
|
||||||
|
Expression<Func<TPreviousProperty, TNewProperty>> selector)
|
||||||
|
{
|
||||||
|
query.Visitor.Visit(selector);
|
||||||
|
|
||||||
|
// If the visitor did not generated a path, return a new IncludeQuery with an unmodified PathMap.
|
||||||
|
if (string.IsNullOrEmpty(query.Visitor.Path))
|
||||||
|
{
|
||||||
|
return new IncludeQuery<TEntity, TNewProperty>(query.PathMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
var pathMap = query.PathMap;
|
||||||
|
var existingPath = pathMap[query];
|
||||||
|
pathMap.Remove(query);
|
||||||
|
|
||||||
|
var includeQuery = new IncludeQuery<TEntity, TNewProperty>(query.PathMap);
|
||||||
|
pathMap[includeQuery] = $"{existingPath}.{query.Visitor.Path}";
|
||||||
|
|
||||||
|
return includeQuery;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
16
src/ApplicationCore/Helpers/Query/IncludeVisitor.cs
Normal file
16
src/ApplicationCore/Helpers/Query/IncludeVisitor.cs
Normal file
@@ -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 : $"{node.Member.Name}.{Path}";
|
||||||
|
|
||||||
|
return base.VisitMember(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
16
src/ApplicationCore/Interfaces/IIncludeQuery.cs
Normal file
16
src/ApplicationCore/Interfaces/IIncludeQuery.cs
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
using Microsoft.eShopWeb.ApplicationCore.Helpers.Query;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Microsoft.eShopWeb.ApplicationCore.Interfaces
|
||||||
|
{
|
||||||
|
public interface IIncludeQuery
|
||||||
|
{
|
||||||
|
Dictionary<IIncludeQuery, string> PathMap { get; }
|
||||||
|
IncludeVisitor Visitor { get; }
|
||||||
|
HashSet<string> Paths { get; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface IIncludeQuery<TEntity, out TPreviousProperty> : IIncludeQuery
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Linq.Expressions;
|
using System.Linq.Expressions;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using Microsoft.eShopWeb.ApplicationCore.Helpers.Query;
|
||||||
|
|
||||||
namespace Microsoft.eShopWeb.ApplicationCore.Specifications
|
namespace Microsoft.eShopWeb.ApplicationCore.Specifications
|
||||||
{
|
{
|
||||||
@@ -26,6 +27,13 @@ namespace Microsoft.eShopWeb.ApplicationCore.Specifications
|
|||||||
{
|
{
|
||||||
Includes.Add(includeExpression);
|
Includes.Add(includeExpression);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected virtual void AddIncludes<TProperty>(Func<IncludeAggregator<T>, IIncludeQuery<T, TProperty>> includeGenerator)
|
||||||
|
{
|
||||||
|
var includeQuery = includeGenerator(new IncludeAggregator<T>());
|
||||||
|
IncludeStrings.AddRange(includeQuery.Paths);
|
||||||
|
}
|
||||||
|
|
||||||
protected virtual void AddInclude(string includeString)
|
protected virtual void AddInclude(string includeString)
|
||||||
{
|
{
|
||||||
IncludeStrings.Add(includeString);
|
IncludeStrings.Add(includeString);
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using Microsoft.eShopWeb.ApplicationCore.Entities.OrderAggregate;
|
using Microsoft.eShopWeb.ApplicationCore.Entities.OrderAggregate;
|
||||||
|
using Microsoft.eShopWeb.ApplicationCore.Helpers.Query;
|
||||||
|
|
||||||
namespace Microsoft.eShopWeb.ApplicationCore.Specifications
|
namespace Microsoft.eShopWeb.ApplicationCore.Specifications
|
||||||
{
|
{
|
||||||
@@ -7,8 +8,7 @@ namespace Microsoft.eShopWeb.ApplicationCore.Specifications
|
|||||||
public CustomerOrdersWithItemsSpecification(string buyerId)
|
public CustomerOrdersWithItemsSpecification(string buyerId)
|
||||||
: base(o => o.BuyerId == buyerId)
|
: base(o => o.BuyerId == buyerId)
|
||||||
{
|
{
|
||||||
AddInclude(o => o.OrderItems);
|
AddIncludes(query => query.Include(o => o.OrderItems).ThenInclude(i => i.ItemOrdered));
|
||||||
AddInclude($"{nameof(Order.OrderItems)}.{nameof(OrderItem.ItemOrdered)}");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
16
tests/UnitTests/ApplicationCore/Helpers/Query/Book.cs
Normal file
16
tests/UnitTests/ApplicationCore/Helpers/Query/Book.cs
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Microsoft.eShopWeb.UnitTests.ApplicationCore.Helpers.Query
|
||||||
|
{
|
||||||
|
public class Book
|
||||||
|
{
|
||||||
|
public string Title { get; set; }
|
||||||
|
public DateTime PublishingDate { get; set; }
|
||||||
|
public Person Author { get; set; }
|
||||||
|
|
||||||
|
public int GetNumberOfSales()
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
using Microsoft.eShopWeb.ApplicationCore.Helpers.Query;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.eShopWeb.UnitTests.ApplicationCore.Helpers.Query.IncludeAggregatorTests
|
||||||
|
{
|
||||||
|
public class Include
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void Should_ReturnIncludeQueryWithCorrectPath_IfIncludeSimpleType()
|
||||||
|
{
|
||||||
|
var includeAggregator = new IncludeAggregator<Person>();
|
||||||
|
|
||||||
|
// There may be ORM libraries where including a simple type makes sense.
|
||||||
|
var includeQuery = includeAggregator.Include(p => p.Age);
|
||||||
|
|
||||||
|
Assert.Contains(includeQuery.Paths, path => path == nameof(Person.Age));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Should_ReturnIncludeQueryWithCorrectPath_IfIncludeFunction()
|
||||||
|
{
|
||||||
|
var includeAggregator = new IncludeAggregator<Person>();
|
||||||
|
|
||||||
|
// This include does not make much sense, but it should at least do not modify the paths.
|
||||||
|
var includeQuery = includeAggregator.Include(p => p.FavouriteBook.GetNumberOfSales());
|
||||||
|
|
||||||
|
Assert.Contains(includeQuery.Paths, path => path == nameof(Person.FavouriteBook));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Should_ReturnIncludeQueryWithCorrectPath_IfIncludeObject()
|
||||||
|
{
|
||||||
|
var includeAggregator = new IncludeAggregator<Person>();
|
||||||
|
var includeQuery = includeAggregator.Include(p => p.FavouriteBook.Author);
|
||||||
|
|
||||||
|
Assert.Contains(includeQuery.Paths, path => path == $"{nameof(Person.FavouriteBook)}.{nameof(Book.Author)}");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Should_ReturnIncludeQueryWithCorrectPath_IfIncludeCollection()
|
||||||
|
{
|
||||||
|
var includeAggregator = new IncludeAggregator<Book>();
|
||||||
|
var includeQuery = includeAggregator.Include(o => o.Author.Friends);
|
||||||
|
|
||||||
|
Assert.Contains(includeQuery.Paths, path => path == $"{nameof(Book.Author)}.{nameof(Person.Friends)}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
using Microsoft.eShopWeb.ApplicationCore.Helpers.Query;
|
||||||
|
using Microsoft.eShopWeb.UnitTests.Builders;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.eShopWeb.UnitTests.ApplicationCore.Helpers.Query.IncludeQueryTests
|
||||||
|
{
|
||||||
|
public class Include
|
||||||
|
{
|
||||||
|
private IncludeQueryBuilder _includeQueryBuilder = new IncludeQueryBuilder();
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Should_ReturnIncludeQueryWithCorrectPath_IfIncludeSimpleType()
|
||||||
|
{
|
||||||
|
var includeQuery = _includeQueryBuilder.WithObjectAsPreviousProperty();
|
||||||
|
|
||||||
|
// There may be ORM libraries where including a simple type makes sense.
|
||||||
|
var newIncludeQuery = includeQuery.Include(b => b.Title);
|
||||||
|
|
||||||
|
Assert.Contains(newIncludeQuery.Paths, path => path == nameof(Book.Title));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Should_ReturnIncludeQueryWithCorrectPath_IfIncludeFunction()
|
||||||
|
{
|
||||||
|
var includeQuery = _includeQueryBuilder.WithObjectAsPreviousProperty();
|
||||||
|
|
||||||
|
// This include does not make much sense, but it should at least do not modify paths.
|
||||||
|
var newIncludeQuery = includeQuery.Include(b => b.GetNumberOfSales());
|
||||||
|
|
||||||
|
// The resulting paths should not include number of sales.
|
||||||
|
Assert.DoesNotContain(newIncludeQuery.Paths, path => path == nameof(Book.GetNumberOfSales));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Should_ReturnIncludeQueryWithCorrectPath_IfIncludeObject()
|
||||||
|
{
|
||||||
|
var includeQuery = _includeQueryBuilder.WithObjectAsPreviousProperty();
|
||||||
|
var newIncludeQuery = includeQuery.Include(b => b.Author);
|
||||||
|
|
||||||
|
Assert.Contains(newIncludeQuery.Paths, path => path == nameof(Book.Author));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Should_ReturnIncludeQueryWithCorrectPath_IfIncludeCollection()
|
||||||
|
{
|
||||||
|
var includeQuery = _includeQueryBuilder.WithObjectAsPreviousProperty();
|
||||||
|
|
||||||
|
var newIncludeQuery = includeQuery.Include(b => b.Author.Friends);
|
||||||
|
var expectedPath = $"{nameof(Book.Author)}.{nameof(Person.Friends)}";
|
||||||
|
|
||||||
|
Assert.Contains(newIncludeQuery.Paths, path => path == expectedPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Should_IncreaseNumberOfPathsByOne()
|
||||||
|
{
|
||||||
|
var includeQuery = _includeQueryBuilder.WithObjectAsPreviousProperty();
|
||||||
|
var numberOfPathsBeforeInclude = includeQuery.Paths.Count;
|
||||||
|
|
||||||
|
var newIncludeQuery = includeQuery.Include(b => b.Author.Friends);
|
||||||
|
var numberOfPathsAferInclude = newIncludeQuery.Paths.Count;
|
||||||
|
|
||||||
|
var expectedNumerOfPaths = numberOfPathsBeforeInclude + 1;
|
||||||
|
|
||||||
|
Assert.Equal(expectedNumerOfPaths, numberOfPathsAferInclude);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Should_NotModifyAnotherPath()
|
||||||
|
{
|
||||||
|
var includeQuery = _includeQueryBuilder.WithObjectAsPreviousProperty();
|
||||||
|
var pathsBeforeInclude = includeQuery.Paths;
|
||||||
|
|
||||||
|
var newIncludeQuery = includeQuery.Include(b => b.Author.Friends);
|
||||||
|
var pathsAfterInclude = newIncludeQuery.Paths;
|
||||||
|
|
||||||
|
Assert.Subset(pathsAfterInclude, pathsBeforeInclude);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
using Microsoft.eShopWeb.ApplicationCore.Helpers.Query;
|
||||||
|
using Microsoft.eShopWeb.UnitTests.Builders;
|
||||||
|
using System.Linq;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.eShopWeb.UnitTests.ApplicationCore.Helpers.Query.IncludeQueryTests
|
||||||
|
{
|
||||||
|
public class ThenInclude
|
||||||
|
{
|
||||||
|
private IncludeQueryBuilder _includeQueryBuilder = new IncludeQueryBuilder();
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Should_ReturnIncludeQueryWithCorrectPath_IfIncludeSimpleType()
|
||||||
|
{
|
||||||
|
var includeQuery = _includeQueryBuilder.WithObjectAsPreviousProperty();
|
||||||
|
var pathBeforeInclude = includeQuery.Paths.First();
|
||||||
|
|
||||||
|
// There may be ORM libraries where including a simple type makes sense.
|
||||||
|
var newIncludeQuery = includeQuery.ThenInclude(p => p.Age);
|
||||||
|
var pathAfterInclude = newIncludeQuery.Paths.First();
|
||||||
|
var expectedPath = $"{pathBeforeInclude}.{nameof(Person.Age)}";
|
||||||
|
|
||||||
|
Assert.Equal(expectedPath, pathAfterInclude);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Should_ReturnIncludeQueryWithCorrectPath_IfIncludeFunction()
|
||||||
|
{
|
||||||
|
var includeQuery = _includeQueryBuilder.WithObjectAsPreviousProperty();
|
||||||
|
var pathBeforeInclude = includeQuery.Paths.First();
|
||||||
|
|
||||||
|
// This include does not make much sense, but it should at least not modify the paths.
|
||||||
|
var newIncludeQuery = includeQuery.ThenInclude(p => p.GetQuote());
|
||||||
|
var pathAfterInclude = newIncludeQuery.Paths.First();
|
||||||
|
|
||||||
|
Assert.Equal(pathBeforeInclude, pathAfterInclude);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Should_ReturnIncludeQueryWithCorrectPath_IfIncludeObject()
|
||||||
|
{
|
||||||
|
var includeQuery = _includeQueryBuilder.WithObjectAsPreviousProperty();
|
||||||
|
var pathBeforeInclude = includeQuery.Paths.First();
|
||||||
|
|
||||||
|
var newIncludeQuery = includeQuery.ThenInclude(p => p.FavouriteBook);
|
||||||
|
var pathAfterInclude = newIncludeQuery.Paths.First();
|
||||||
|
var expectedPath = $"{pathBeforeInclude}.{nameof(Person.FavouriteBook)}";
|
||||||
|
|
||||||
|
Assert.Equal(expectedPath, pathAfterInclude);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Should_ReturnIncludeQueryWithCorrectPath_IfIncludeCollection()
|
||||||
|
{
|
||||||
|
var includeQuery = _includeQueryBuilder.WithObjectAsPreviousProperty();
|
||||||
|
var pathBeforeInclude = includeQuery.Paths.First();
|
||||||
|
|
||||||
|
var newIncludeQuery = includeQuery.ThenInclude(p => p.Friends);
|
||||||
|
var pathAfterInclude = newIncludeQuery.Paths.First();
|
||||||
|
var expectedPath = $"{pathBeforeInclude}.{nameof(Person.Friends)}";
|
||||||
|
|
||||||
|
Assert.Equal(expectedPath, pathAfterInclude);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Should_ReturnIncludeQueryWithCorrectPath_IfIncludePropertyOverCollection()
|
||||||
|
{
|
||||||
|
var includeQuery = _includeQueryBuilder.WithCollectionAsPreviousProperty();
|
||||||
|
var pathBeforeInclude = includeQuery.Paths.First();
|
||||||
|
|
||||||
|
var newIncludeQuery = includeQuery.ThenInclude(p => p.FavouriteBook);
|
||||||
|
var pathAfterInclude = newIncludeQuery.Paths.First();
|
||||||
|
var expectedPath = $"{pathBeforeInclude}.{nameof(Person.FavouriteBook)}";
|
||||||
|
|
||||||
|
Assert.Equal(expectedPath, pathAfterInclude);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
using Microsoft.eShopWeb.ApplicationCore.Helpers.Query;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq.Expressions;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.eShopWeb.UnitTests.ApplicationCore.Helpers.Query.IncludeVisitorTests
|
||||||
|
{
|
||||||
|
public class Visit
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void Should_SetPath_IfPassedExpressionWithSimpleType()
|
||||||
|
{
|
||||||
|
var visitor = new IncludeVisitor();
|
||||||
|
Expression<Func<Book, string>> expression = (book) => book.Author.FirstName;
|
||||||
|
visitor.Visit(expression);
|
||||||
|
|
||||||
|
var expectedPath = $"{nameof(Book.Author)}.{nameof(Person.FirstName)}";
|
||||||
|
Assert.Equal(expectedPath, visitor.Path);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Should_SetPath_IfPassedExpressionWithObject()
|
||||||
|
{
|
||||||
|
var visitor = new IncludeVisitor();
|
||||||
|
Expression<Func<Book, Book>> expression = (book) => book.Author.FavouriteBook;
|
||||||
|
visitor.Visit(expression);
|
||||||
|
|
||||||
|
var expectedPath = $"{nameof(Book.Author)}.{nameof(Person.FavouriteBook)}";
|
||||||
|
Assert.Equal(expectedPath, visitor.Path);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Should_SetPath_IfPassedExpressionWithCollection()
|
||||||
|
{
|
||||||
|
var visitor = new IncludeVisitor();
|
||||||
|
Expression<Func<Book, List<Person>>> expression = (book) => book.Author.Friends;
|
||||||
|
visitor.Visit(expression);
|
||||||
|
|
||||||
|
var expectedPath = $"{nameof(Book.Author)}.{nameof(Person.Friends)}";
|
||||||
|
Assert.Equal(expectedPath, visitor.Path);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Should_SetPath_IfPassedExpressionWithFunction()
|
||||||
|
{
|
||||||
|
var visitor = new IncludeVisitor();
|
||||||
|
Expression<Func<Book, string>> expression = (book) => book.Author.GetQuote();
|
||||||
|
visitor.Visit(expression);
|
||||||
|
|
||||||
|
var expectedPath = nameof(Book.Author);
|
||||||
|
Assert.Equal(expectedPath, visitor.Path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
19
tests/UnitTests/ApplicationCore/Helpers/Query/Person.cs
Normal file
19
tests/UnitTests/ApplicationCore/Helpers/Query/Person.cs
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Microsoft.eShopWeb.UnitTests.ApplicationCore.Helpers.Query
|
||||||
|
{
|
||||||
|
public class Person
|
||||||
|
{
|
||||||
|
public int Age { get; set; }
|
||||||
|
public string FirstName { get; set; }
|
||||||
|
public string LastName { get; set; }
|
||||||
|
|
||||||
|
public Book FavouriteBook { get; set; }
|
||||||
|
public List<Person> Friends { get; set; }
|
||||||
|
|
||||||
|
public string GetQuote()
|
||||||
|
{
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
29
tests/UnitTests/Builders/IncludeQueryBuilder.cs
Normal file
29
tests/UnitTests/Builders/IncludeQueryBuilder.cs
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
using Microsoft.eShopWeb.ApplicationCore.Helpers.Query;
|
||||||
|
using Microsoft.eShopWeb.ApplicationCore.Interfaces;
|
||||||
|
using Microsoft.eShopWeb.UnitTests.ApplicationCore.Helpers.Query;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Microsoft.eShopWeb.UnitTests.Builders
|
||||||
|
{
|
||||||
|
public class IncludeQueryBuilder
|
||||||
|
{
|
||||||
|
public IncludeQuery<Person, List<Person>> WithCollectionAsPreviousProperty()
|
||||||
|
{
|
||||||
|
var pathMap = new Dictionary<IIncludeQuery, string>();
|
||||||
|
var query = new IncludeQuery<Person, List<Person>>(pathMap);
|
||||||
|
pathMap[query] = nameof(Person.Friends);
|
||||||
|
|
||||||
|
return query;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IncludeQuery<Book, Person> WithObjectAsPreviousProperty()
|
||||||
|
{
|
||||||
|
var pathMap = new Dictionary<IIncludeQuery, string>();
|
||||||
|
var query = new IncludeQuery<Book, Person>(pathMap);
|
||||||
|
pathMap[query] = nameof(Book.Author);
|
||||||
|
|
||||||
|
return query;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user