繁体   English   中英

在表达式中转换类型<Func<T1, bool> &gt; 表达<Func<T2, bool> &gt; (LINQ to SQL)

[英]Convert type in Expression<Func<T1, bool>> to Expression<Func<T2, bool>> (LINQ to SQL)

笔记:

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

它适用于 func:

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

因为我无法将 func 包装到表达式树(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;
            }

谢谢)

我找到了我需要的东西!

  1. 替换表达式树中的类型
  2. 如何更改表达式树中的类型?

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);

此解决方案适用于单个表达式 (x=>x.TypeId=="MyType"),但此时它在生产中工作了一段时间。

T1 和 T2 不需要相互分配。

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

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

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;
    }

}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM