1

我有以下查询:

// 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
                                                                           }));

现在因为 linq to entity 不允许在查询中使用 PropertyInfo,我需要在集合上运行 ToList() 以便首先在 db 上执行查询,然后执行所需的 SelectMany()。

这个查询比它需要从数据库中查询的多,当有很多数据时这将是一个问题(查询的列是字符串类型,其他可能是 blob,这些是我不想从中提取数据的)

所以问题是如何根据运行时构建的列表限制从数据库查询的列?

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

4

1 回答 1

1

您在这里的观察:

问题在于创建匿名类型,根据类型 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));
    }
}

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

于 2013-04-11T11:03:44.407 回答