var numbers = new int[] { 1, 2, 3, 4, 5 };
var contacts = from c in context.Contacts
where c.ContactID == numbers.Max() | c.ContactID == numbers.FirstOrDefault()
select c;
foreach (var item in contacts) Console.WriteLine(item.ContactID); ;
Linq-to-Entities 查询首先转换为 Linq表达式树,然后由对象服务转换为命令树。如果 Linq-to-Entities 查询嵌套了 Linq-to-Objects 查询,那么这个嵌套查询也会被翻译成一个表达式树。
a) 我假设嵌套的 Linq-to-Objects 查询的所有运算符都没有实际执行,而是特定 DB(或可能是对象服务)的数据提供者知道如何将 Linq-to-Objects 运算符的逻辑转换为适当的 SQL陈述?
b)数据提供者知道如何仅为某些 Linq-to-Objects 运算符创建等效的 SQL 语句?
c) 同样,数据提供者知道如何仅为 Net Framework 类库中的一些非 Linq 方法创建等效的 SQL 语句?
回复亚当·米尔斯:
1)我对你的回复有点困惑。在回复b)时,您同意如果说Linq2Entities Data Provider for SQL Server 支持特定的 Linq-to-Objects 运算符,那么它将尝试将其转换为等效的 SQL 语句,并且在回复c)时您还同意,如果这provider 支持特定的非 Linq 方法,它会将其转换为等效的 SQL 语句(如果它不支持它,它将抛出异常)。但是对于a)您的回答与您对c)所说的相反,因此该提供程序不会尝试转换Max
为等效的 Sql 语句,而是会执行它并在查询中使用返回的值?
2)无论如何,我只知道一些 Sql,所以我不能完全确定,但是阅读为上述代码生成的 Sql 查询似乎数据提供者实际上并没有执行numbers.Max
方法,而是以某种方式发现numbers.Max
应该返回最大值然后继续在生成的 Sql 查询中包含对TSQL 的内置 MAX 函数的调用。它还将numbers
数组保存的所有值放入 Sql 查询中。
SELECT CASE
WHEN (([Project1].[C1] = 1)
AND ([Project1].[C1] IS NOT NULL)) THEN '0X0X'
ELSE '0X1X'
END AS [C1],
[Extent1].[ContactID] AS [ContactID],
[Extent1].[FirstName] AS [FirstName],
[Extent1].[LastName] AS [LastName],
[Extent1].[Title] AS [Title],
[Extent1].[AddDate] AS [AddDate],
[Extent1].[ModifiedDate] AS [ModifiedDate],
[Extent1].[RowVersion] AS [RowVersion],
CASE
WHEN (([Project1].[C1] = 1)
AND ([Project1].[C1] IS NOT NULL)) THEN [Project1].[CustomerTypeID]
END AS [C2],
CASE
WHEN (([Project1].[C1] = 1)
AND ([Project1].[C1] IS NOT NULL)) THEN [Project1].[InitialDate]
END AS [C3],
CASE
WHEN (([Project1].[C1] = 1)
AND ([Project1].[C1] IS NOT NULL)) THEN [Project1].[PrimaryDesintation]
END AS [C4],
CASE
WHEN (([Project1].[C1] = 1)
AND ([Project1].[C1] IS NOT NULL)) THEN [Project1].[SecondaryDestination]
END AS [C5],
CASE
WHEN (([Project1].[C1] = 1)
AND ([Project1].[C1] IS NOT NULL)) THEN [Project1].[PrimaryActivity]
END AS [C6],
CASE
WHEN (([Project1].[C1] = 1)
AND ([Project1].[C1] IS NOT NULL)) THEN [Project1].[SecondaryActivity]
END AS [C7],
CASE
WHEN (([Project1].[C1] = 1)
AND ([Project1].[C1] IS NOT NULL)) THEN [Project1].[Notes]
END AS [C8],
CASE
WHEN (([Project1].[C1] = 1)
AND ([Project1].[C1] IS NOT NULL)) THEN [Project1].[RowVersion]
END AS [C9],
CASE
WHEN (([Project1].[C1] = 1)
AND ([Project1].[C1] IS NOT NULL)) THEN [Project1].[BirthDate]
END AS [C10],
CASE
WHEN (([Project1].[C1] = 1)
AND ([Project1].[C1] IS NOT NULL)) THEN [Project1].[HeightInches]
END AS [C11],
CASE
WHEN (([Project1].[C1] = 1)
AND ([Project1].[C1] IS NOT NULL)) THEN [Project1].[WeightPounds]
END AS [C12],
CASE
WHEN (([Project1].[C1] = 1)
AND ([Project1].[C1] IS NOT NULL)) THEN [Project1].[DietaryRestrictions]
END AS [C13]
FROM [dbo].[Contact] AS [Extent1]
LEFT OUTER JOIN (SELECT [Extent2].[ContactID] AS [ContactID],
[Extent2].[BirthDate] AS [BirthDate],
[Extent2].[HeightInches] AS [HeightInches],
[Extent2].[WeightPounds] AS [WeightPounds],
[Extent2].[DietaryRestrictions] AS [DietaryRestrictions],
[Extent3].[CustomerTypeID] AS [CustomerTypeID],
[Extent3].[InitialDate] AS [InitialDate],
[Extent3].[PrimaryDesintation] AS [PrimaryDesintation],
[Extent3].[SecondaryDestination] AS [SecondaryDestination],
[Extent3].[PrimaryActivity] AS [PrimaryActivity],
[Extent3].[SecondaryActivity] AS [SecondaryActivity],
[Extent3].[Notes] AS [Notes],
[Extent3].[RowVersion] AS [RowVersion],
cast(1 as bit) AS [C1]
FROM [dbo].[ContactPersonalInfo] AS [Extent2]
INNER JOIN [dbo].[Customers] AS [Extent3]
ON [Extent2].[ContactID] = [Extent3].[ContactID]) AS [Project1]
ON [Extent1].[ContactID] = [Project1].[ContactID]
LEFT OUTER JOIN (SELECT TOP (1) [c].[C1] AS [C1]
FROM (SELECT [UnionAll3].[C1] AS [C1]
FROM (SELECT [UnionAll2].[C1] AS [C1]
FROM (SELECT [UnionAll1].[C1] AS [C1]
FROM (SELECT 1 AS [C1]
FROM (SELECT 1 AS X) AS [SingleRowTable1]
UNION ALL
SELECT 2 AS [C1]
FROM (SELECT 1 AS X) AS [SingleRowTable2]) AS [UnionAll1]
UNION ALL
SELECT 3 AS [C1]
FROM (SELECT 1 AS X) AS [SingleRowTable3]) AS [UnionAll2]
UNION ALL
SELECT 4 AS [C1]
FROM (SELECT 1 AS X) AS [SingleRowTable4]) AS [UnionAll3]
UNION ALL
SELECT 5 AS [C1]
FROM (SELECT 1 AS X) AS [SingleRowTable5]) AS [c]) AS [Limit1]
ON 1 = 1
LEFT OUTER JOIN (SELECT TOP (1) [c].[C1] AS [C1]
FROM (SELECT [UnionAll7].[C1] AS [C1]
FROM (SELECT [UnionAll6].[C1] AS [C1]
FROM (SELECT [UnionAll5].[C1] AS [C1]
FROM (SELECT 1 AS [C1]
FROM (SELECT 1 AS X) AS [SingleRowTable6]
UNION ALL
SELECT 2 AS [C1]
FROM (SELECT 1 AS X) AS [SingleRowTable7]) AS [UnionAll5]
UNION ALL
SELECT 3 AS [C1]
FROM (SELECT 1 AS X) AS [SingleRowTable8]) AS [UnionAll6]
UNION ALL
SELECT 4 AS [C1]
FROM (SELECT 1 AS X) AS [SingleRowTable9]) AS [UnionAll7]
UNION ALL
SELECT 5 AS [C1]
FROM (SELECT 1 AS X) AS [SingleRowTable10]) AS [c]) AS [Limit2]
ON 1 = 1
CROSS JOIN (SELECT MAX([UnionAll12].[C1]) AS [A1]
FROM (SELECT [UnionAll11].[C1] AS [C1]
FROM (SELECT [UnionAll10].[C1] AS [C1]
FROM (SELECT [UnionAll9].[C1] AS [C1]
FROM (SELECT 1 AS [C1]
FROM (SELECT 1 AS X) AS [SingleRowTable11]
UNION ALL
SELECT 2 AS [C1]
FROM (SELECT 1 AS X) AS [SingleRowTable12]) AS [UnionAll9]
UNION ALL
SELECT 3 AS [C1]
FROM (SELECT 1 AS X) AS [SingleRowTable13]) AS [UnionAll10]
UNION ALL
SELECT 4 AS [C1]
FROM (SELECT 1 AS X) AS [SingleRowTable14]) AS [UnionAll11]
UNION ALL
SELECT 5 AS [C1]
FROM (SELECT 1 AS X) AS [SingleRowTable15]) AS [UnionAll12]) AS [GroupBy1]
WHERE [Extent1].[ContactID] IN ([GroupBy1].[A1], (CASE
WHEN ([Limit1].[C1] IS NULL) THEN 0
ELSE [Limit2].[C1]
END))
基于此,Linq2Entities 提供程序是否可能确实不执行非 Linq 和 Linq-to-Object 方法,而是为其中一些方法创建等效的 SQL 语句(而对于其他方法则抛出异常)?
第二次编辑:
好的,我按照你说的做了:
对于b)我创建了 Linq-to-Objects 扩展方法:
public static class TEST_CLASS
{
public static int Testing<TSource>(this IEnumerable<TSource> source)
{
Console.WriteLine("Testing Called"); // here I've put a breakpoint
return source.Count();
}
}
List<int> list = new List<int>() {1,2,3,4,5,6 };
var contact = (from c in context.Contacts
where c.ContactID == list.Testing()
select c).First();
当我在调试模式下运行代码时,我立即得到以下异常(因此调试器在抛出异常之前不会进入测试方法):
System.NotSupportedException:LINQ to Entities 无法识别方法“Int32 TestingInt32”方法,并且该方法无法转换为存储表达式。
对于c)我创建了非 Linq 方法:
public class Another_TEST_CLASS
{
public static int Testing_Again()
{
Console.WriteLine("Testing_Again called");// here I've put a breakpoint
return 1000;
}
}
var contact = (from c in context.Contacts
where c.ContactID == Another_TEST_CLASS.Testing_Again()
select c).First();
当我在调试模式下运行代码时,我立即得到以下异常(因此调试器在抛出异常之前不会进入 Testing_Again 方法):
System.NotSupportedException:LINQ to Entities 无法识别方法“Int32 Testing_Again()”方法,并且此方法无法转换为存储表达式。在 System.Data.Objects.ELinq.ExpressionConverter.MethodCallTranslator.Default
先感谢您