【问题标题】:Use "option(maxrecursion 0)" with EntityFramework EntitySql将“option(maxrecursion 0)”与 EntityFramework EntitySql 一起使用
【发布时间】:2016-05-15 10:24:44
【问题描述】:

我有一个 SqlServer 函数,该函数根据输入执行 cte 递归选择,输入是一个带有 ids 的 csv 字符串。

不幸的是,我不能在我的函数中使用“option(maxrecursion 0)”,它必须在函数执行时使用。问题是我找不到如何将此选项与 EntityFramework 的 EntitySql 一起使用。

考虑到我的函数叫做MyRecursiveFunction,这里有一些代码sn-ps:

public virtual IQueryable<MyFunctionReturnType> ExecuteMyFunction(IObjectContextAdapter objContextAdapter, string csvIds)
{
    var idsParam = new ObjectParameter("idsParam", csvIds);

    // This is the original one, that works, but has no "option(maxrecursion 0)"
    return objContextAdapter.CreateQuery<MyFunctionReturnType>("[MyRecursiveFunction](@idsParam)", idsParam);

    // gives me an error of incorrect syntax near "option"
    return objContextAdapter.CreateQuery<MyFunctionReturnType>("select VALUE tblAlias from [MyRecursiveFunction](@idsParam) as tblAlias OPTION(MAXRECURSION 0)", idsParam);

    // Also gives me syntax error:
    return objContextAdapter.CreateQuery<MyFunctionReturnType>("MyRecursiveFunction(@idsParam) option(maxrecursion 0)", idsParam);
}

有人知道如何将option(maxrecursion 0) 与 entitySql 一起使用吗?

我知道我可以使用“ExecuteStoreQuery”来执行我想要的任何 sql 查询,但我确实需要一个 IQueryable,因为“ExecuteMyFunction”的返回将在实现之前与另一个 IQueryable 结合

请节省您的时间,不要建议同时调用ExecuteStoreQueryAsQueryable.... 我真的不想实现整个结果集,因为我只会实现 10 个分页结果。

这是我的 TVF 的表示:

-- Consider that I have the given table for executing this function.
-- This table has a foreign key to itself, as the data represents a tree, like an organization chart
CREATE TABLE MyTable
(
    Id INT,
    ParentId INT, -- FK to 'MyTable'.'Id',
    Name VARCHAR(400)
)

-- Here is my function definition:
CREATE FUNCTION MyRecursiveFunction (@idsParam VARCHAR(MAX))
RETURNS TABLE
AS
RETURN
(
    -- create a cte for recursively getting the data
    with myCte (id, parentId) as
    (
        SELECT tbl.Id, tbl.ParentId FROM MyTable AS tbl
        -- This function just transform the varchar into a table of "Value"
        INNER JOIN [dbo].[SplitTextIntoTableOfInt](@idsParam, ',') AS ids ON a.ParentId = ids.Value

        UNION ALL

        SELECT a.Id, a.ParentId FROM myCte AS parent
        INNER JOIN MyTable tbl ON tbl.ParentId = parent.Id
    )
    SELECT * FROM myCte -- I can't use 'option(maxrecursion 0)' in here
)

【问题讨论】:

  • 直到您使用 foreach 迭代 IQueryableIEnumerableToList()FirstOrDefault()... 不会向数据库发送查询。您是否真的尝试过ExecuteStoreQuery 后跟AsQueryable()?我对此并不完全确定,因为我很少使用 EF 进行 sql 查询,但可能只有一个查询会访问数据库。正如我所说,我对此并不完全确定,如果我错了,请原谅:)
  • 我测试过了。 ExecuteStoreQuery 返回的 IEnumerable 在我使用它之前不会被执行,但由于它是一个 IEnumerable,而不是一个 IQueryable(它调用“AsQueryable”并没有什么不同),它已经在内存中的对象中转换,所以当我尝试将它与另一个 IQueryable 结合并实现它,这是我的场景,抛出异常。

标签: c# sql-server entity-framework common-table-expression entity-sql


【解决方案1】:

OPTION(MAXRECURSION 0) 是特定于 SQL Server 语法的,我认为 EntitySql 永远不会支持这种特定的语法。这会使抽象与底层数据存储过度耦合,难以支持其他数据库服务器。

如果您达到某个递归限制,那么检查您的设计可能是个好主意,因为降低限制会使您的问题变得更糟。

【讨论】:

    【解决方案2】:

    您唯一能做的就是使用EF interception 并在运行之前将该选项添加到EF 生成的SQL。

    为此,您需要实现IDbCommandInterceptor interface,并使用DbInterception.Add(new YousCommandInterceptor()); 注册您的拦截器。

    您的拦截器可以在查询发送到服务器之前添加选项。 SQL查询在所选方法的命令参数中可用(你应该拦截ReaderExecuted(DbCommand, DbCommandInterceptionContext&lt;DbDataReader&gt;)

    【讨论】:

    • 嗨@JotaBe,我正在使用带有 CodeFirst 的 TVF。 CTE 递归在这个 TVF 中,这就是为什么我需要指定 option(maxrecursion 0)。我可以执行 TVF,问题是使用 option(maxrecursion 0) 执行它。我该怎么做?
    • 您能否展示您如何定义 TVF 以及如何使用 SQL 运行它以指定选项的代码?我不明白为什么它不在函数定义本身内。
    • 不可能。 option(maxrecursion 0) 不能在 TBV 内部定义,只能在执行查询时定义。 stackoverflow.com/questions/7428669
    • 我已经完全改变了我的答案。您可以使用 EF 拦截来做到这一点,它允许在发送到服务器之前修改 SQL 查询。您必须仅在必要时实现必要的逻辑以包含选项(可能通过检查 TVF 名称是否在查询中)
    猜你喜欢
    • 2020-12-29
    • 2014-11-03
    • 1970-01-01
    • 2017-03-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多