2012-02-02 45 views
1

我使用LinqKit用以下搜索功能的EntityFramework 4.0搜索庫:IQueryable的<T>與EntityObject使用泛型和接口(可能嗎?)

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

和使用IQueryable的返回值子集的另一個類查詢的方式使用布爾LinqKit PredicateBuilder表達式是不可能的:

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

的這裏的問題是,「T」作爲EntityObject沒有定義GUID,所以我不能用這個。這種自然的反應是實際定義SubsetByUser()方法使用約束帶GUID屬性:

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

,它使用反射來獲得奔跑收藏 - 周圍的編譯器工作。錯誤我認爲這是相當聰明的,但它導致了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()返回它。有什麼方法可以將這些放在一起,我錯過了嗎?

+0

是否可以顯示你如何用這個SubsetByUser功能(在那裏你會從你的查詢稱呼它)多一點點的代碼。試圖做類似的事情。 – AaronLS 2012-05-31 21:07:27

+0

@AaronLS,我不再在這個項目上了,但基本上是-IIRC,set參數是來自另一個查詢的返回集。所以它只是_subsetContainer.SubsetByUser (_container.Search (謂詞),someUser); – JohnMetta 2012-06-15 16:56:09

回答

2
這個自然的反應是實際確定使用約束帶GUID屬性SubsetByUser()方法: ... 但是,這是行不通的。我正在使用LinqKit和Expandable()方法: System.NotSupportedException:無法將類型'Oasis.DataModel.Arc'轉換爲 類型'Oasis.DataModel.Interfaces.IHaveMetadata'。 LINQ to Entities只支持 轉換實體數據模型原始類型

您與此非常接近。如果您使用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.*函數的表達式樹,這樣就可以避免包括在第一位置的鑄造。

+0

這是完美的。正是我想要的。非常感謝! – JohnMetta 2012-02-02 23:38:10

+0

@ hvd我想我正在嘗試做類似的事情。你能舉一個例子說明如何在查詢中調用SubsetByUser?我很困惑,因爲它不是擴展方法。我試圖創建可由linq2entities使用方法:http://stackoverflow.com/questions/10826275/iqueryable-extension-method-for-linq2entities – AaronLS 2012-05-31 15:30:37

+0

@AaronLS你會在SubsetByUser使用'從X(。 ..)...選擇x' - 這不是你想要做的。這是一個返回查詢的函數。你正試圖從查詢中調用一個函數。 (也就是說,我已經發現我的答案是不必要的複雜,並且會在我可以的時候更新。) – hvd 2012-05-31 19:46:06