C# 添加特定于包含实体的where子句
我的数据模型有许多具有公共父级的表。我使用的是实体框架,并且有一个从子对象返回到父对象的导航属性,而不是相反。这样做的原因是,这个共同父母的孩子将有100个。每个报告的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
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()
调用。实际上,这也是我最终得到的结果。再次感谢!:)