2015-06-23 43 views
3

我的實體模型主要由6個實體組成,這些實體之間基於2個屬性進行連接。在LINQ to Entities中使用中間實體會導致System.NotSupportedException

我必須根據舊的純文本SQL查詢構建182個LINQ查詢。這些查詢有一些共同的部分,所以爲了避免重複,我已經構建了一個小框架來在構建塊之上構建查詢。我會馬上展示例子。由於所有查詢都是由6個實體的組合(從根實體SectionA開始)之間的連接組成的,因此我爲了方便起見而構建了一個封裝類JoinOfSections,它包裝了所有這些節。

在該類上,我可以執行所有182個查詢共有的簡單LINQ評估。

例如

public override IQueryable<QueryRow> Run(Models.auitool2014Entities dataContext, int aUserId, DateTime? dataInizioControllo, DateTime? dataFineControllo, string[] a52Exclude, bool codifiche2014) 
     string now = DateTime.Now.ToString("yyyyMMdd"); 

     var sj = dataContext.sezione_a.SelectMany(
      a => dataContext.sezione_d.Where(d => a.A03 == d.A03 && a.utente == d.utente).DefaultIfEmpty(), 
      (a, d) => new SezioneJoin { A = a, D = d }); 

     sj = CommonFiltering(sj, aUserId, dataInizioControllo, dataFineControllo, a52Exclude); 

     return (from SezioneJoin ssj in sj 
       let a = ssj.A 
       let d = ssj.D 

       where 
       a.utente == aUserId && 
       (
       String.IsNullOrEmpty(a.A21) || String.IsNullOrEmpty(a.A51) || 
       a.A51.CompareTo(a.A21) < 0 || 
       a.A21.CompareTo(now) > 0 || 
       a.A51.CompareTo(now) > 0 || 
       a.A21.CompareTo("19000101") < 0 || 
       a.A51.CompareTo("19000101") < 0 
       ) 
       select ssj).Select(Select()); 
    } 

    protected virtual IQueryable<SezioneJoin> CommonFiltering(IQueryable<SezioneJoin> sj, int aUserId, DateTime? dataInizioControllo, DateTime? dataFineControllo, string[] a52Exclude) 
    { 
     sj = sj.Where(x => x.A.utente == aUserId); 
     if (dataInizioControllo != null) 
     { 
      string dataInzio = dataInizioControllo.Value.ToString("yyyyMMdd"); 
      sj = sj.Where(x => x.A.A21.CompareTo(dataInzio) >= 0); 
     } 
     if (dataFineControllo != null) 
     { 
      string dataFine = dataFineControllo.Value.ToString("yyyyMMdd"); 
      sj = sj.Where(x => x.A.A21.CompareTo(dataFine) <= 0); 
     } 
     if (a52Exclude != null) 
      sj = sj.Where(x => !a52Exclude.Contains(x.A.A52)); 

     return sj.Take(Parameters.ROW_LIMIT); 
    } 

Select()方法是一種常見的圖案的簡化。由於結果集必須才能被壓扁的舊組件來處理它,我發明了另一個適配器層

[Serializable] 
public class QueryRow 
{ 
    public string A01 { get; set; } 

    public string A01a { get; set; } 

    public string A01b { get; set; } 

    public string A02 { get; set; } 

    public string A03 { get; set; } 

    public string A11 { get; set; } 

    public string A12 { get; set; } 

    // Dozens of string members 
} 

爲了不復制粘貼& ......

protected virtual Expression<Func<SezioneJoin, QueryRow>> Select() 
    { 
     return sj => new QueryRow 
     { 
      A01 = sj.A.A01, 
      A01a = sj.A.A01a, 
      A01b = sj.A.A01b, 
      A02 = sj.A.A02, 
      A03 = sj.A.A03, 
      A11 = sj.A.A11, 
      A12 = sj.A.A12, 
      A12a = sj.A.A12a, 
      A12b = sj.A.A12b, 
      A12c = sj.A.A12c, 
      A21 = sj.A.A21, 
      A22 = sj.A.A22, 
      A23 = sj.A.A23, 
      A24 = sj.A.A24, 
      A25 = sj.A.A25, 
      A31 = sj.A.A31, 
      A31a = sj.A.A31a, 
      A31b = sj.A.A31b, 
      A32 = sj.A.A32, 
     } 

SezioneJoin類代表數據集中的一行是多個實體之間的組合JOIN,如下所示。它被設計,以便對任何查詢來實例化其定製JOIN(如A內d,左d左E,A內d左1H)

public class SezioneJoin 
{ 

    public SezioneA A { get; set; } 

    public SezioneD D { get; set; } 

    public SezioneE E { get; set; } 

    public SezioneF F { get; set; } 

    public SezioneG G { get; set; } 

    public SezioneH H { get; set; } 

} 

基本上所有查詢要求,該數據集上過濾當前用戶ID和可選檢查日期,再加上他們全部允許最大數量的結果。

我付出了我的企圖概括與NotSupportedException概念(異常消息由我翻譯)

無法轉換類型「DiagnosticoSite.Data.Query.SezioneJoin」到 「DiagnosticoSite.Data.Query.SezioneJoin 」。 LINQ到實體支持 僅枚舉或原始EDM數據類型

問題可能在於(a, d) => new SezioneJoin { A = a, D = d }線:如果我選擇匿名類型LINQ查詢工作得完美無缺,但然後我可以查詢對象不傳遞給受保護的方法,裝飾它與額外的常見檢查。

作爲他們的182個查詢,我很重要的是找到一種方法來爲所有查詢添加通用檢查,這些查詢不是而是拷貝&粘貼。

我想知道如何使用不在數據上下文中的「緩衝區」或「中間」實體來操作LINQ to Entities查詢,以便可以將查詢本身作爲複雜參數傳遞給參數裝飾者方法。

在枚舉和調用ToString()方法的IQueryable方法返回Run時會發生錯誤。我需要ToString來提取發給DB的查詢

+1

該代碼應該工作。你能添加「SezioneJoin」的定義嗎?並且指定您正在使用的實體框架的版本。 –

+1

可以肯定的是,你說你翻譯了異常信息。你確定該消息表明它不能將'DiagnosticoSite.Data.Query.SezioneJoin'轉換爲'DiagnosticoSite.Data.Query.SezioneJoin'(同一類型)嗎? –

+0

這似乎是一個太晚實現實體的問題。我沒有詳細閱讀該問題,但LINQ-To-SQL的非模型類立即觸發了此警報。在調用ToList或以其他方式將SQL結果枚舉爲實際的.NET CLR對象之前,您不能選擇具有LINQ查詢的非模型對象,因爲LINQ引擎無法將您的代碼映射到查詢。我很可能是錯的,但是想增加我的2美分。 – Machinarius

回答

1

EF大概不知道SezioneJoin是什麼。

你可以將這種類型定義爲一個實體,也許它可以工作。但是,如果您的CommonFiltering是現實的,那就沒有必要。試想一下:

var q = CommonFiltering(dataContext.sezione_a, aUserId, dataInizioControllo, dataFineControllo, a52Exclude) 
    .Join(
     dataContext.sezione_d, 
     a => new { A03 = a.A03, User = a.utente }, 
     d => new { A03 = d.A03, User = d.utente }, 
     (a, d) => new SezioneJoin { A = a, D = d } 
    ) 
    .Where(x => 
     ... 
    ).Take(Parameters.ROW_LIMIT); 

protected virtual IQueryable<AType> CommonFiltering(IQueryable<SezioneA> sj, int aUserId, DateTime? dataInizioControllo, DateTime? dataFineControllo, string[] a52Exclude) 
{ 
    sj = sj.Where(x => x.utente == aUserId); 
    if (dataInizioControllo != null) 
    { 
    string dataInzio = dataInizioControllo.Value.ToString("yyyyMMdd"); 
    sj = sj.Where(x => x.A21.CompareTo(dataInzio) >= 0); 
    } 
    if (dataFineControllo != null) 
    { 
    string dataFine = dataFineControllo.Value.ToString("yyyyMMdd"); 
    sj = sj.Where(x => x.A21.CompareTo(dataFine) <= 0); 
    } 
    if (a52Exclude != null) 
    sj = sj.Where(x => !a52Exclude.Contains(x.A52)); 

    return sj; 
} 

真的,你只需要擔心路過的加入,而結果不是組件它,如果過濾的點關心在同一工序都AD的一些品質。這裏你的過濾只關心A,這是一個定義的類型。

如果你真的需要處理類似的事情,你可以建立表達式。

爲了簡化,我們只考慮一個帶簽名IQueryable<T> Filter<T>(IQueryable<T> source, bool makeA21Match)的版本。如果makeA21Match是真的,那麼我們添加Where(sj => sj.A.A21 == sj.D.A21)到查詢,否則我們不這樣做:

private static IQueryable<T> Filter<T>(IQueryable<T> source, bool makeA21Match) 
{ 
    if(makeA21Match) 
    { 
    var getA = typeof(T).GetProperty("A"); // .A 
    var getD = typeof(T).GetProperty("D"); // .D 
    var getAA21 = typeof(SezioneA).GetProperty("A21"); // a.A21 for some A. 
    var getDA21 = typeof(SezioneD).GetProperty("A21"); // d.A21 for some D. 
    var parExp = Expression.Parameter(typeof(T)); // sj. 
    var getAExp = Expression.Property(parExp, getA); // sj.A 
    var getDExp = Expression.Property(parExp, getD); // sj.D 
    var getAA21Exp = Expression.Property(getAExp, getAA21); // sj.A.A21 
    var getDA21Exp = Expression.Property(getDExp, getDA21); // sj.D.A21 
    var eqExp = Expression.Equal(getAA21Exp, getDA21Exp); // sj.A.A21 == sj.D.A21 
    var λExp = Expression.Lambda<Func<T, bool>>(eqExp, parExp); // sj => sj.A.A21 == sj.D.A21 
    source = source.Where(λExp); 
    } 
    return source; 
} 

這是一個很大的不僅僅是其在C#代碼lambda表達式更令人費解,並且缺乏編譯時的檢查類型,但它的確意味着我們可以將表達式應用於匿名類型的可查詢語句,並且在這種情況下性能應該可比。

均衡的方法是將第一濾波器sezione_asezione_d儘可能的,因爲可以更容易地完成,然後具有僅與表達建設的更復雜的手工編碼的那些情況下涉及的方法絕對需要它。您甚至可以緩存在必要時生成的Expression<Func<T, bool>>

+0

現在的問題是:你可以**實際上**使用希臘字母在C#中的變量名?如果你有足夠的創造力,這將非常有趣:-) –

+0

回到話題:我不認爲讓SezioneJoin成爲一個實體甚至是可行的,因爲它是一個包裝。它沒有映射表。或者我可以創建一個包裝EDM實體的複雜類型(未映射到表)?這將是修復程序 –

+0

您可以在C#變量名稱中使用任何字母。我不推薦它,但這已經違反了在美國拼寫中使用英語術語的準則,爲什麼不呢?即使可以創建'SezioneJoin',我不推薦它,因爲它將是一個沒有實體的實體類型,最好與其餘的答案一起使用;儘可能多地使用顯式類型過濾器進行過濾,並在必要時在末尾使用該方法。 –