添加特定于包含的实体的where子句

时间:2012-11-05 19:14:00

标签: c# linq entity-framework

我的数据模型有许多具有共同父级的表。我正在使用实体框架并从子对象返回到父对象的导航属性,而不是其他方式。原因是这个共同父母将有100个孩子。每个报告的标准都是相同的,例如:

 crudEngine.Read<ChildEntity>()
   .Include(parameters => parameters.ParentEntity)
   .Where(parameter => parameter.ParentEntity.LastUser.Contains(searchText) ||
         (isInt && SqlFunctions.DatePart("year", parameter.ParentEntity.CreationDate) == intSearchText) ||
         (isInt && SqlFunctions.DatePart("month", parameter.ParentEntity.CreationDate) == intSearchText) ||
         (isInt && SqlFunctions.DatePart("day", parameter.ParentEntity.CreationDate) == intSearchText) ||
         (isDate && SqlFunctions.DateAdd("dy", SqlFunctions.DateDiff("dy", "1900-01-01", parameter.ParentEntity.CreationDate), "1900-01-01") == dateSearchText) ||
         (isInt && SqlFunctions.DatePart("year", parameter.ParentEntity.LastModifiedDate) == intSearchText) ||
         (isInt && SqlFunctions.DatePart("month", parameter.ParentEntity.LastModifiedDate) == intSearchText) ||
         (isInt && SqlFunctions.DatePart("day", parameter.ParentEntity.LastModifiedDate) == intSearchText) ||
         (isDate && SqlFunctions.DateAdd("dy", SqlFunctions.DateDiff("dy", "1900-01-01", parameter.ParentEntity.LastModifiedDate), "1900-01-01") == dateSearchText) ||
          parameter.ParentEntity.CreationUser.Contains(searchText) ||
          parameter.ParentEntity.Description.Contains(searchText)...

这个标准将用于引用ParentEntity的每个子实体。如果可能的话,我想以某种方式将这个部分分成它自己的函数,如下所示:

private Predicate<ParentEntity> GetParentWhere(string searchText)
    {
        int intSearchText;
        bool isInt = int.TryParse(searchText, out intSearchText);

        DateTime dateSearchText;
        bool isDate = DateTime.TryParse(searchText, out dateSearchText);

        SubmissionStatus submissionStatus;
        bool isStatus = Enum.TryParse(searchText, true, out submissionStatus);

        return parent =>                
               (isInt && SqlFunctions.DatePart("year", parent.CreationDate) == intSearchText) ||
               (isInt && SqlFunctions.DatePart("month", parent.CreationDate) == intSearchText) ||
               (isInt && SqlFunctions.DatePart("day", parent.CreationDate) == intSearchText) ||
               (isDate && SqlFunctions.DateAdd("dy", SqlFunctions.DateDiff("dy", "1900-01-01", parent.CreationDate), "1900-01-01") == dateSearchText) ||
               (isInt && SqlFunctions.DatePart("year", parent.LastModifiedDate) == intSearchText) ||
               (isInt && SqlFunctions.DatePart("month", parent.LastModifiedDate) == intSearchText) ||
               (isInt && SqlFunctions.DatePart("day", parent.LastModifiedDate) == intSearchText) ||
               (isDate && SqlFunctions.DateAdd("dy", SqlFunctions.DateDiff("dy", "1900-01-01", parent.LastModifiedDate), "1900-01-01") == dateSearchText) ||
               parent.CreationUser.Contains(searchText) ||
               parent.Description.Contains(searchText) ||
               parent.LastUser.Contains(searchText) ||
               (isStatus && parent.Status == (int) submissionStatus);

    }

然后从我的代码中调用它,如下所示:

        return crudEngine.Read<ChildEntity>().Include(parameters => parameters.ParentEntity)
                         .Where(GetParentWhere(searchText));

但这不起作用,因为Where表达式是使用子类型而不是父类型键入的。有关如何实现这一点的任何想法?

谢谢!

1 个答案:

答案 0 :(得分:2)

最简单的选择是让您的子实体实现一个公开ParentEntity属性的接口:

public interface IHaveParentEntity
{
   ParentEntity ParentEntity { get; }
}

private Expression<Func<TChildEntity, bool>> GetParentWhere<TChildEntity>(string searchText) 
   where TChildEntity : IHaveParentEntity
{
   int intSearchText;
   bool isInt = int.TryParse(searchText, out intSearchText);

   DateTime dateSearchText;
   bool isDate = DateTime.TryParse(searchText, out dateSearchText);

   SubmissionStatus submissionStatus;
   bool isStatus = Enum.TryParse(searchText, true, out submissionStatus);

   return child => 
      child.ParentEntity.CreationUser.Contains(searchText) ||
      ...
   ;
}

...

return crudEngine.Read<ChildEntity>()
    .Include(parameters => parameters.ParentEntity)
    .Where(GetParentWhere<ChildEntity>(searchText));

如果您无法修改子实体,则可能需要查看构建表达式树。由于查询的复杂性,使用表达式重写可能更容易,而不是手工构建整个表达式:

private Expression<Func<TChildEntity, bool>> GetParentWhere<TChildEntity>(
   Expression<Func<TChildEntity, ParentEntity>> parentSelector, 
   string searchText)
{
   int intSearchText;
   bool isInt = int.TryParse(searchText, out intSearchText);

   DateTime dateSearchText;
   bool isDate = DateTime.TryParse(searchText, out dateSearchText);

   SubmissionStatus submissionStatus;
   bool isStatus = Enum.TryParse(searchText, true, out submissionStatus);

   Expression<Func<ParentEntity, bool>> predicate = parent => 
      parent.CreationUser.Contains(searchText) ||
      ...
   ;

   Expression body = ReplacementVisitor.Replace(
      predicate, 
      predicate.Parameters[0], 
      parentSelector.Body);

   return Expression.Lambda<Func<TChildEntity, bool>>(body, 
      parentSelector.Parameters[0]);
}

private sealed class ReplacementVisitor : ExpressionVisitor
{
  private IList<ParameterExpression> SourceParameters { get; set; }
  private Expression ToFind { get; set; }
  private Expression ToReplace { get; set; }

  public static Expression Replace(
      LambdaExpression source, 
      Expression toFind, 
      Expression toReplace)
  {
     var visitor = new ReplacementVisitor
     {
        SourceParameters = source.Parameters,
        ToFind = toFind,
        ToReplace = toReplace,
     };

     return visitor.Visit(source.Body);
  }

  private Expression ReplaceNode(Expression node)
  {
     return (node == ToFind) ? ToReplace : node;
  }

  protected override Expression VisitParameter(ParameterExpression node)
  {
     if (SourceParameters.Contains(node)) return ReplaceNode(node);
     return SourceParameters.FirstOrDefault(p => p.Name == node.Name) ?? node;
  }
}

...

return crudEngine.Read<ChildEntity>()
    .Include(parameters => parameters.ParentEntity)
    .Where(GetParentWhere<ChildEntity>(child => child.ParentEntity, searchText));