2008-10-10 61 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()