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 ではクエリで PropertyInfo を使用できないため、最初にデータベースでクエリを実行し、次に目的の SelectMany() を実行するために、セットで ToList() を実行する必要があります。

これは、データベースから必要以上にクエリを実行します。これは、大量のデータがある場合に問題になります (クエリされた列は文字列型であり、他の列は BLOB である可能性があり、これらはデータを取得したくない列です)。

問題は、実行時に作成されたリストに基づいて、db からクエリされる列をどのように制限できるかということです。

式ツリーを作成してセットの 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 であるがエンティティ モデルの一部ではないエンティティ タイプの 2 番目のバージョンを作成できる場合があります。

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を持っていますが、持っていません。CB

于 2013-04-11T11:03:44.407 に答える