diff --git a/src/MaksIT.Core.Tests/Extensions/ExpressionExtensionsTests.cs b/src/MaksIT.Core.Tests/Extensions/ExpressionExtensionsTests.cs new file mode 100644 index 0000000..f99cef3 --- /dev/null +++ b/src/MaksIT.Core.Tests/Extensions/ExpressionExtensionsTests.cs @@ -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.NewGuid(), Guid.NewGuid(), Guid.NewGuid() }; + var targetId = ids[1]; + var predicate = ExpressionExtensions.CreateContainsPredicate(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.NewGuid(), Guid.NewGuid(), Guid.NewGuid() }; + var targetId = Guid.NewGuid(); + var predicate = ExpressionExtensions.CreateContainsPredicate(ids, nameof(TestEntity.Id)); + + // Act + var result = predicate.Compile()(new TestEntity { Id = targetId }); + + // Assert + Assert.False(result); + } + + [Fact] + public void CombineWith_ShouldCombineTwoPredicates() { + // Arrange + Expression> firstPredicate = x => x.Age > 18; + Expression> 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; } + } +} + diff --git a/src/MaksIT.Core/Extensions/ExpressionExtensions.cs b/src/MaksIT.Core/Extensions/ExpressionExtensions.cs new file mode 100644 index 0000000..30d8b3c --- /dev/null +++ b/src/MaksIT.Core/Extensions/ExpressionExtensions.cs @@ -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> CreateContainsPredicate(IEnumerable ids, string propertyName) { + var parameter = Expression.Parameter(typeof(T), "x"); + var property = Expression.Property(parameter, propertyName); + var containsMethod = typeof(List).GetMethod("Contains", new[] { typeof(Guid) }); + var containsCall = Expression.Call(Expression.Constant(ids), containsMethod!, property); + + return Expression.Lambda>(containsCall, parameter); + } + + public static Expression> CombineWith(this Expression> first, Expression> 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>(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); + } + } +} + diff --git a/src/MaksIT.Core/MaksIT.Core.csproj b/src/MaksIT.Core/MaksIT.Core.csproj index ac29a1b..ac6b41e 100644 --- a/src/MaksIT.Core/MaksIT.Core.csproj +++ b/src/MaksIT.Core/MaksIT.Core.csproj @@ -8,7 +8,7 @@ MaksIT.Core - 1.1.0 + 1.1.1 Maksym Sadovnychyy MAKS-IT MaksIT.Core