Combine Expression (Expression<Func<TIn,TOut>> with Expression<Func<TOut, bool>>) - c#

I have two expressions:
Expression<Func<T1, T2>>
Expression<Func<T2, bool>>
And I want to combine these and get a new expression of type Expression<Func<T1, bool>> so that it can be used in Entity Framework LINQ.
I combine them with help of Expression.Invoke(), but it doesn't work.
//Extensinon method
public static Expression<Func<T1, bool>> Compose<T1, T2>(this Expression<Func<T1, T2>> convertExpr, Expression<Func<T2, bool>> predicate)
=> Expression.Lambda<Func<T1, bool>>(Expression.Invoke(predicate, convertExpr.Body), convertExpr.Parameters.First());
...
Expression<Func<One, Two>> convert;
Expression<Func<Two, bool>> predicate;
Expression<Func<One, bool>> filter=convert.Compose(predicate);
// Works fine
List<One> lst;
lst.AsQueryable().Where(filter);
DbSet<One> src;
// In next line throws runtime NotSupportedException: The LINQ expression node type 'Invoke' is not supported in LINQ to Entities
src.Where(filter);
UPDATE
For example:
public class Book
{
public int Id {get; protected set;}
public string Name {get; protected set;}
public virtual Genre {get; protected set;}
}
public class Genre
{
public int Id {get; protected set;}
public string Name {get; protected set;}
}
...
Expression<Func<Book, Genre>> convert = b=>b.Genre;
Expression<Func<Genre, bool>> predicate = g=>g.Name=="fantasy";
// Need: b=>b.Genre=="fantasy"
Expression<Func<Genre, bool>> filter=convert.Compose(predicate);
// Works fine
List<Book> lst;
lst.AsQueryable().Where(filter);
DbSet<Book> books;
// In next line throws runtime NotSupportedException: The LINQ expression node type 'Invoke' is not supported in LINQ to Entities
books.Where(filter);

Using the common ExpressionVisitor for replacing one Expression with another, my standard Compose function (in the more common mathematical order, I think), substitutes the Body of one LambdaExpression for the parameter in another:
public static class ExpressionExt {
// Compose: (y => f(y)).Compose(x => g(x)) -> x => f(g(x))
/// <summary>
/// Composes two LambdaExpression into a new LambdaExpression
/// </summary>
/// <param name="Tpg">Type of parameter to gFn, and type of parameter to result lambda.</param>
/// <param name="Tpf">Type of result of gFn and type of parameter to fFn.</param>
/// <param name="TRes">Type of result of fFn and type of result of result lambda.</param>
/// <param name="fFn">The outer LambdaExpression.</param>
/// <param name="gFn">The inner LambdaExpression.</param>
/// <returns>LambdaExpression representing outer composed with inner</returns>
public static Expression<Func<Tpg, TRes>> Compose<Tpg, Tpf, TRes>(this Expression<Func<Tpf, TRes>> fFn, Expression<Func<Tpg, Tpf>> gFn) =>
Expression.Lambda<Func<Tpg, TRes>>(fFn.Body.Replace(fFn.Parameters[0], gFn.Body), gFn.Parameters[0]);
/// <summary>
/// Replaces an Expression (reference Equals) with another Expression
/// </summary>
/// <param name="orig">The original Expression.</param>
/// <param name="from">The from Expression.</param>
/// <param name="to">The to Expression.</param>
/// <returns>Expression with all occurrences of from replaced with to</returns>
public static Expression Replace(this Expression orig, Expression from, Expression to) => new ReplaceVisitor(from, to).Visit(orig);
}
/// <summary>
/// ExpressionVisitor to replace an Expression (that is Equals) with another Expression.
/// </summary>
public class ReplaceVisitor : ExpressionVisitor {
readonly Expression from;
readonly Expression to;
public ReplaceVisitor(Expression from, Expression to) {
this.from = from;
this.to = to;
}
public override Expression Visit(Expression node) => node == from ? to : base.Visit(node);
}
With this available, your example is straightforward:
Expression<Func<One, Two>> convert = p1 => new Two(p1);
Expression<Func<Two, bool>> predicate = p2 => p2 == new Two();
Expression<Func<One, bool>> filter = predicate.Compose(convert);
However, especially with EF expressions, it may be preferable to use my replacement for Invoke, which handles null propogation, in cases where your arguments may be null. It also uses the same Replace ExpressionVisitor as above:
public static class ExpressionExt2 {
public static Expression PropagateNull(this Expression orig) => new NullVisitor().Visit(orig);
// Apply: (x => f).Apply(args)
/// <summary>
/// Substitutes an array of Expression args for the parameters of a lambda, returning a new Expression
/// </summary>
/// <param name="e">The original LambdaExpression to "call".</param>
/// <param name="args">The Expression[] of values to substitute for the parameters of e.</param>
/// <returns>Expression representing e.Body with args substituted in</returns>
public static Expression Apply(this LambdaExpression e, params Expression[] args) {
var b = e.Body;
foreach (var pa in e.Parameters.Zip(args, (p, a) => (p, a)))
b = b.Replace(pa.p, pa.a);
return b.PropagateNull();
}
}
/// <summary>
/// ExpressionVisitor to replace a null.member Expression with a null
/// </summary>
public class NullVisitor : System.Linq.Expressions.ExpressionVisitor {
public override Expression Visit(Expression node) {
if (node is MemberExpression nme && nme.Expression is ConstantExpression nce && nce.Value == null)
return Expression.Constant(null, nce.Type.GetMember(nme.Member.Name).Single().GetMemberType());
else
return base.Visit(node);
}
}
public static class MeberInfoExt {
public static Type GetMemberType(this MemberInfo member) {
switch (member) {
case FieldInfo mfi:
return mfi.FieldType;
case PropertyInfo mpi:
return mpi.PropertyType;
case EventInfo mei:
return mei.EventHandlerType;
default:
throw new ArgumentException("MemberInfo must be if type FieldInfo, PropertyInfo or EventInfo", nameof(member));
}
}
}
Given Apply, your Compose is simply:
public static Expression<Func<T1, bool>> Compose<T1, T2>(this Expression<Func<T1, T2>> convertExpr, Expression<Func<T2, bool>> predicate)
=> Expression.Lambda<Func<T1, bool>>(predicate.Apply(convertExpr.Body), convertExpr.Parameters.First());

Related

PredicateBuilder does not work with Where clause

I just copied the following source code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
/// <summary>
/// Enables the efficient, dynamic composition of query predicates.
/// </summary>
public static class PredicateBuilder
{
/// <summary>
/// Creates a predicate that evaluates to true.
/// </summary>
public static Expression<Func<T, bool>> True<T>() { return param => true; }
/// <summary>
/// Creates a predicate that evaluates to false.
/// </summary>
public static Expression<Func<T, bool>> False<T>() { return param => false; }
/// <summary>
/// Creates a predicate expression from the specified lambda expression.
/// </summary>
public static Expression<Func<T, bool>> Create<T>(Expression<Func<T, bool>> predicate) { return predicate; }
public static Expression<Func<T, bool>> Create<T>(bool value) { return param => value; }
/// <summary>
/// Combines the first predicate with the second using the logical "and".
/// </summary>
public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second)
{
return first.Compose(second, Expression.AndAlso);
}
/// <summary>
/// Combines the first predicate with the second using the logical "or".
/// </summary>
public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second)
{
return first.Compose(second, Expression.OrElse);
}
/// <summary>
/// Negates the predicate.
/// </summary>
public static Expression<Func<T, bool>> Not<T>(this Expression<Func<T, bool>> expression)
{
var negated = Expression.Not(expression.Body);
return Expression.Lambda<Func<T, bool>>(negated, expression.Parameters);
}
public static Func<T, bool> ToFunc<T>(this Expression<Func<T, bool>> expression)
{
return expression.Compile();
}
/// <summary>
/// Combines the first expression with the second using the specified merge function.
/// </summary>
static Expression<T> Compose<T>(this Expression<T> first, Expression<T> second, Func<Expression, Expression, Expression> merge)
{
// zip parameters (map from parameters of second to parameters of first)
var map = first.Parameters
.Select((f, i) => new { f, s = second.Parameters[i] })
.ToDictionary(p => p.s, p => p.f);
// replace parameters in the second lambda expression with the parameters in the first
var secondBody = ParameterRebinder.ReplaceParameters(map, second.Body);
// create a merged lambda expression with parameters from the first expression
return Expression.Lambda<T>(merge(first.Body, secondBody), first.Parameters);
}
class ParameterRebinder : ExpressionVisitor
{
readonly Dictionary<ParameterExpression, ParameterExpression> map;
ParameterRebinder(Dictionary<ParameterExpression, ParameterExpression> map)
{
this.map = map ?? new Dictionary<ParameterExpression, ParameterExpression>();
}
public static Expression ReplaceParameters(Dictionary<ParameterExpression, ParameterExpression> map, Expression exp)
{
return new ParameterRebinder(map).Visit(exp);
}
protected override Expression VisitParameter(ParameterExpression p)
{
ParameterExpression replacement;
if (map.TryGetValue(p, out replacement))
{
p = replacement;
}
return base.VisitParameter(p);
}
}
}
Then, I am using it as below
public class Person
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public DateTimeOffset BirthDate { get; set; }
}
using Bogus;
using System.Collections.Generic;
public static class PeopleDataGenerator
{
public static IEnumerable<Person> GetPeople(int count = 200)
{
var people = new Faker<Person>()
.StrictMode(true)
.RuleFor(p => p.Id, f => f.Random.Int(0,1000))
.RuleFor(p => p.FirstName, f => f.Name.FirstName())
.RuleFor(p => p.LastName, f => f.Name.LastName())
.RuleFor(p => p.BirthDate, f => f.Person.DateOfBirth)
;
return people.Generate(count);
}
}
and
IEnumerable<Person> people = PeopleDataGenerator.GetPeople(500);
var pb = PredicateBuilder.True<Person>();
pb.And(x => x.Id > 500);
pb.And(x => x.FirstName.StartsWith("A", StringComparison.OrdinalIgnoreCase));
IEnumerable<Person> result = people.Where(pb.ToFunc()).ToList();
But the people and result are the same!. There is no filtering in final result.
Did I miss something?
Instead of:
pb.And(x => x.Id > 500);
write
pb = pb.And(x => x.Id > 500);
And change next call as well.
Or you can simply have methods stacked:
var pb = PredicateBuilder.True<Person>()
.And(x => x.Id > 500)
.And(x => x.FirstName.StartsWith("A", StringComparison.OrdinalIgnoreCase));

Issue with remapping inner Lambda in current search LINQ

I'm using Automapper and three-layered architecture in my application. I have a method which takes Linq query (LambdaExpression) as a parameter.
To pass this parameter in another layer I need to remap it. So, I created an ExpressionExtension class, which is responsible for returning remapped Expression:
/// <summary>
/// A class which contains extension methods for <see cref="Expression"/> and <see cref="Expression{TDelegate}"/> instances.
/// </summary>
public static class ExpressionExtensions
{
/// <summary>
/// Remaps all property access from type <typeparamref name="TSource"/> to <typeparamref name="TDestination"/> in <paramref name="expression"/>.
/// </summary>
/// <typeparam name="TSource">The type of the source element.</typeparam>
/// <typeparam name="TDestination">The type of the destination element.</typeparam>
/// <typeparam name="TResult">The type of the result from the lambda expression.</typeparam>
/// <param name="expression">The <see cref="Expression{TDelegate}"/> to remap the property access in.</param>
/// <returns>An <see cref="Expression{TDelegate}"/> equivalent to <paramref name="expression"/>, but applying to elements of type <typeparamref name="TDestination"/> instead of <typeparamref name="TSource"/>.</returns>
public static Expression<Func<TDestination, TResult>> RemapForType<TSource, TDestination, TResult>(
this Expression<Func<TSource, TResult>> expression)
{
Contract.Requires(expression != null);
Contract.Ensures(Contract.Result<Expression<Func<TDestination, TResult>>>() != null);
var newParameter = Expression.Parameter(typeof(TDestination));
Contract.Assume(newParameter != null);
var visitor = new AutoMapVisitor<TSource, TDestination>(newParameter);
var remappedBody = visitor.Visit(expression.Body);
if (remappedBody == null)
{
throw new InvalidOperationException("Unable to remap expression");
}
return Expression.Lambda<Func<TDestination, TResult>>(remappedBody, newParameter);
}
}
And code for AutoMapVisitor:
/// <summary>
/// An <see cref="ExpressionVisitor"/> implementation which uses <see href="http://automapper.org">AutoMapper</see> to remap property access from elements of type <typeparamref name="TSource"/> to elements of type <typeparamref name="TDestination"/>.
/// </summary>
/// <typeparam name="TSource">The type of the source element.</typeparam>
/// <typeparam name="TDestination">The type of the destination element.</typeparam>
public class AutoMapVisitor<TSource, TDestination> : ExpressionVisitor
{
private readonly ParameterExpression _newParameter;
private readonly TypeMap _typeMap = Mapper.FindTypeMapFor<TSource, TDestination>();
/// <summary>
/// Initialises a new instance of the <see cref="AutoMapVisitor{TSource, TDestination}"/> class.
/// </summary>
/// <param name="newParameter">The new <see cref="ParameterExpression"/> to access.</param>
public AutoMapVisitor(ParameterExpression newParameter)
{
_newParameter = newParameter;
}
/// <summary>
/// Visits the children of the <see cref="T:System.Linq.Expressions.MemberExpression"/>.
/// </summary>
/// <returns>
/// The modified expression, if it or any subexpression was modified; otherwise, returns the original expression.
/// </returns>
/// <param name="node">The expression to visit.</param>
protected override Expression VisitMember(MemberExpression node)
{
var propertyMaps = _typeMap.GetPropertyMaps();
// Find any mapping for this member
var propertyMap = propertyMaps.SingleOrDefault(map => map.SourceMember == node.Member);
if (propertyMap == null)
{
return base.VisitMember(node);
}
var destinationProperty = propertyMap.DestinationProperty;
var destinationMember = destinationProperty.MemberInfo;
// Check the new member is a property too
var property = destinationMember as PropertyInfo;
if (property == null)
{
return base.VisitMember(node);
}
// Access the new property
var newPropertyAccess = Expression.Property(_newParameter, property);
return base.VisitMember(newPropertyAccess);
}
/// <summary>
///
/// </summary>
/// <typeparam name="T">Func of type TIn and result is TResult</typeparam>
/// <param name="node">Expression of Func with certain input and result types</param>
/// <returns></returns>
protected override Expression VisitLambda<T>(Expression<T> node)
{
//predicate.RemapForType<PersonViewModel, PersonDTO, bool>()
// считаем, что будет 1 параметр(объект) операции
var source = node.Parameters.FirstOrDefault();
// берем первый найденный маппинг источника
TypeMap typeMap = source == null
? null
: Mapper.GetAllTypeMaps().FirstOrDefault(tMap => tMap.SourceType == source.Type);
if (typeMap!=null)
{
var ts = typeMap.SourceType;
var td = typeMap.DestinationType;
var res = node.ReturnType;
return VisitLambda(node, ts, td, res);
//return node.RemapForType<ts,td,res>();
}
return base.VisitLambda<T>(node);
}
private Expression<Func<TOut,TResult>> VisitLambda<T,TIn, TOut, TResult>(Expression<T> node, TIn source, TOut destination, TResult result)
{
ParameterExpression pe = Expression.Parameter(typeof (TIn), "innerParam");
var value = typeof(T) as Func<TIn, TResult>;
Expression<Func<TIn, TResult>> input = Expression.Lambda<Func<TIn, TResult>>(Expression.Invoke(node.Body,node.Parameters)); // tried also without Expression.Invoke
return input.RemapForType<TIn, TOut, TResult>();
}
}
I understand that I need to override base Lambda Visitor and to call ExpressionExtension.RemapForType method once again to remap inner lambda. But how can I pass the right parameters to it? So, I decided to create generic method but nothing works right.
Could you help me to get the right solution.
E.g. something like that:
Expression<Func<TSource,TResult>> a = val => val.FullName == "ABC" && val.SomeEnumerableProperty.Any(inner => inner.City == "Moscow");
// Here we will get remapped lambda with TDestination class as a parameter and TDestination class properties remapped by AutoMapper as you saw in my posted code
Expression<Func<TDestination, TResult>> b = a.RemapForType<TSource,TDestination,TResult>();
And in I don't know which classes will be used as Source and Destination in inner Lambda because it is runtime. So the example is simply for you to understand what I want to do.
EDITED
So, I changed my Visitor in this variant:
/// <summary>
/// An <see cref="ExpressionVisitor"/> implementation which uses <see href="http://automapper.org">AutoMapper</see> to remap property access from elements of type <typeparamref name="TSource"/> to elements of type <typeparamref name="TDestination"/>.
/// </summary>
/// <typeparam name="TSource">The type of the source element.</typeparam>
/// <typeparam name="TDestination">The type of the destination element.</typeparam>
public class AutoMapVisitor<TSource, TDestination> : ExpressionVisitor
{
private readonly TypeMap _typeMap;
private readonly List<TypeMap> _typeMaps = new List<TypeMap>();
private readonly Dictionary<Type, ParameterExpression> parameterMap = new Dictionary<Type, ParameterExpression>();
private Dictionary<MemberInfo, Expression> memberMap = new Dictionary<MemberInfo, Expression>();
/// <summary>
/// Initialises a new instance of the <see cref="AutoMapVisitor{TSource, TDestination}"/> class.
/// </summary>
/// <param name="newParameter">The new <see cref="ParameterExpression"/> to access.</param>
public AutoMapVisitor(ParameterExpression newParameter)
{
_typeMap = Mapper.FindTypeMapFor<TSource, TDestination>();
Contract.Assume(_typeMap != null);
_typeMaps.Add(_typeMap);
_typeMaps.AddRange(FillNestedTypeMaps(_typeMap));
parameterMap.Add(newParameter.Type, newParameter); // main parameter which we don't need to recreate
foreach (TypeMap map in _typeMaps)
{
if (parameterMap.ContainsKey(map.DestinationType)) continue;
parameterMap.Add(map.DestinationType, Expression.Parameter(map.DestinationType, map.DestinationType.Name.ToLower()));
}
}
/// <summary>
/// находим все возможные маппинги текущей конфигурации вниз по иерархии свойств класса источника
/// </summary>
/// <param name="tMap">Объект конфигурации, содержащий информацию о типе источника и типе назначения</param>
private IEnumerable<TypeMap> FillNestedTypeMaps(TypeMap tMap)
{
List<TypeMap> result = new List<TypeMap>();
// 1 where: маппинг только между классами
// 2 where: маппинг не равен входящему и ReverseMap значению, например на входе: A -> B, тогда отпадут значения A -> B и B -> A из проверки
// 3 where: берем те свойства, тип которых совпадает с входящим типом источника или, если это обобщенный тип, с его аргументом и тоже самое для типа назначения
IEnumerable<PropertyMap> pMaps = tMap.GetPropertyMaps();
var list = Mapper.GetAllTypeMaps()
.Where(
map => map.SourceType.IsClass && map.DestinationType.IsClass)
.Where(map =>
!(map.Equals(tMap) ||
(map.SourceType == tMap.DestinationType && map.DestinationType == tMap.SourceType)))
.Where(
map =>
pMaps
.Any(
pi =>
{
var pis = pi.SourceMember as PropertyInfo;
if (pis == null) return false;
bool forSource = pis.PropertyType == map.SourceType ||
(pis.PropertyType.IsGenericType &&
pis.PropertyType.GetGenericArguments()[0] == map.SourceType);
bool forDestination = pi.DestinationPropertyType == map.DestinationType ||
(pi.DestinationPropertyType.IsGenericType &&
pi.DestinationPropertyType.GetGenericArguments()[0] == map.DestinationType);
return forSource && forDestination;
}))
.ToList();
if (list.Count > 0)
{
result.AddRange(list);
foreach (TypeMap typeMap in list)
{
result.AddRange(FillNestedTypeMaps(typeMap));
}
}
return result;
}
private Type Map(Type type)
{
var tMap = _typeMaps.FirstOrDefault(map => map.SourceType == type);
Contract.Assume(tMap != null);
return tMap.DestinationType;
}
private ParameterExpression Map(ParameterExpression parameter)
{
var mappedType = Map(parameter.Type);
ParameterExpression mappedParameter;
if (!parameterMap.TryGetValue(mappedType, out mappedParameter))
parameterMap.Add(mappedType, mappedParameter = Expression.Parameter(mappedType, parameter.Name));
return mappedParameter;
}
private Expression Map(MemberInfo mi, Expression exp)
{
Expression val;
if (!memberMap.TryGetValue(mi, out val))
{
foreach (PropertyMap propertyMap in
_typeMaps.Select(map => map.GetPropertyMaps().SingleOrDefault(m => m.SourceMember == mi))
.Where(propertyMap => propertyMap != null))
{
memberMap.Add(mi, val = Expression.PropertyOrField(exp, propertyMap.DestinationProperty.MemberInfo.Name));
break;
}
}
return val;
}
/// <summary>
/// Visits the children of the <see cref="T:System.Linq.Expressions.MemberExpression"/>.
/// </summary>
/// <returns>
/// The modified expression, if it or any subexpression was modified; otherwise, returns the original expression.
/// </returns>
/// <param name="node">The expression to visit.</param>
protected override Expression VisitMember(MemberExpression node)
{
var expression = Visit(node.Expression);
if (expression == node.Expression)
return node;
return Map(node.Member, expression);
}
protected override Expression VisitLambda<T>(Expression<T> node)
{
return Expression.Lambda(Visit(node.Body), node.Parameters.Select(Map));
}
protected override Expression VisitParameter(ParameterExpression node)
{
return Map(node);
}
protected override Expression VisitMethodCall(MethodCallExpression node)
{
// if static object and generic method
if (node.Object == null && node.Method.IsGenericMethod)
{
// Static generic method
var args = Visit(node.Arguments);
var genericArgs = node.Method.GetGenericArguments().Select(Map).ToArray();
var method = node.Method.GetGenericMethodDefinition().MakeGenericMethod(genericArgs);
return Expression.Call(method, args);
}
return base.VisitMethodCall(node);
}
}
And now after remapping it throws an Exception:
variable 'person' of type 'Reestr.DAL.Entities.Person' referenced from scope '', but it is not defined
It is interesting that this Exception is thrown only when we have an inner Lambda (e.g. method Any). But if we didn't include it, using only simple search conditions, everything works fine! Seems like there is missing reference on Enumerable property of mapped class or there is newly created parameter somewhere and I can't understand where it can be.
Could you give me any suggestions, please, how can I solve this issue! Thanks!
EDITED
As #MBoros mentioned Automapper supports such mapping out of the box (of course if there are no hard code sections). The problem is in ServiceStack.OrmLite!!! I've tried simply passing manually created query with inner subquery and it failed with an exception I've posted earlier...

Can I use Linq's Except() with a lambda expression comparer?

I know I can call linq's Except and specify a custom IEqualityComparer, but implementing a new Comparer class for each data type seems like an overkill for this purpose. Can I use a lambda expression to provide the equality function, like when I use Where or other LINQ functions?
If I can't, is there an alternative?
For any one still looking; here's another way of implementing a custom lambda comparer.
public class LambdaComparer<T> : IEqualityComparer<T>
{
private readonly Func<T, T, bool> _expression;
public LambdaComparer(Func<T, T, bool> lambda)
{
_expression = lambda;
}
public bool Equals(T x, T y)
{
return _expression(x, y);
}
public int GetHashCode(T obj)
{
/*
If you just return 0 for the hash the Equals comparer will kick in.
The underlying evaluation checks the hash and then short circuits the evaluation if it is false.
Otherwise, it checks the Equals. If you force the hash to be true (by assuming 0 for both objects),
you will always fall through to the Equals check which is what we are always going for.
*/
return 0;
}
}
you can then create an extension for the linq Except an Intersect that take in lambda's
/// <summary>
/// Returns all items in the first collection except the ones in the second collection that match the lambda condition
/// </summary>
/// <typeparam name="T">The type</typeparam>
/// <param name="listA">The first list</param>
/// <param name="listB">The second list</param>
/// <param name="lambda">The filter expression</param>
/// <returns>The filtered list</returns>
public static IEnumerable<T> Except<T>(this IEnumerable<T> listA, IEnumerable<T> listB, Func<T, T, bool> lambda)
{
return listA.Except(listB, new LambdaComparer<T>(lambda));
}
/// <summary>
/// Returns all items in the first collection that intersect the ones in the second collection that match the lambda condition
/// </summary>
/// <typeparam name="T">The type</typeparam>
/// <param name="listA">The first list</param>
/// <param name="listB">The second list</param>
/// <param name="lambda">The filter expression</param>
/// <returns>The filtered list</returns>
public static IEnumerable<T> Intersect<T>(this IEnumerable<T> listA, IEnumerable<T> listB, Func<T, T, bool> lambda)
{
return listA.Intersect(listB, new LambdaComparer<T>(lambda));
}
Usage:
var availableItems = allItems.Except(filterItems, (p, p1) => p.Id== p1.Id);
Can you not use a .Where with a lambda that filters out your required values?
Example as requested:
static void Main(string[] args)
{
var firstCustomers = new[] { new Customer { Id = 1, Name = "Bob" }, new Customer { Id = 2, Name = "Steve" } };
var secondCustomers = new[] { new Customer { Id = 2, Name = "Steve" }, new Customer { Id = 3, Name = "John" } };
var customers = secondCustomers.Where(c => !firstCustomers.Select(fc => fc.Id).Contains(c.Id));
}
public class Customer
{
public int Id { get; set; }
public string Name { get; set; }
}
I don't think you can directly with the basic LINQ interfaces, but I've seen people implement a LambdaComparer class with extension methods which will help you do it.
Here's an example
Here's something simple I whipped up:
public class CustomComparer<TSource, TCompareType> : IEqualityComparer<TSource> where TSource : class
{
private readonly Func<TSource, TCompareType> getComparisonObject;
public CustomComparer(Func<TSource,TCompareType> getComparisonObject)
{
if (getComparisonObject == null) throw new ArgumentNullException("getComparisonObject");
this.getComparisonObject = getComparisonObject;
}
/// <summary>
/// Determines whether the specified objects are equal.
/// </summary>
/// <returns>
/// true if the specified objects are equal; otherwise, false.
/// </returns>
/// <param name="x">The first object of type <paramref name="T"/> to compare.
/// </param><param name="y">The second object of type <paramref name="T"/> to compare.
/// </param>
public bool Equals(TSource x, TSource y)
{
if (x == null)
{
return (y == null);
}
else if (y == null)
{
return false;
}
return EqualityComparer<TCompareType>.Default.Equals(getComparisonObject(x), getComparisonObject(y));
}
/// <summary>
/// Returns a hash code for the specified object.
/// </summary>
/// <returns>
/// A hash code for the specified object.
/// </returns>
/// <param name="obj">The <see cref="T:System.Object"/> for which a hash code is to be returned.
/// </param><exception cref="T:System.ArgumentNullException">The type of <paramref name="obj"/> is a reference type and <paramref name="obj"/> is null.
/// </exception>
public int GetHashCode(TSource obj)
{
return EqualityComparer<TCompareType>.Default.GetHashCode(getComparisonObject(obj));
}
}
Usage:
var myItems = allItems.Except(theirItems, new CustomComparer(item => item.Name));
Use an extension!
public static IEnumerable<T> Except<T, TKey>(this IEnumerable<T> items, IEnumerable<T> other,
Func<T, TKey> getKey)
{
return from item in items
join otherItem in other on getKey(item)
equals getKey(otherItem) into tempItems
from temp in tempItems.DefaultIfEmpty()
where ReferenceEquals(null, temp) || temp.Equals(default(T))
select item;
}
Source
Here' is l except r solution, based on a LINQ outer join technique:
from l in new[] { 1, 2, 3 }
join r in new[] { 2, 4, 5 }
on l equals r
into rr
where !rr.Any()
select l
Will yield: 1, 3
But you may try this. This is so much easy, I think code contains errors. Of course, the code is for small amounts, without LINQ translates to db etc.
public static IEnumerable<TSource> ExceptPredicate<TSource>(this IEnumerable<TSource> first, IEnumerable<TSource> second, Func<TSource, TSource, bool> compare) {
foreach (var itmFirst in first) {
if (!second.Any(itmsecond => compare(itmFirst, itmsecond))) {
yield return itmFirst;
}
}
yield break;
}

When using a repository is it possible for a type to return a Func that the repository uses to test for existing entities?

For example given a Factory with a method
public static T Save<T>(T item) where T : Base, new()
{
/* item.Id == Guid.Empty therefore item is new */
if (item.Id == Guid.Empty && repository.GetAll<T>(t => t.Name == item.Name))
{
throw new Exception("Name is not unique");
}
}
how do I create a property of Base (say MustNotAlreadyExist) so that I can change the method above to
public static T Save<T>(T item) where T : Base, new()
{
/* item.Id == Guid.Empty therefore item is new */
if (item.Id == Guid.Empty && repository.GetAll<T>(t.MustNotAlreadyExist))
{
throw new Exception("Name is not unique");
}
}
public class Base
{
...
public virtual Expression<Func<T, bool>> MustNotAlreadyExist()
{
return (b => b.Name == name); /* <- this clearly doesn't work */
}
}
and then how can I override MustNotAlreadyExist in Account : Base
public class Account : Base
{
...
public override Expression<Func<T, bool>> MustNotAlreadyExist()
{
return (b => b.Name == name && b.AccountCode == accountCode); /* <- this doesn't work */
}
...
}
Try this:
public class Account : Base
{
...
public override Expression<Func<T, bool>> MustNotAlreadyExist()
{
return (b => b.Name == name && b.AccountCode == accountCode).Any();
}
...
}
The Any() method will return true if any record matches the predicate. It could be argued that it is outside the responsibility of the repository to check for presence of a record before saving.
UPDATE:
There is a great article on CodeProject that describes a generic Repository for Entity Framework:
http://www.codeproject.com/KB/database/ImplRepositoryPatternEF.aspx
This could be applied to a non-Entity Framework data context. Here is an excerpt that provides a very flexible method for checking for an existing value by accepting the name of a field, a value, and a key value. You can apply this to any Entity type and use it to check for the presence of an entity before attempting a save.
/// <summary>
/// Check if value of specific field is already exist
/// </summary>
/// <typeparam name="E"></typeparam>
/// <param name="fieldName">name of the Field</param>
/// <param name="fieldValue">Field value</param>
/// <param name="key">Primary key value</param>
/// <returns>True or False</returns>
public bool TrySameValueExist(string fieldName, object fieldValue, string key)
{
// First we define the parameter that we are going to use the clause.
var xParam = Expression.Parameter(typeof(E), typeof(E).Name);
MemberExpression leftExprFieldCheck =
MemberExpression.Property(xParam, fieldName);
Expression rightExprFieldCheck = Expression.Constant(fieldValue);
BinaryExpression binaryExprFieldCheck =
MemberExpression.Equal(leftExprFieldCheck, rightExprFieldCheck);
MemberExpression leftExprKeyCheck =
MemberExpression.Property(xParam, this._KeyProperty);
Expression rightExprKeyCheck = Expression.Constant(key);
BinaryExpression binaryExprKeyCheck =
MemberExpression.NotEqual(leftExprKeyCheck, rightExprKeyCheck);
BinaryExpression finalBinaryExpr =
Expression.And(binaryExprFieldCheck, binaryExprKeyCheck);
//Create Lambda Expression for the selection
Expression<Func<E, bool>> lambdaExpr =
Expression.Lambda<Func<E, bool>>(finalBinaryExpr,
new ParameterExpression[] { xParam });
//Searching ....
return ((IRepository<E, C>)this).TryEntity(new Specification<E>(lambdaExpr));
}
/// <summary>
/// Check if Entities exist with Condition
/// </summary>
/// <param name="selectExpression">Selection Condition</param>
/// <returns>True or False</returns>
public bool TryEntity(ISpecification<E> selectSpec)
{
return _ctx.CreateQuery<E>("[" + typeof(E).Name + "]").Any<E>
(selectSpec.EvalPredicate);
}
I am not shure if you problem is solvable since you need to access both the repository and the new item to be checked. The new item to be checked is not available in a seperate method.
However you can outsource the call to GetAll so that your code becomes something similar to
(not tested)
public static T Save<T>(T item) where T : Base, new()
{
if (item.Id == Guid.Empty && (Check(repository, item)))
{
throw new Exception("Name is not unique");
}
}
public class Base
{
...
public Func<Enumerable<T>, T, bool> Check { get; set;}
public Base()
{
Check = (col, newItem) => (null != col.FirstOrDefault<T>(
item => item.Name == newItem.Name));
}
}
OK, here is the answer, this is a combination of the code posted by Dave Swersky and a little but of common sense.
public interface IUniqueable<T>
{
Expression<Func<T, bool>> Unique { get; }
}
public class Base, IUniqueable<Base>
{
...
public Expression<Func<Base, bool>> Unique
{
get
{
var xParam = Expression.Parameter(typeof(Base), typeof(Base).Name);
MemberExpression leftExprFieldCheck = MemberExpression.Property(xParam, "Name");
Expression rightExprFieldCheck = Expression.Constant(this.Name);
BinaryExpression binaryExprFieldCheck = MemberExpression.Equal(leftExprFieldCheck, rightExprFieldCheck);
return Expression.Lambda<Func<Base, bool>>(binaryExprFieldCheck, new ParameterExpression[] { xParam });
}
}
...
}
public class Account : Base, IUniqueable<Account>
{
...
public new Expression<Func<Account, bool>> Unique
{
get
{
var xParam = Expression.Parameter(typeof(Account), typeof(Account).Name);
MemberExpression leftExprNameCheck = MemberExpression.Property(xParam, "Name");
Expression rightExprNameCheck = Expression.Constant(this.Name);
BinaryExpression binaryExprNameCheck = MemberExpression.Equal(leftExprNameCheck, rightExprNameCheck);
MemberExpression leftExprFieldCheck = MemberExpression.Property(xParam, "AccountCode");
Expression rightExprFieldCheck = Expression.Constant(this.AccountCode);
BinaryExpression binaryExprFieldCheck = MemberExpression.Equal(leftExprFieldCheck, rightExprFieldCheck);
BinaryExpression binaryExprAllCheck = Expression.OrElse(binaryExprNameCheck, binaryExprFieldCheck);
return Expression.Lambda<Func<Account, bool>>(binaryExprAllCheck, new ParameterExpression[] { xParam });
}
}
...
}
public static class Manager
{
...
public static T Save<T>(T item) where T : Base, new()
{
if (!item.IsValid)
{
throw new ValidationException("Unable to save item, item is not valid", item.GetRuleViolations());
}
if (item.Id == Guid.Empty && repository.GetAll<T>().Any(((IUniqueable<T>)item).Unique))
{
throw new Exception("Item is not unique");
}
return repository.Save<T>(item);
}
...
}
Essentially by implementing the IUniqueable interface for a specific type I can return a different Expression for each type. All good :-)

How to get properties Names from object parameter?

i write some utility class but how to get name ? Can that send parameter Name to Mehod not use object.Name?
class AutoConfig_Control {
public List<string> ls;
public AutoConfig_Control(List<string> lv)
{
ls = lv;
}
public void Add(object t)
{
//t.Name; << How to do this
//Howto Get t.Name in forms?
ls.Add(t.ToString());
}
}
class AutoConfig
{
List<string> ls = new List<string>();
public string FileConfig
{
set;
get;
}
public AutoConfig_Control Config
{
get {
AutoConfig_Control ac = new AutoConfig_Control(ls);
ls = ac.ls;
return ac;
}
//get;
}
public string SaveConfig(object t) {
return ls[0].ToString();
}
}
Example For use.
AutoConfig.AutoConfig ac = new AutoConfig.AutoConfig();
ac.Config.Add(checkBox1.Checked);
MessageBox.Show(ac.SaveConfig(checkBox1.Checked));
The only way i know is a hack like this:
/// <summary>
/// Gets the name of a property without having to write it as string into the code.
/// </summary>
/// <param name="instance">The instance.</param>
/// <param name="expr">An expression like "x => x.Property"</param>
/// <returns>The name of the property as string.</returns>
public static string GetPropertyName<T, TProperty>(this T instance, Expression<Func<T, TProperty>> expr)
{
var memberExpression = expr.Body as MemberExpression;
return memberExpression != null
? memberExpression.Member.Name
: String.Empty;
}
If you place it in a static class you will get the extension method GetPropertyName.
Then you can use it in your example like
checkbox1.GetPropertyName(cb => cb.Checked)
Completing Rauhotz answer :
Expression.Body might be a UnaryExpression (for boolean properties for example)
Here's what you should do to work with these kind of expressions:
var memberExpression =(expression.Body is UnaryExpression? expression.Body.Operand :expression.Body) as MemberExpression;

Categories

Resources