2017-01-17 5 views
2

Примечание:типа Преобразовать в Expression <Func <T1, bool>> в Expression <Func <T2, bool>> (LINQ к SQL)

  1. Мое приложение (asp.net MVC) разделяется на слои для слабосвязанных целей.
  2. Объект krt_Naftan и ScrollLineDTO имеют одинаковое свойство и используют в разные слои. Я использую Automapper для преобразования между ними.
  3. Я хотел бы использовать некоторый предикат (x => x.DTBUHOTCHET == '01.01.2016') Недостаток LINQ to SQL (дерево выражений).

Это работа для FUNC:

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

Потому что я не могу обернуть FUNC для выражения дерева (LINQ к SQL) // не работает EF6

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

I попробуйте преобразовать тип в выражения (ошибка компиляции)

Expression<Func<ScrollLineDTO, bool>> predicate = x => x.DTBUHOTCHET == '01.01.2016'; 

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

Вопрос: Как я могу использовать функцию преобразования для выражений дерева выражений? или, возможно, необходимо что-то еще;)

1) Метод индекса (ПИ слой)

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) (слой БЛЛ)

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-слой) получить данные из БД

/// <summary> 
      /// Get lazy data set (with cashing or not (attr MergeOption) 
      /// </summary> 
      /// <param name="predicate">filter condition for retriew data from source(database)</param> 
      /// <param name="enableDetectChanges">Compare two snapshot of data (one when retriew 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; 
      } 

ТНХ)

+0

Что не работает и что вы пытаетесь сделать? Почему вы пытаетесь преобразовать выражения? Linq to SQL может позволить выражениям, которые приводят к загрузке всего в память. Linq to EF правильно предотвращает их и выдает исключение. –

+0

Почему вы используете выражения вместо написания, например 'myQuery = myQuery.Where (x => x.DTBUHOTCHET ==" 01.01.2016 ");'? Похоже, вы пытаетесь создать какие-то динамические критерии, но это можно легко сделать, используя различные инструкции 'Where' –

+0

Да, у меня есть« общая »служба для запроса к Db i, использую общий метод (не относящийся к типу объекта и тип лямбда-предиката). т.е. IEnumerable GetSkipRows (int page, int size, out long recordCount, Expression > orderPredicate, Expression > filterPredicate = null, bool caсhe = false) где T: class; – AllmanTool

ответ

1

Я нашел то, что мне нужно!

  1. Replace a type in an expression tree
  2. How to change a type in an expression tree?

Solusion является паттерн посетитель (Linq есть Встроенный ExpressionVisitor implementaion (С # 4.0)).

В моем случае implementaion является:

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

и в конце концов я получил functionaly, что я искал)

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

Пожалуйста, отметьте этот ответ как принятый. –

+0

Как я могу адаптировать это выражение для использования параметров с разными названиями: это работает: _visitService.Get (х => x.CustomerId == firstDbVisit.CustomerId) , но это не делает: _visitService.Get (х => x.CustomerId == firstCustomer.Id) Поскольку имена параметров разные. –

0

Это решение работает для отдельных выражений (х => 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; 
    } 

} 
Смежные вопросы