Entity framework 实体框架核心:使用导航属性动态构建选择列表
问题类似于,但答案并没有向我提供两个关键问题:Entity framework 实体框架核心:使用导航属性动态构建选择列表,entity-framework,entity-framework-core,Entity Framework,Entity Framework Core,问题类似于,但答案并没有向我提供两个关键问题: 我需要代码来处理导航属性 我正在尝试构建扩展方法 我想编写如下查询: this.context.User .Where(t => t.Id > 10) .SelectCustom(t => t.Address.Country.Title) .OrderBy(t => t.DisplayName) .Skip(10).Take(5); 在提供的链接中,我得到了答案: public clas
- 我需要代码来处理导航属性
- 我正在尝试构建扩展方法
this.context.User
.Where(t => t.Id > 10)
.SelectCustom(t => t.Address.Country.Title)
.OrderBy(t => t.DisplayName)
.Skip(10).Take(5);
在提供的链接中,我得到了答案:
public class SelectList<TSource>
{
private List<MemberInfo> members = new List<MemberInfo>();
public SelectList<TSource> Add<TValue>(Expression<Func<TSource, TValue>> selector)
{
var member = ((MemberExpression)selector.Body).Member;
members.Add(member);
return this;
}
public Expression<Func<TSource, TResult>> ToDynamicColumns()
{
return this.members.??????????;
}
}
public static IQueryable<T> SelectCustom<T>(this IQueryable<T> query, Expression<Func<TSource, TKey>> FirstAdditional = null)
{
var columns = new SelectList<T>();
columns.Add(t => t.Id);
columns.Add(t => t.DisplayName)
if (FirstAdditional != null)
columns.Add(FirstAdditional);
return query.Select(columns.ToDynamicColumns);
}
公共类选择列表
{
私有列表成员=新列表();
公共选择列表添加(表达式选择器)
{
var member=((MemberExpression)selector.Body).member;
成员。添加(成员);
归还这个;
}
公共表达式ToDynamicColumns()
{
把这个还给大家。??????????;
}
}
公共静态IQueryable SelectCustom(此IQueryable查询,表达式FirstAdditional=null)
{
var columns=new SelectList();
columns.Add(t=>t.Id);
columns.Add(t=>t.DisplayName)
if(FirstAdditional!=null)
列。添加(第一个附加);
返回query.Select(columns.ToDynamicColumns);
}
这可以用EF Core 2.0来完成吗?你可以用Expression.ListInit来完成,这里的
TResult
必须有一个Add
实例方法,例如Dictionary
。这可能行得通,但我还没有编译它。无论如何,这应该给出足够的提示,说明如何以您想要的方式构建它
public Expression<Func<TSource, Dictionary<string, object>>> ToDynamicColumns()
{
var addMethod = typeof(TResult).GetMethod("Add");
var paramX = Expression.Parameter(typeof(TSource), "e");
var bindings =
this.members.Select (
member =>
return Expression.ElementInit(
addMethod,
Expression.Constant(mem.Name),
Expression.Convert(Expression.Property(paramX, member), typeof(object))
);
)
var listInit = Expression.ListInit(
Expression.New(typeof(TResult)),
bindings
);
return Expression.Lambda<Func<TSource, Dictionary<string, object>>(
listInit,
paramX
);
}
public表达式ToDynamicColumns()
{
var addMethod=typeof(TResult).GetMethod(“Add”);
var paramX=表达式参数(typeof(TSource),“e”);
变量绑定=
this.members.Select(
成员=>
返回表达式.ElementInit(
addMethod,
表达式常量(mem.Name),
Expression.Convert(Expression.Property(paramX,member),typeof(object))
);
)
var listInit=Expression.listInit(
表达式.New(typeof(TResult)),
绑定
);
返回表达式。LambdaEF将查看Lambda调用操作,就像该表达式的主体是内联的一样。因此,我建议不要使用源表达式,只生成表达式来调用它们
此外,我还将保持结果类型的简单性,并将每一行作为对象数组返回。这将比创建大量字典的开销更小。如果确实需要按名称访问字段,则应创建一个字典来维护名称和列号之间的关系
公共类选择列表
{
私有列表成员=新列表();
公共选择列表添加(表达式选择器)
{
成员。添加(选择器);
归还这个;
}
公共表达式ToDynamicColumns()
{
var参数=表达式参数(typeof(TSource),“e”);
返回表达式.Lambda(
Expression.NewArrayInit(
类型(对象),
成员。选择(m=>
Expression.Convert(Expression.Invoke(m,参数),typeof(对象))
)
),
参数);
}
}
虽然在您的例子中,因为您编写的扩展方法只返回相同的键细节和单个附加字段,所以您可能可以定义一个泛型类型来保存结果,并且完全避免使用Linq表达式
public类UserResult{
公共int Id{get;set;}
公共字符串DisplayName{get;set;}
公共V值{get;set;}
}
公共静态IQueryable SelectCustom(此IQueryable查询,表达式ValueGetter)
{
返回query.Select(u=>newuserresult{
Id=u.Id,
DisplayName=u.DisplayName,
Value=ValueGetter(u)
});
}
几乎可以说,如果c#只允许您从另一个表达式中编译一个表达式的调用,那么我们可以实现一个ExpressionVisitor来展开任何编译调用
公共类DontCompile:ExpressionVisitor
{
受保护的重写表达式VisitMember(MemberExpression节点)
{
//内联任何作为表达式的lambda参数
如果(node.Expression)为常量表达式lambdaArgs
&&节点。成员为FieldInfo字段
&&typeof(表达式).IsAssignableFrom(field.FieldType))
返回(表达式)字段.GetValue(lambdaArgs.Value);
返回base.VisitMember(节点);
}
受保护的重写表达式VisitMethodCall(MethodCallExpression节点)
{
//不要编译lambda表达式
如果(node.Method.Name==“Compile”
&&typeof(LambdaExpression).IsAssignableFrom(node.Object.Type))
回访(node.Object);
返回base.VisitMethodCall(节点);
}
public static Expression Tidy(Expression func)=>(Expression)new DontCompile().Visit(func);
}
...
返回query.Select(DontCompile.Tidy(u=>newuserresult{
Id=u.Id,
DisplayName=u.DisplayName,
Value=ValueGetter.Compile()(u)
});
...
但这看起来有点混乱。有什么原因不能只选择一个视图模型吗?例如,.select(user=>newuserviewmodel{Id=user.Id,Name=user.DisplayName})
?为什么需要它是动态的?因为用户可以定义他希望看到的列。这些列可以很深(导航属性上的导航属性…)你不应该在某个地方清理用户输入吗?你可能会发现System.Linq.Dynamic.Core很有用。我用它做过类似的事情。如果是这样的话,我会用System.Linq.Dynamic.Core,但我很确定它可以用表达式来完成。逻辑上想想。对于像DisplayName
这样的简单属性,函数会创建一些东西像这样t=>newt{DisplayName=t.DisplayName}
。但是在嵌套属性的情况下,赋值的左侧是什么(没有这样的C#构造),即t=>newt{???=t.Address.Country.Title}
我相信LINQ会查看调用