C# 根据运行时设置的列表限制查询列

C# 根据运行时设置的列表限制查询列,c#,linq,entity-framework,C#,Linq,Entity Framework,我有以下疑问: // Type T is constrained to a class that contains "ID" property // propertiesToQuery is a list constructed based on type T var set = AppContext.Set<T>(); var result = set.SelectMany(x => propertiesToQuery.Select(p => new { x.ID, V

我有以下疑问:

// Type T is constrained to a class that contains "ID" property
// propertiesToQuery is a list constructed based on type T
var set = AppContext.Set<T>();
var result = set.SelectMany(x => propertiesToQuery.Select(p => new { x.ID, Value = x.GetType().GetProperty(p.Name).GetValue(x) })
                                                          .Where(p => p.Value != null)
                                                          .Select(p => new SearchIndexItem
                                                                           {
                                                                               Key = p.Value.ToString(),
                                                                               Url = Url.Action("Edit", type.Name, new { p.ID }),
                                                                               Type = type
                                                                           }));
//类型T被约束为包含“ID”属性的类
//propertiesToQuery是基于类型T构造的列表
var set=AppContext.set();
var result=set.SelectMany(x=>propertiesToQuery.Select(p=>new{x.ID,Value=x.GetType().GetProperty(p.Name).GetValue(x)})
.Where(p=>p.Value!=null)
.选择(p=>new SearchIndexItem
{
Key=p.Value.ToString(),
Url=Url.Action(“编辑”,type.Name,new{p.ID}),
类型=类型
}));
现在,因为linq to entities不允许在查询中使用PropertyInfo,所以我需要在集合上运行ToList(),以便首先在db上执行查询,然后执行所需的SelectMany()

这会从数据库中查询更多数据,当有大量数据时,这将是一个问题(查询的列的类型为string,其他列可以是blob,这些是我不想从中提取数据的列)

因此,问题是如何根据运行时构造的列表限制从db查询的列

我试图创建一个表达式树,并将其传递给集合上的Select()方法,但问题在于创建匿名类型,根据类型T的不同,匿名类型可能会有所不同。

您的观察结果如下:

问题在于创建匿名类型,根据类型T的不同,匿名类型可能会有所不同

准确,;在运行时构造结果列是一个很大的问题。唯一简单的方法是使其看起来像是填充了一个具有所有成员的类型的投影,例如等效于:

// where our list is "Foo", "Bar":
x => new SomeType {
   Foo = x.Foo,
   Bar = x.Bar
   // but other SomeType properties exist, but are't mapped
}
最明显的竞争对手是实体类型,因此您正在将一组
Customer
行部分映射到
Customer
对象,但大多数ORM不允许您这样做:如果投影是实体类型,则他们需要整个类型(即
x=>x
)。您可以创建第二个版本的实体类型,它是常规POCO/DTO,但不是实体模型的一部分,即

Customer x => new CustomerDto {
   Foo = x.Foo,
   Bar = x.Bar
}
您可以在运行时将其作为
Expression.MemberInit
的一部分执行。例如:

class Foo
{
    public string A { get; set; }
    public int B { get; set; }
    public DateTime C { get; set; }
}
class FooDto
{
    public string A { get; set; }
    public int B { get; set; }
    public DateTime C { get; set; }
}
class Program
{
    static void Main(string[] args)
    {
        var data = new[] { new Foo { A = "a", B = 1, C = DateTime.Now}}
                 .AsQueryable();
        var mapped = PartialMap<Foo, FooDto>(data, "A", "C").ToList();
    }
    static IQueryable<TTo> PartialMap<TFrom, TTo>(
        IQueryable<TFrom> source, params string[] members)
    {
        var p = Expression.Parameter(typeof(TFrom));
        var body = Expression.MemberInit(Expression.New(typeof(TTo)),
            from member in members
            select (MemberBinding)Expression.Bind(
                typeof(TTo).GetMember(member).Single(),
                Expression.PropertyOrField(p, member))
            );
        return source.Select(Expression.Lambda<Func<TFrom, TTo>>(body, p));
    }
}
class-Foo
{
公共字符串A{get;set;}
公共int B{get;set;}
公共日期时间C{get;set;}
}
食品类
{
公共字符串A{get;set;}
公共int B{get;set;}
公共日期时间C{get;set;}
}
班级计划
{
静态void Main(字符串[]参数)
{
var data=new[]{new Foo{A=“A”,B=1,C=DateTime.Now}
.AsQueryable();
var-mapped=PartialMap(数据“A”、“C”).ToList();
}
静态可量化局部映射(
IQueryable源,参数字符串[]成员)
{
var p=表达式参数(typeof(TFrom));
var body=Expression.MemberInit(Expression.New(typeof(TTo)),
从一个成员到另一个成员
选择(MemberBinding)Expression.Bind(
typeof(TTo).GetMember(member).Single(),
Expression.PropertyOrField(p,成员))
);
返回source.Select(Expression.Lambda(body,p));
}
}
在输出中,
A
C
有值,但
B
没有值。

您的观察结果如下:

问题在于创建匿名类型,根据类型T的不同,匿名类型可能会有所不同

准确,;在运行时构造结果列是一个很大的问题。唯一简单的方法是使其看起来像是填充了一个具有所有成员的类型的投影,例如等效于:

// where our list is "Foo", "Bar":
x => new SomeType {
   Foo = x.Foo,
   Bar = x.Bar
   // but other SomeType properties exist, but are't mapped
}
最明显的竞争对手是实体类型,因此您正在将一组
Customer
行部分映射到
Customer
对象,但大多数ORM不允许您这样做:如果投影是实体类型,则他们需要整个类型(即
x=>x
)。您可以创建第二个版本的实体类型,它是常规POCO/DTO,但不是实体模型的一部分,即

Customer x => new CustomerDto {
   Foo = x.Foo,
   Bar = x.Bar
}
您可以在运行时将其作为
Expression.MemberInit
的一部分执行。例如:

class Foo
{
    public string A { get; set; }
    public int B { get; set; }
    public DateTime C { get; set; }
}
class FooDto
{
    public string A { get; set; }
    public int B { get; set; }
    public DateTime C { get; set; }
}
class Program
{
    static void Main(string[] args)
    {
        var data = new[] { new Foo { A = "a", B = 1, C = DateTime.Now}}
                 .AsQueryable();
        var mapped = PartialMap<Foo, FooDto>(data, "A", "C").ToList();
    }
    static IQueryable<TTo> PartialMap<TFrom, TTo>(
        IQueryable<TFrom> source, params string[] members)
    {
        var p = Expression.Parameter(typeof(TFrom));
        var body = Expression.MemberInit(Expression.New(typeof(TTo)),
            from member in members
            select (MemberBinding)Expression.Bind(
                typeof(TTo).GetMember(member).Single(),
                Expression.PropertyOrField(p, member))
            );
        return source.Select(Expression.Lambda<Func<TFrom, TTo>>(body, p));
    }
}
class-Foo
{
公共字符串A{get;set;}
公共int B{get;set;}
公共日期时间C{get;set;}
}
食品类
{
公共字符串A{get;set;}
公共int B{get;set;}
公共日期时间C{get;set;}
}
班级计划
{
静态void Main(字符串[]参数)
{
var data=new[]{new Foo{A=“A”,B=1,C=DateTime.Now}
.AsQueryable();
var-mapped=PartialMap(数据“A”、“C”).ToList();
}
静态可量化局部映射(
IQueryable源,参数字符串[]成员)
{
var p=表达式参数(typeof(TFrom));
var body=Expression.MemberInit(Expression.New(typeof(TTo)),
从一个成员到另一个成员
选择(MemberBinding)Expression.Bind(
typeof(TTo).GetMember(member).Single(),
Expression.PropertyOrField(p,成员))
);
返回source.Select(Expression.Lambda(body,p));
}
}

在输出中,
A
C
有值,但
B
没有值。

哇,这是一个有趣的方法。我可以使用代理/适配器/装饰器而不是dto类型吗?或者实体框架会检测继承并抛出错误吗?@Chris不知道-如果你发现了,请告诉我不幸的是,这是不可能的:/但是非常感谢你的想法,我想我应该能够调整它以适应我的nee