Expression Cast Error - No coercion operator is defined between types
Asked Answered
U

1

12

In my Data Repository I have a base class and derived class as below.

public abstract class RepositoryBase<T> : IRepository<T> where T : EntityBase
{
    public async Task<T> FindOneAsync(Expression<Func<T, bool>> predicate)
    {
        List<T> list = await SearchForAsync(predicate);
        return list.FirstOrDefault();
    }
}

public class CommentUrlRepository : RepositoryBase<CommentUrl>, ICommentUrlRepository
{
    public async Task<CommentUrlCommon> FindOneAsync(
        Expression<Func<CommentUrlCommon, bool>> predicate
    )
    {
        Expression<Func<CommentUrl, bool>> lambda = Cast(predicate);
        CommentUrl commentUrl = await FindOneAsync(lambda);
        return MappingManager.Map(commentUrl);
    }

    private Expression<Func<CommentUrl, bool>> Cast(
        Expression<Func<CommentUrlCommon, bool>> predicate
    )
    {
        Expression converted =
            Expression.Convert(
                predicate,
                typeof(Expression<Func<CommentUrl, bool>>)
            );

        // throws exception
        // No coercion operator is defined between types
        return Expression.Lambda<Func<CommentUrl, bool>>(converted, predicate.Parameters);
    }
}

When I hit "Cast" function I am getting following error:

No coercion operator is defined between types 'System.Func`2[CommentUrlCommon,System.Boolean]' and 'System.Linq.Expressions.Expression`1[System.Func`2[CommentUrl,System.Boolean]]'.

How can I cast this Expression value?

Upside answered 2/11, 2016 at 21:53 Comment(1)
You'll have to pull apart and rebuild the expression, at the very least by substituting its parameter (and possibly any member access expressions on the parameter). It can be hard work and very easy to get wrong. You can (and probably should) avoid doing this by not exposing any methods that accept expressions. Use more specialised methods (i.e. FindOneAsync(int primaryKey)) instead.Scalpel
R
3

I think what you want cannot be done...
Check this question for more.
If you are lucky and your expression is simple, Marc Gravell's Convert method might work for you

And a simpler example that demonstrates your problem

using System;
using System.Linq.Expressions;

namespace Program
{
    internal class Program
    {
        private static void Main(string[] args)
        {
            Expression<Func<CommentUrlCommon, bool>> predicate = f => f.Id == 1;

            //As you know this doesn't work
            //Expression converted = Expression.Convert(predicate, typeof(Expression<Func<CommentUrl, bool>>));

            //this doesn't work either...
            Expression converted2 = Expression.Convert(predicate, typeof(Expression<Func<CommentUrlCommon, bool>>));

            Console.ReadLine();
        }
    }

    public class CommentUrlCommon
    {
        public int Id { get; set; }
    }

    public class CommentUrl
    {
        public int Id { get; set; }
    }
}
Reimburse answered 2/11, 2016 at 23:44 Comment(2)
The solution provided in the link you supplied worked great. Thanks!Upside
Great example of building a System.Linq.Expressions.Expression directly from a Linq query. Previously I was trying to do this by reflection and it was not pleasantGermiston

© 2022 - 2024 — McMap. All rights reserved.