(feature): expression extensions

This commit is contained in:
Maksym Sadovnychyy 2024-10-17 13:18:10 +02:00
parent 6da856e2ad
commit d8eacdd32a
3 changed files with 104 additions and 1 deletions

View File

@ -0,0 +1,59 @@
using System.Linq.Expressions;
using MaksIT.Core.Extensions;
namespace MaksIT.Core.Tests.Extensions;
public class ExpressionExtensionsTests {
[Fact]
public void CreateContainsPredicate_ShouldReturnTrue_WhenIdIsInList() {
// Arrange
var ids = new List<Guid> { Guid.NewGuid(), Guid.NewGuid(), Guid.NewGuid() };
var targetId = ids[1];
var predicate = ExpressionExtensions.CreateContainsPredicate<TestEntity>(ids, nameof(TestEntity.Id));
// Act
var result = predicate.Compile()(new TestEntity { Id = targetId });
// Assert
Assert.True(result);
}
[Fact]
public void CreateContainsPredicate_ShouldReturnFalse_WhenIdIsNotInList() {
// Arrange
var ids = new List<Guid> { Guid.NewGuid(), Guid.NewGuid(), Guid.NewGuid() };
var targetId = Guid.NewGuid();
var predicate = ExpressionExtensions.CreateContainsPredicate<TestEntity>(ids, nameof(TestEntity.Id));
// Act
var result = predicate.Compile()(new TestEntity { Id = targetId });
// Assert
Assert.False(result);
}
[Fact]
public void CombineWith_ShouldCombineTwoPredicates() {
// Arrange
Expression<Func<TestEntity, bool>> firstPredicate = x => x.Age > 18;
Expression<Func<TestEntity, bool>> secondPredicate = x => x.Name.StartsWith("A");
// Act
var combinedPredicate = firstPredicate.CombineWith(secondPredicate);
var compiledPredicate = combinedPredicate.Compile();
// Assert
Assert.True(compiledPredicate(new TestEntity { Age = 20, Name = "Alice" }));
Assert.False(compiledPredicate(new TestEntity { Age = 17, Name = "Alice" }));
Assert.False(compiledPredicate(new TestEntity { Age = 20, Name = "Bob" }));
}
private class TestEntity {
public Guid Id { get; set; }
public int Age { get; set; }
public string Name { get; set; }
}
}

View File

@ -0,0 +1,44 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
namespace MaksIT.Core.Extensions;
public static class ExpressionExtensions {
public static Expression<Func<T, bool>> CreateContainsPredicate<T>(IEnumerable<Guid> ids, string propertyName) {
var parameter = Expression.Parameter(typeof(T), "x");
var property = Expression.Property(parameter, propertyName);
var containsMethod = typeof(List<Guid>).GetMethod("Contains", new[] { typeof(Guid) });
var containsCall = Expression.Call(Expression.Constant(ids), containsMethod!, property);
return Expression.Lambda<Func<T, bool>>(containsCall, parameter);
}
public static Expression<Func<T, bool>> CombineWith<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second) {
var parameter = first.Parameters[0];
var visitor = new SubstituteParameterVisitor(second.Parameters[0], parameter);
var secondBody = visitor.Visit(second.Body);
var combinedBody = Expression.AndAlso(first.Body, secondBody);
return Expression.Lambda<Func<T, bool>>(combinedBody, parameter);
}
private class SubstituteParameterVisitor : ExpressionVisitor {
private readonly ParameterExpression _oldParameter;
private readonly ParameterExpression _newParameter;
public SubstituteParameterVisitor(ParameterExpression oldParameter, ParameterExpression newParameter) {
_oldParameter = oldParameter;
_newParameter = newParameter;
}
protected override Expression VisitParameter(ParameterExpression node) {
// Replace old parameter with the new one
return node == _oldParameter ? _newParameter : base.VisitParameter(node);
}
}
}

View File

@ -8,7 +8,7 @@
<!-- NuGet package metadata --> <!-- NuGet package metadata -->
<PackageId>MaksIT.Core</PackageId> <PackageId>MaksIT.Core</PackageId>
<Version>1.1.0</Version> <Version>1.1.1</Version>
<Authors>Maksym Sadovnychyy</Authors> <Authors>Maksym Sadovnychyy</Authors>
<Company>MAKS-IT</Company> <Company>MAKS-IT</Company>
<Product>MaksIT.Core</Product> <Product>MaksIT.Core</Product>