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

C# 添加特定于包含实体的where子句,c#,linq,entity-framework,C#,Linq,Entity Framework,我的数据模型有许多具有公共父级的表。我使用的是实体框架,并且有一个从子对象返回到父对象的导航属性,而不是相反。这样做的原因是,这个共同父母的孩子将有100个。每个报告的where标准相同,例如: crudEngine.Read<ChildEntity>() .Include(parameters => parameters.ParentEntity) .Where(parameter => parameter.ParentEntity.LastUser.Co

我的数据模型有许多具有公共父级的表。我使用的是实体框架,并且有一个从子对象返回到父对象的导航属性,而不是相反。这样做的原因是,这个共同父母的孩子将有100个。每个报告的where标准相同,例如:

 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)...
然后从我的代码中调用它,类似这样:

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));
return crudEngine.Read().Include(参数=>parameters.ParentEntity)
.Where(GetParentWhere(searchText));
但是,这不起作用,因为Where表达式是使用子类型而不是父类型键入的。你有没有想过如何做到这一点


谢谢

最简单的选择是让您的子实体实现一个接口,该接口公开了
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));
公共接口IHaveParentEntity
{
父实体父实体{get;}
}
私有表达式GetParentWhere(字符串搜索文本)
其中TChildEntity:IHaveParentEntity
{
int搜索文本;
bool isInt=int.TryParse(searchText,out intSearchText);
DateTime日期搜索文本;
bool isDate=DateTime.TryParse(searchText,out-dateSearchText);
提交状态提交状态;
bool isStatus=Enum.TryParse(searchText,true,out submissionStatus);
返回子项=>
child.ParentEntity.CreationUser.Contains(searchText)||
...
;
}
...
返回crudEngine.Read()
.Include(参数=>parameters.ParentEntity)
.Where(GetParentWhere(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));
私有表达式GetParentWhere(
表达式父选择器,
字符串搜索(文本)
{
int搜索文本;
bool isInt=int.TryParse(searchText,out intSearchText);
DateTime日期搜索文本;
bool isDate=DateTime.TryParse(searchText,out-dateSearchText);
提交状态提交状态;
bool isStatus=Enum.TryParse(searchText,true,out submissionStatus);
表达式谓词=parent=>
parent.CreationUser.Contains(searchText)||
...
;
表达式体=ReplacementVisitor.Replace(
谓语
predicate.Parameters[0],
(b.机构);
返回表达式.Lambda(body,
parentSelector.Parameters[0]);
}
私有密封类替换访问者:ExpressionVisitor
{
私有IList源参数{get;set;}
私有表达式ToFind{get;set;}
私有表达式替换{get;set;}
公共静态表达式替换(
LambdaExpression源,
表示找到,
表达式(替换)
{
var visitor=新的替换visitor
{
SourceParameters=source.Parameters,
ToFind=ToFind,
重新放置,
};
回访者.访问(来源.主体);
}
专用表达式替换节点(表达式节点)
{
return(node==ToFind)?ToReplace:node;
}
受保护的重写表达式VisitParameter(ParameterExpression节点)
{
if(SourceParameters.Contains(node))返回ReplaceNode(node);
返回SourceParameters.FirstOrDefault(p=>p.Name==node.Name)??节点;
}
}
...
返回crudEngine.Read()
.Include(参数=>parameters.ParentEntity)
.Where(GetParentWhere(child=>child.ParentEntity,searchText));

visitor模式是我考虑过的,并且是沿着这条道路开始的,尽管我希望为了实现这一点,我忽略了一些更容易的东西。谢谢你的回答。事实上,我实现了你的代码Richard,我一直在调用GetParentWhere…第一个参数是表达式类型,但是当按照你指定的方式调用它时,
.Where(GetParentWhere(parameter=>parameter.Parent,searchText))
,我收到一个编译错误,说明类型是错误的:
参数2:无法从“System.Predicate”转换为“System.Linq.Expressions.Expression”
看起来您使用的是标准的
Queryable.Where
方法,该方法需要的是
表达式而不是谓词。我将用您需要做的更改更新第二个代码示例。您需要做的更改是:将返回类型从
谓词
更改为
表达式
;将
表达式.Lambda
更改为
表达式.Lambda
;然后删除
.Compile()
调用。实际上,这也是我最终得到的结果。再次感谢!:)