2008-10-10 52 views
2

我在Linq-to-SQL類上創建了一些額外的功能,以便在開發我的應用程序時使事情變得更加簡單。例如,我定義了一個從合同列表中檢索活動合同的屬性。但是,如果我試圖在lambda表達式中使用此屬性,或者通常在查詢中使用此屬性,它將引發一個異常,即沒有與該屬性匹配的SQL語句,或者它會爲每個項目生成一個查詢(=大量往返於服務器)。在Linq-to-SQL中映射計算的屬性以執行SQL語句

查詢本身並不複雜f.ex:

var activeContracts = customer.Contracts.Where(w => w.ContractEndDate == null); 

而我想它是閱讀:

var activeContracts = customer.ActiveContracts; 

我這樣做的主要原因是因爲它會降低在我的部分是邏輯錯誤,如果我將來想改變定義活動合同的內容,我不必重做很多代碼。

有沒有一種方法來指定屬性應該生成的SQL。或者有沒有辦法確保它可以在下面的查詢中使用?

var singleContractCustomers = db.Customers.Where(w => w.ActiveContracts.Count() == 1); 
+0

什麼是你的財產類型? – 2008-10-10 08:39:54

回答

3

當單獨訪問,我懷疑有一個返回IQueryable的查詢會工作 - 但是,我希望,當這是一個更大的表達式的一部分,表達解釋會抱怨(這似乎是你所描述的)。

但是,我懷疑你可能會把它分解一點。試着增加(客戶):

public static Expression<Func<Customer, bool>> HasActiveContract 
    { 
     get { return cust => cust.Contracts.Count() == 1; } 
    } 

那麼你應該能夠使用:

var filtered = db.Customers.Where(Customer.HasActiveContract); 

顯然,這是難以運行它(從這裏開始),看看有什麼TSQL它來了,但如果這樣做往返,我會很驚訝;我期望在TSQL中執行COUNT()。作爲最重要的查詢,您應該也可以包裝:

public IQueryable<Customer> CustomersWithActiveContract 
    { 
     get { return Customers.Where(Customer.HasActiveContract); } 
    } 

是否有這些工作?

1

這就像一個魅力。由CustomersWithActiveContracts生成的SQL陳述對我看起來很好。

{SELECT [t0].[CustomerID], [t0].[cFirstName], [t0].[cLastName] 
FROM [dbo].[Customers] AS [t0] 
WHERE ((
    SELECT COUNT(*) 
    FROM [dbo].[Contracts] AS [t1] 
    WHERE (([t1].[ContractEndDate] > @p0) OR ([t1].[ContractEndDate] IS NULL)) AND ([t1].[cId] = [t0].[cId]) 
    )) > @p1 
} 

它也應該意味着它可以建立在這個查詢上,而不會產生更多的數據庫訪問。

1

另一種選擇是使用Microsoft.Linq.Translations庫。這將允許您定義屬性和它的LINQ的翻譯如下:

partial class Customer 
{ 
    private static readonly CompiledExpression<Employee,IEnumerable<Contract>> activeContractsExpression 
     = DefaultTranslationOf<Customer> 
      .Property(c => c.ActiveContracts) 
      .Is(c => c.Contracts.Where(x => x.ContractEndDate == null)); 

    public IEnumerable<Contract> ActiveContracts 
    { 
     get 
     { 
      // This is only called when you access your property outside a query 
      return activeContractsExpression.Evaluate(this); 
     } 
    } 
} 

然後你就可以查詢它像這樣:

var singleContractCustomers = db.Customers.WithTranslations() 
           .Where(w => w.ActiveContracts.Count() == 1); 

通知調用WithTranslations()。這會創建一個特殊的包裝IQueryable,它將在將查詢表達式傳遞給Linq to SQL之前用它的轉換替換對您的計算屬性的所有引用。

此外,如果您包含Microsoft.Linq.Translations.Auto命名空間而不是System.Linq,那麼您甚至不需要致電WithTranslations()