Is it possible to change the generic delegate type parameter Func <T, bool>?
I believe the answer to this question is no, but it doesnโt matter here.
Basically, I have a Linq2Sql data Linq2Sql with its generated types. I also have business objects whose property names (for the sake of this question) exactly match the property names of the types associated with them. Business types are used throughout the application, and the generated types are used ONLY to access the database - this setting is desirable for a number of reasons, so please do not offer answers that require any changes to this.
There are various controls in the user interface layer that allow the user to customize the search methods, for example. what fields to search, search terms, etc. Using these controls, I can create a nice Func<T, bool> delegate to encapsulate search conditions / queries. The problem is that the Func delegate is created with a parameter of type T , which is a business object, and when it is passed to the data access layer, I need it to be associated with the corresponding generated type.
So my question is: can I change the generic Func delegate type parameter from a business object type to its associated type while maintaining the same conditions?
eg. can Func<MasterTrack, bool> => Func<DbMasterTrack, bool> when properties match? Also note that I can simply transfer all user-selected search parameters to the data access level, but there are quite a lot of them, so I was hoping to avoid this.
I do not believe that this is possible, but you can avoid the following actions:
- Make
DbMasterTrackimplicitly convertible toMasterTrack; - When prompted, simply wrap
Func<MasterTack,bool>inFunc<DbMasterTrack,bool>.
I should also note that if you use Func<T, bool> instead of Expression<Func<T, bool> , that you are not actually filtering the result set at the database level, but this may be what you already know .
Example:
class MasterTrack { public int Id { get; set; } public string Name { get; set; } } class DbMasterTrack { public int Id { get; set; } public string Name { get; set; } public static implicit operator MasterTrack(DbMasterTrack @this) { return new MasterTrack { Id = @this.Id, Name = @this.Name }; } } class Program { static void Main(string[] args) { var tracks = new List<DbMasterTrack> { new DbMasterTrack { Id = 1, Name = "T1" }, new DbMasterTrack { Id = 2, Name = "T2" }, }; Func<MasterTrack, bool> query = t => t.Id == 1; var result = tracks.Where((Func<DbMasterTrack, bool>)(t => query(t))); foreach (var item in result) { Console.WriteLine("{0}|{1}", item.Id, item.Name); } } } A few things
Linq2Sql can also use
Expression<Func<T, bool>>instead ofFunc<T, bool>.Cannot change type
Expression<Func<T, bool>>Is it possible to copy / recreate
Expression<Func<T, bool>>, where you replace type T with another type.Linq2Sql will NOT accept an interface type in its expression. Therefore, if you are thinking of creating interfaces for the "abstract" actual type, this will not work.
Now, to create Expression<Func<T2, bool>> from Expression<Func<T, bool>> , I once created the following code. It is not โcomplete,โ since not all possible paths in an expression are supported. But the main and / or combinations, where you check the properties for values โโ(<> =! = Or combinations), then work fine.
Using this code, you can:
Expression<Func<MasterTrack, bool>> criteria = m => m.Id == 1; Expression<Func<DbMasterTrack, bool>> dbCriteria = ExpressionRewriter.CastParam<MasterTrack, DbMasterTrack>(criteria); Here we go.
public static class ExpressionRewriter { /// <summary> /// Casts the param of an expression. /// </summary> /// <typeparam name="TIn">The type of the in.</typeparam> /// <typeparam name="TOut">The type of the out.</typeparam> /// <param name="inExpr">The in expr.</param> /// <returns></returns> public static Expression<Func<TOut, bool>> CastParam<TIn, TOut>(Expression<Func<TIn, bool>> inExpr) { if (inExpr.NodeType == ExpressionType.Lambda && inExpr.Parameters.Count > 0) { var inP = inExpr.Parameters[0]; var outP = Expression.Parameter(typeof(TOut), inP.Name); var outBody = Rewrite<TIn, TOut>( inExpr.Body, expr => (expr is ParameterExpression) ? outP : expr ); return Expression.Lambda<Func<TOut, bool>>( outBody, new ParameterExpression[] { outP }); } else { throw new NotSupportedException(); } } /// <summary> /// Rewrites the specified expression. /// </summary> /// <typeparam name="TIn">The type of the in.</typeparam> /// <typeparam name="TOut">The type of the out.</typeparam> /// <param name="exp">The exp.</param> /// <param name="c">The c.</param> /// <returns></returns> private static Expression Rewrite<TIn, TOut>(Expression exp, Func<Expression, Expression> c) { Expression clone = null; var be = exp as BinaryExpression; switch (exp.NodeType) { case ExpressionType.AndAlso: clone = Expression.AndAlso(Rewrite<TIn, TOut>(be.Left, c), Rewrite<TIn, TOut>(be.Right, c), be.Method); break; case ExpressionType.OrElse: clone = Expression.OrElse(Rewrite<TIn, TOut>(be.Left, c), Rewrite<TIn, TOut>(be.Right, c), be.Method); break; case ExpressionType.Equal: clone = Expression.Equal(Rewrite<TIn, TOut>(be.Left, c), Rewrite<TIn, TOut>(be.Right, c), be.IsLiftedToNull, be.Method); break; case ExpressionType.GreaterThan: clone = Expression.GreaterThan(Rewrite<TIn, TOut>(be.Left, c), Rewrite<TIn, TOut>(be.Right, c), be.IsLiftedToNull, be.Method); break; case ExpressionType.GreaterThanOrEqual: clone = Expression.GreaterThanOrEqual(Rewrite<TIn, TOut>(be.Left, c), Rewrite<TIn, TOut>(be.Right, c), be.IsLiftedToNull, be.Method); break; case ExpressionType.LessThan: clone = Expression.LessThan(Rewrite<TIn, TOut>(be.Left, c), Rewrite<TIn, TOut>(be.Right, c), be.IsLiftedToNull, be.Method); break; case ExpressionType.LessThanOrEqual: clone = Expression.LessThanOrEqual(Rewrite<TIn, TOut>(be.Left, c), Rewrite<TIn, TOut>(be.Right, c), be.IsLiftedToNull, be.Method); break; case ExpressionType.NotEqual: clone = Expression.NotEqual(Rewrite<TIn, TOut>(be.Left, c), Rewrite<TIn, TOut>(be.Right, c), be.IsLiftedToNull, be.Method); break; case ExpressionType.Not: var ue = exp as UnaryExpression; clone = Expression.Not(Rewrite<TIn, TOut>(ue.Operand, c)); break; case ExpressionType.MemberAccess: var me = exp as MemberExpression; MemberInfo newMember = me.Member; Type newType = newMember.DeclaringType; if (newType == typeof(TIn)) { newType = typeof(TOut); MemberInfo[] members = newType.GetMember(me.Member.Name); if (members.Length == 1) { newMember = members[0]; } else { throw new NotSupportedException(); } } clone = Expression.MakeMemberAccess(Rewrite<TIn, TOut>(me.Expression, c), newMember); break; case ExpressionType.Constant: var ce = exp as ConstantExpression; clone = Expression.Constant(ce.Value); break; case ExpressionType.Parameter: var pe = exp as ParameterExpression; Type peNewType = pe.Type; if (peNewType == typeof(TIn)) { peNewType = typeof(TOut); } clone = Expression.Parameter(peNewType, pe.Name); break; case ExpressionType.Call: MethodCallExpression mce = exp as MethodCallExpression; if (mce.Arguments != null && mce.Arguments.Count > 0) { List<Expression> expressionList = new List<Expression>(); foreach (Expression expression in mce.Arguments) { expressionList.Add(Rewrite<TIn, TOut>(expression, c)); } clone = Expression.Call(Rewrite<TIn, TOut>(mce.Object, c), mce.Method, expressionList.ToArray()); } else { clone = Expression.Call(Rewrite<TIn, TOut>(mce.Object, c), mce.Method); } break; case ExpressionType.Invoke: InvocationExpression ie = exp as InvocationExpression; List<Expression> arguments = new List<Expression>(); foreach (Expression expression in ie.Arguments) { arguments.Add(Rewrite<TIn, TOut>(expression, c)); } clone = Rewrite<TIn, TOut>(ie.Expression, c); //clone = Expression.Invoke(Rewrite<TIn, TOut>(ie.Expression, c), arguments); break; case ExpressionType.Convert: var ue2 = exp as UnaryExpression; //clone = Expression.Not(Rewrite<TIn, TOut>(ue2.Operand, c)); clone = Expression.Convert(ue2.Operand, ue2.Type, ue2.Method); break; default: throw new NotImplementedException(exp.NodeType.ToString()); } return c(clone); } }