Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/variables/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Entity framework 实体框架核心:使用导航属性动态构建选择列表_Entity Framework_Entity Framework Core - Fatal编程技术网

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会查看调用