在Expression <func <t1,bool =“”>&gt;中转换类型表达式<func <t2,bool =“”>&gt; (LINQ to SQL)

时间:2017-01-17 08:55:35

标签: c# linq mapping expression func

注意:

  1. 我的应用程序(asp.net mvc)在图层上是分开的,用于松耦合目的。
  2. 实体krt_Naftan和ScrollLineDTO具有相同的属性并在不同的层中使用。我使用Automapper在它们之间进行转换。
  3. 我想通过LINQ to SQL(表达式树)使用一些谓词(x => x.DTBUHOTCHET == '01.01.2016')。
  4. 适用于func:

    Func<ScrollLineDTO, bool> predicate = x => x.DTBUHOTCHET == '01.01.2016';
    Func<krt_Naftan, bool> func = x => predicate(Mapper.Map<ScrollLineDTO>(x));
    

    因为我无法将func包装到Expression树(LINQ to SQL) //不适用于EF6

    Expression<Func<krt_Naftan, bool>> filter = x => func(x);
    

    我尝试在表达式中转换类型(编译错误)

    Expression<Func<ScrollLineDTO, bool>> predicate = x => x.DTBUHOTCHET == '01.01.2016';
    
    Expression<Func<krt_Naftan, bool>> func = x =>predicate(Mapper.Map<ScrollLineDTO>(x));
    

    问题:如何在表达式树语句中使用转换功能?或者可能需要别的东西;)

    1)索引方法(UI层)

    public ActionResult Index(DateTime? period = null, int page = 1, bool asService = false, ushort initialSizeItem = 15) {
        if (Request.IsAjaxRequest()) {
            long recordCount;
    
            //default
            Expression<Func<ScrollLineDTO, bool>> predicate = x => x.DTBUHOTCHET == (period == null ? new DateTime(DateTime.Today.Year, DateTime.Today.Month, 1) : period);
    
            var result = new IndexMV() {
                ListKrtNaftan = _bussinesEngage.SkipTable(page, initialSizeItem, out recordCount, predicate),
                    ...
                }
            };
      ....
    

    2)(BLL层)

     public IEnumerable<T> SkipTable<T>(int page, int initialSizeItem, out long recordCount, Expression<Func<T, bool>> predicate = null) {
    
                if (predicate == null) {
                    //convert func types
    
                    Expression<Func<krt_Naftan, bool>> func = x => predicate(Mapper.Map<ScrollLineDTO>(x));
                    //wrap in func to expression (is not impossible, maybe if pass method...)
                    //Expression<Func<krt_Naftan, bool>> filter = x => func(x);
    
                    return (IEnumerable<T>)Mapper.Map<IEnumerable<ScrollLineDTO>>(Engage.GetSkipRows(page, initialSizeItem, out recordCount, x => x.KEYKRT, func));
                }
    
                return (IEnumerable<T>)Mapper.Map<IEnumerable<ScrollLineDTO>>(Engage.GetSkipRows<krt_Naftan, long>(page, initialSizeItem, out recordCount, x => x.KEYKRT));
            }
    
    /// <summary>
    /// Return pagging part of table and general count of rows
    /// </summary>
    /// <typeparam name="T">Current enity</typeparam>
    /// <typeparam name="TKey">Type for ordering</typeparam>
    /// <param name="page">Number page</param>
    /// <param name="size">Count row per one page</param>
    /// <param name="recordCount"></param>
    /// <param name="orderPredicate">Condition for ordering</param>
    /// <param name="filterPredicate">Condition for filtering</param>
    /// <param name="caсhe"></param>
    /// <returns>Return definition count rows of specific entity</returns>
    public IEnumerable<T> GetSkipRows<T, TKey>(int page, int size, out long recordCount, Expression<Func<T, TKey>> orderPredicate, Expression<Func<T, bool>> filterPredicate = null, bool caсhe = false) where T : class {
        recordCount = GetCountRows(filterPredicate);
        using (Uow = new UnitOfWork()) {
            return Uow.Repository<T>().Get_all(filterPredicate, caсhe).OrderByDescending(orderPredicate).Skip((page - 1) * size).Take(size).ToList();
        }
    }
    

    3)(DLL层)从db

    获取数据
    /// <summary>
                /// Get lazy data set (with cashing or not (attr MergeOption )
                /// </summary>
                /// <param name="predicate">filter condition for retrieving data from source(database)</param>
                /// <param name="enableDetectChanges">Compare two snapshot of data (one when retrieve data from database other when call method saveChanges(). If exists some diffrences => generate avaible SQL command</param>
                /// <param name="enableTracking"></param>
                /// <returns></returns>
                public IQueryable<T> Get_all(Expression<Func<T, bool>> predicate = null, bool enableDetectChanges = true, bool enableTracking = true) {
                    /*//sync data in Db & EF (if change not tracking for EF)
                        ((IObjectContextAdapter)_context).ObjectContext.Refresh(RefreshMode.StoreWins, _dbSet.Where(predicate));
                        _context.Entry(_dbSet.Where(predicate)).Reload(); EF 4.1+*/
                    ActiveContext.Configuration.AutoDetectChangesEnabled = enableDetectChanges;
                    if (predicate == null) return (enableTracking) ? _dbSet : _dbSet.AsNoTracking();
                    var result = (enableTracking) ? _dbSet.Where(predicate) : _dbSet.AsNoTracking().Where(predicate);
    
                    return result;
                }
    

    THX)

2 个答案:

答案 0 :(得分:1)

我找到了我需要的东西!

  1. Replace a type in an expression tree
  2. How to change a type in an expression tree?
  3. Solusion是访问者模式(lINQ具有内置ExpressionVisitor实现(С#4.0))。

    在我的案例中,实现是:

      public static Expression<Func<OutT, bool>> ConvertTypeExpression<inT, OutT>(Expression expression) where OutT : class {
    
                var param = Expression.Parameter(typeof(OutT), "x");
    
                var result = new CustomExpVisitor<OutT>(param).Visit(expression); 
    
                Expression<Func<OutT, bool>> lambda = Expression.Lambda<Func<OutT, bool>>(result, new[] { param });
    
                return lambda;
            }
    
       private class CustomExpVisitor<T> : ExpressionVisitor {
                ParameterExpression _param;
    
                public CustomExpVisitor(ParameterExpression param) {
                    _param = param;
                }
    
                protected override Expression VisitParameter(ParameterExpression node) {
                    return _param;
                }
    
                protected override Expression VisitMember(MemberExpression node) {
                    if (node.Member.MemberType == MemberTypes.Property) {
                        MemberExpression memberExpression = null;
    
                        var memberName = node.Member.Name;
                        var otherMember = typeof(T).GetProperty(memberName);
    
                        memberExpression = Expression.Property(Visit(node.Expression), otherMember);
    
                        return memberExpression;
                    } else {
                        return base.VisitMember(node);
                    }
                }
            }
    

    最后我得到了我搜索过的函数

    var filterPredicate = PredicateExtensions.ConvertTypeExpression<ScrollLineDTO, krt_Naftan>(predicate.Body);
    

答案 1 :(得分:0)

此解决方案适用于单个表达式(x =&gt; x.TypeId ==“MyType”),但此时它在生产中有效。

T1和T2不需要彼此分配。

步骤如下: 1.将表达式转换为字典 2.生成等式表达式(您可以实现其他比较)

我随着时间的推移将这些库拼凑在一起,不记得它们的来源,但这里是主要的方法。

public class ExpressionHelper
{

    public static IDictionary<string, object> GetMethodParams<T>(Expression<Func<T, bool>> fromExpression)
    {
        if (fromExpression == null) return null;


        var body = fromExpression.Body as BinaryExpression;
        if (body == null) return new Dictionary<string, object>();
        var rVal = new Dictionary<string, object>();

        var leftLambda = body.Left as BinaryExpression;
        if (leftLambda != null)
        {
            var params1 = GetExpressionParams(leftLambda);
            foreach (var o in params1) rVal.Add(o.Key, o.Value);
        }
        var rightLambda = body.Right as BinaryExpression;
        if (rightLambda != null)
        {
            var params1 = GetExpressionParams(rightLambda);
            foreach (var o in params1) rVal.Add(o.Key, o.Value);
        }
        else
        {
            var params1 = GetExpressionParams(body);
            foreach (var o in params1) rVal.Add(o.Key, o.Value);
        }

        return rVal;
    }

    /// <summary>
    ///     Get Expression Parameters Recursively
    /// </summary>
    /// <param name="body"></param>
    /// <returns></returns>
    private static IDictionary<string, object> GetExpressionParams(BinaryExpression body)
    {
        if (body == null) return new Dictionary<string, object>();

        var rVal = new Dictionary<string, object>();

        var leftLambda = body.Left as BinaryExpression;
        while (leftLambda != null)
        {
            var params1 = GetExpressionParams(leftLambda);
            foreach (var o in params1) if (!rVal.ContainsKey(o.Key)) rVal.Add(o.Key, o.Value);
            leftLambda = body.Left as BinaryExpression;
        }
        var rightLambda = body.Right as BinaryExpression;
        while (rightLambda != null)
        {
            var params1 = GetExpressionParams(rightLambda);
            foreach (var o in params1) if (!rVal.ContainsKey(o.Key)) rVal.Add(o.Key, o.Value);
            rightLambda = body.Right as BinaryExpression;
        }

        var rightValue = GetValue(body.Right);
        if (rightValue == null)
        {
            var rightSide = body.Right as ConstantExpression;
            if (rightSide != null) rightValue = rightSide.Value;
        }

        var leftSideName = GetMemberName(body.Left);
        if (string.IsNullOrEmpty(leftSideName)) return rVal;
        if (!rVal.ContainsKey(leftSideName)) rVal.Add(leftSideName, rightValue);

        return rVal;
    }

    /// <summary>
    ///     Get an Equals Expression from the name and value
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="propertyName"></param>
    /// <param name="value"></param>
    /// <returns></returns>
    public static Expression<Func<T, bool>> GetEqualExpression<T>(string propertyName, object value)
    {
        var p = Expression.Parameter(typeof(T));
        var property = Expression.Property(p, propertyName);

        Expression propertyExpression = Expression.Call(property, property.Type.GetMethod("ToString", Type.EmptyTypes));

        var equalsExpression = Expression.Equal(propertyExpression, Expression.Constant(value?.ToString()));

        var lambda = Expression.Lambda<Func<T, bool>>(equalsExpression, p);
        return lambda;
    }
}

使用具有相同属性StatusId的项目。

public class DataReader
{

    /// <summary>
    ///     Get your list via data manager or something
    /// </summary>
    public List<T> ListItems<T>()
        where T : IStatusIdProperty
    {
        return new List<T>();
    }

    public List<T> GetPubItems<T, TView>()
        where T:IStatusIdProperty
        where TView : IStatusIdProperty
    {
        var expression = ConvertExpression<T, TView>(x => x.StatusId == "Pub");
        return ListItems<T>().Where(expression.Compile()).ToList();
    }


    public Expression<Func<T, bool>> ConvertExpression<T, TView>(Expression<Func<TView, bool>> predicate)
        where T : IStatusIdProperty
        where TView : IStatusIdProperty
    {
        var paramDictionary = predicate.GetParamsDictionary().FirstOrDefault();
        var expression = ExpressionHelper.GetEqualExpression<T>(paramDictionary.Key, paramDictionary.Value);
        return expression;
    }

}