1

次の検索機能を備えたLinqKitを使用したEntityFramework4.0の検索リポジトリがあります。

public IQueryable<T> Search<T>(Expression<Func<T, bool>> predicate) 
    where T : EntityObject
{
    return _unitOfWork.ObjectSet<T>().AsExpandable().Where(predicate);
}

また、IQueryableの戻り値を使用して、ブール値のLinqKitPredicateBuilder式では不可能な方法でクエリをサブセット化する別のクラス:

public IQueryable<T> SubsetByUser<T>(IQueryable<T> set, User user) 
    where T : EntityObject
{
    return set.Join(_searcher.Search<Metadatum>((o) => o.UserGUID == user.GUID),
                    arc => arc.GUID,
                    meta => meta.ElementGUID,
                    (arc, meta) => arc);
}

ここでの問題は、EntityObjectとしての「T」がGUIDを定義していないため、これを使用できないことです。これに対する自然な応答は、GUIDプロパティで制約を使用するようにSubsetByUser()メソッドを実際に定義することです。

public IQueryable<T> SubsetByUser<T>(IQueryable<T> set, User user) 
    where T : IHaveMetadata
{
    return set.Join(_searcher.Search<Metadatum>((o) => o.UserGUID == user.GUID),
                    arc => arc.GUID,
                    meta => meta.ElementGUID,
                    (arc, meta) => arc);
}

しかし、これは機能しません。LinqKitを使用していますが、Expandable()メソッドの結果は次のようになります。

System.NotSupportedException: Unable to cast the type 'Oasis.DataModel.Arc' to
type 'Oasis.DataModel.Interfaces.IHaveMetadata'. LINQ to Entities only supports 
casting Entity Data Model primitive types

IQueryableを返す必要があります。私はこのような偽物をすることができます:

public IQueryable<T> SubsetByUser<T>(IQueryable<T> set, User user) 
    where T : EntityObject
{
    return set.AsEnumerable()
              .Join(_searcher.Search<Metadatum>((o) => o.UserGUID == user.GUID),
                    arc => arc.GUID,
                    meta => meta.ElementGUID,
                    (arc, meta) => arc)
              .AsQueryable();
}

もちろん、これは機能しますが、もちろん、これは非常にクレイジーなことでもあります。(IQueryableが必要な理由は、最終的になるまでクエリを実行しないためです。

私もこれを試しました:

public IQueryable<T> SubsetByUser<T>(IQueryable<T> set, User user) 
    where T : EntityObject
{
    return set.Join(_searcher.Search<Metadatum>((o) => o.UserGUID == user.GUID),
                    arc => arc.GetType().GetProperty("GUID").GetValue(arc,null),
                    meta => meta.ElementGUID,
                    (arc, meta) => arc);
}

これは、リフレクションを使用してRunsコレクションを取得し、コンパイラエラーを回避します。それはかなり賢いと思いましたが、LINQ例外が発生します。

System.NotSupportedException: LINQ to Entities does not recognize the 
method 'System.Object GetValue(System.Object, System.Object[])' method, 
and this method cannot be translated into a store expression.

検索方法を変更することもできます。

public IQueryable<T> Search<T>(Expression<Func<T, bool>> predicate) 
    where T : IRunElement
{
    return _unitOfWork.ObjectSet<T>().AsExpandable().Where(predicate);
}

ただし、IRunElementはEntityObjectではなく、ObjectSetはTをクラスとして制約するため、これはもちろんコンパイルされません。

最後の可能性は、単にすべてのパラメーターと戻り値をIEnumerableにすることです。

public IEnumerable<T> SubsetByUser<T>(IEnumerable<T> set, User user) 
    where T : EntityObject
{
    return set.Join(_searcher.Search<Metadatum>((o) => o.UserGUID == user.GUID),
                    arc => arc.GetType().GetProperty("GUID").GetValue(arc,null),
                    meta => meta.ElementGUID,
                    (arc, meta) => arc);
}

これも機能しますが、これもインスタンス化を最後まで遅らせることはできません。

したがって、すべてをIEnumerableとしてインスタンス化し、AsQueryable()を使用して返すことなく、この作業を行うためにできることはほとんどないようです。私が欠けているこれをまとめることができる方法はありますか?

4

1 に答える 1

2
これに対する自然な対応は、SubsetByUser() メソッドを実際に定義して、GUID プロパティで制約を使用することです: ... しかし、これは機能しません。LinqKit を使用していますが、Expandable() メソッドの結果は次のようになります。LINQ to Entities は、Entity Data Model プリミティブ型のキャストのみをサポートします

あなたはそれに非常に近いです。ExpressionVisitor自動的に生成される不要なキャスト (基本型または実装されたインターフェイスへのキャスト) をすべて削除するを使用すると、これを機能させることができます。

public IQueryable<T> SubsetByUser<T>(IQueryable<T> set, User user) 
    where T : IHaveMetadata
{
    Expression<Func<T, Guid>> GetGUID = arc => arc.GUID;
    GetGUID = (Expression<Func<T, Guid>>)RemoveUnnecessaryConversions.Instance.Visit(GetGUID);
    return set.Join(_searcher.Search<Metadatum>((o) => o.UserGUID == user.GUID),
        GetGUID,
        meta => meta.ElementGUID,
        (arc, meta) => arc);
}

public class RemoveUnnecessaryConversions : ExpressionVisitor
{
    public static readonly RemoveUnnecessaryConversions Instance = new RemoveUnnecessaryConversions();

    protected RemoveUnnecessaryConversions() { }

    protected override Expression VisitUnary(UnaryExpression node)
    {
        if (node.NodeType == ExpressionType.Convert
            && node.Type.IsAssignableFrom(node.Operand.Type))
        {
            return base.Visit(node.Operand);
        }
        return base.VisitUnary(node);
    }
}

または、関数を使用して式ツリーを手動で作成Expression.*し、最初からキャストを含めないようにします。

于 2012-02-02T22:24:42.127 に答える