【问题标题】:Mapping computed properties in Linq-to-SQL to actuall SQL statements将 Linq-to-SQL 中的计算属性映射到实际的 SQL 语句
【发布时间】:2008-10-10 08:32:46
【问题描述】:

我在我的 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);

【问题讨论】:

  • 您的房产类型是什么?
  • 我用过 bool 等简单的和 IQueryable 等复杂的,结果大致相同。

标签: c# .net linq linq-to-sql


【解决方案1】:

当单独访问时,我怀疑有一个返回 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); }
    }

这些有用吗?

【讨论】:

    【解决方案2】:

    这就像一个魅力。 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
    }
    

    这也应该意味着可以在此查询的基础上进行构建,而不会产生更多的数据库访问。

    【讨论】:

      【解决方案3】:

      另一个选择是使用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()

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-11-23
        • 2010-11-26
        • 1970-01-01
        • 2012-02-15
        • 2023-03-14
        相关资源
        最近更新 更多