我在Linq-to-SQL类上创建了一些额外的功能,以便在开发应用程序时更轻松。例如,我已经定义了一个从合同列表中检索活动合同的属性。但是,如果我尝试在lambda表达式中使用此属性,或者通常在查询中使用此属性,则会抛出一个异常,即没有与该属性匹配的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 :(得分:3)
当单独访问时,我怀疑有一个返回IQueryable的查询会起作用 - 但是,我希望当这是一个更大的Expression的一部分时,表达式解释器会抱怨(这看起来就像你所描述的那样)。 p>
然而,我怀疑你可能会将其分解一下。尝试添加(对客户):
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 :(得分: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
}
它还应该意味着可以构建此查询,而不会产生更多的数据库访问。
答案 2 :(得分: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()
!