.Net Core 5 Rest Api Entity Framework 从控制器获取记录列表时出现 Linq 表达式转换错误
.Net Core 5 Rest Api Entity Framework Linq Expression Translation Error While Getting Record List From Controller
我正在做一个项目,在获取查询列表时出错。我将在下面的代码块中讲述。
//AT REPOSITORY SIDE THIS FUNCTION GETS FILTERED LIST OF RECORDS
public List<TEntity> GetList(Expression<Func<TEntity, bool>> filter)
{
using (var context=new MyContext()){
return context.Set<TEntity>().Where(filter).ToList();
}
}
//AT ENTITY SIDE THIS FUNCTION CHECKS IF ALL PROPERTIES ARE EQUAL WITH THE GIVEN OBJECT
public bool PublicPropertiesEqual(object which)
{
var type = this.GetType();
var whichType = which.GetType();
var whichProperties = whichType.GetProperties(BindingFlags.Public | BindingFlags.Instance);
foreach (PropertyInfo pi in type.GetProperties(BindingFlags.Public | BindingFlags.Instance))
{
if(whichProperties.Any(x => x.Name == pi.Name)){
object selfValue = type.GetProperty(pi.Name).GetValue(this, null);
object whichValue = type.GetProperty(pi.Name).GetValue(which,null);
if (selfValue != whichValue && (selfValue == null || !selfValue.Equals(whichValue)))
{
return false;
}
}
}
return true;
}
//AT CONTROLLER SIDE ONLY CALLED REPO GETLIST FUNCTION WITH GIVEN FILTER
[HttpGet]
public IActionResult GetList([FromQuery] TEntity query)
{
List<TEntity> res = _repo.GetList(x = x.PublicPropertiesEqual(query));
return Ok(res);
}
问题
当我执行代码时,出现了这样的错误
InvalidOperationException: The LINQ expression 'DbSet()
.Where(c => c.PublicPropertiesEqual(__query_0))' could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to 'AsEnumerable', 'AsAsyncEnumerable', 'ToList', or 'ToListAsync'. See https://go.microsoft.com/fwlink/?linkid=2101038 for more information.
所以我无法从数据库中获取记录。我尝试在控制器端编写如下所示的自定义表达式树,但它也不起作用。
[HttpGet]
public IActionResult GetList([FromQuery] TEntity query)
{
var param = Expression.Parameter(typeof(TEntity));
var lambda = Expression.Lambda<Func<TEntity,bool>>(
Expression.Call(
param,
_entityType.GetMethod("PublicPropertiesEqual"),
Expression.Constant(query)
),
param
);
List<TEntity> res = _repo.GetList(lambda);
return Ok(res);
}
以及执行这段代码后的错误
InvalidOperationException: The LINQ expression 'DbSet()
.Where(c => c.PublicPropertiesEqual(Customer))' could not be translated. REST OF THE ERROR IS SAME ABOVE...
作为结论,我如何使用 PublicPropertiesEqual(object) 函数过滤查询?
这样的函数很容易写,但是你必须自己过滤掉导航属性:
public static Expression<Func<T, bool>> PublicPropertiesEqual<T>(T entity)
{
var props = typeof(T).GetProperties();
var entityParam = Expression.Parameter(typeof(T), "e");
var entityExpr = Expression.Constant(entity);
var equality = props.Select(p => Expression.Equal(
Expression.MakeMemberAccess(entityParam, p),
Expression.MakeMemberAccess(entityExpr, p)
));
var predicate = equality.Aggregate(Expression.AndAlso);
return Expression.Lambda<Func<T, bool>>(predicate, entityParam);
}
用法很简单:
List<TEntity> res = _repo.GetList(PublicPropertiesEqual(query));
无论如何,如果您可以访问 DbContext
,最好将其传递给函数并重新使用 IModel
信息。
public static Expression<Func<T, bool>> PublicPropertiesEqual<T>(DbContext ctx, T entity)
{
var et = ctx.Model.FindEntityType(typeof(T));
if (et == null)
throw new InvalidOperationException();
var props = et.GetProperties();
var entityParam = Expression.Parameter(typeof(T), "e");
var entityExpr = Expression.Constant(entity);
var equality = props
.Where(p => !p.IsForeignKey() && !p.IsIndexerProperty())
.Select(p => Expression.Equal(
Expression.MakeMemberAccess(entityParam, p.PropertyInfo),
Expression.MakeMemberAccess(entityExpr, p.PropertyInfo)
));
var predicate = equality.Aggregate(Expression.AndAlso);
return Expression.Lambda<Func<T, bool>>(predicate, entityParam);
}
我正在做一个项目,在获取查询列表时出错。我将在下面的代码块中讲述。
//AT REPOSITORY SIDE THIS FUNCTION GETS FILTERED LIST OF RECORDS
public List<TEntity> GetList(Expression<Func<TEntity, bool>> filter)
{
using (var context=new MyContext()){
return context.Set<TEntity>().Where(filter).ToList();
}
}
//AT ENTITY SIDE THIS FUNCTION CHECKS IF ALL PROPERTIES ARE EQUAL WITH THE GIVEN OBJECT
public bool PublicPropertiesEqual(object which)
{
var type = this.GetType();
var whichType = which.GetType();
var whichProperties = whichType.GetProperties(BindingFlags.Public | BindingFlags.Instance);
foreach (PropertyInfo pi in type.GetProperties(BindingFlags.Public | BindingFlags.Instance))
{
if(whichProperties.Any(x => x.Name == pi.Name)){
object selfValue = type.GetProperty(pi.Name).GetValue(this, null);
object whichValue = type.GetProperty(pi.Name).GetValue(which,null);
if (selfValue != whichValue && (selfValue == null || !selfValue.Equals(whichValue)))
{
return false;
}
}
}
return true;
}
//AT CONTROLLER SIDE ONLY CALLED REPO GETLIST FUNCTION WITH GIVEN FILTER
[HttpGet]
public IActionResult GetList([FromQuery] TEntity query)
{
List<TEntity> res = _repo.GetList(x = x.PublicPropertiesEqual(query));
return Ok(res);
}
问题
当我执行代码时,出现了这样的错误
InvalidOperationException: The LINQ expression 'DbSet() .Where(c => c.PublicPropertiesEqual(__query_0))' could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to 'AsEnumerable', 'AsAsyncEnumerable', 'ToList', or 'ToListAsync'. See https://go.microsoft.com/fwlink/?linkid=2101038 for more information.
所以我无法从数据库中获取记录。我尝试在控制器端编写如下所示的自定义表达式树,但它也不起作用。
[HttpGet]
public IActionResult GetList([FromQuery] TEntity query)
{
var param = Expression.Parameter(typeof(TEntity));
var lambda = Expression.Lambda<Func<TEntity,bool>>(
Expression.Call(
param,
_entityType.GetMethod("PublicPropertiesEqual"),
Expression.Constant(query)
),
param
);
List<TEntity> res = _repo.GetList(lambda);
return Ok(res);
}
以及执行这段代码后的错误
InvalidOperationException: The LINQ expression 'DbSet() .Where(c => c.PublicPropertiesEqual(Customer))' could not be translated. REST OF THE ERROR IS SAME ABOVE...
作为结论,我如何使用 PublicPropertiesEqual(object) 函数过滤查询?
这样的函数很容易写,但是你必须自己过滤掉导航属性:
public static Expression<Func<T, bool>> PublicPropertiesEqual<T>(T entity)
{
var props = typeof(T).GetProperties();
var entityParam = Expression.Parameter(typeof(T), "e");
var entityExpr = Expression.Constant(entity);
var equality = props.Select(p => Expression.Equal(
Expression.MakeMemberAccess(entityParam, p),
Expression.MakeMemberAccess(entityExpr, p)
));
var predicate = equality.Aggregate(Expression.AndAlso);
return Expression.Lambda<Func<T, bool>>(predicate, entityParam);
}
用法很简单:
List<TEntity> res = _repo.GetList(PublicPropertiesEqual(query));
无论如何,如果您可以访问 DbContext
,最好将其传递给函数并重新使用 IModel
信息。
public static Expression<Func<T, bool>> PublicPropertiesEqual<T>(DbContext ctx, T entity)
{
var et = ctx.Model.FindEntityType(typeof(T));
if (et == null)
throw new InvalidOperationException();
var props = et.GetProperties();
var entityParam = Expression.Parameter(typeof(T), "e");
var entityExpr = Expression.Constant(entity);
var equality = props
.Where(p => !p.IsForeignKey() && !p.IsIndexerProperty())
.Select(p => Expression.Equal(
Expression.MakeMemberAccess(entityParam, p.PropertyInfo),
Expression.MakeMemberAccess(entityExpr, p.PropertyInfo)
));
var predicate = equality.Aggregate(Expression.AndAlso);
return Expression.Lambda<Func<T, bool>>(predicate, entityParam);
}