EF:将表值参数从 C# 传递给用户定义的函数

Gol*_*lda 10 c# sql-server entity-framework table-valued-parameters entity-framework-6

我在 SQL Server 中有一个用户定义的函数,它接受 TVP(表值参数)作为参数。在 EF 中,如何从 C# 调用这样的函数?

我尝试使用该方法,ObjectContext.CreateQuery<>但出现以下错误:

函数“QueryByParam”的参数“param”无效。参数只能是可以转换为 Edm 标量类型的类型。

也尝试过方法ObjectContext.ExecuteStoreQuery<>并得到相同的错误。IQueryable无论如何它都不会返回。

示例代码

[DbFunction(nameof(SampleDbContext), "QueryByParam")]
public IQueryable<SecurityQueryRow> QueryByParam(IEnumerable<ProfileType> profiles, bool isActive = false)
{
    DataTable dataTable = ....
    ObjectParameter profilesParam = new ObjectParameter("profileTypeIds", dataTable);

    ObjectParameter isActiveParam = new ObjectParameter("isActive ", isActive);

    return ((IObjectContextAdapter)this).ObjectContext.CreateQuery<SecurityQueryRow>(
            string.Format("[{0}].[{1}](@profileTypeIds, @isActive)", GetType().Name, "QueryByParam"),
            profilesParam,
            isActiveParam);
}
Run Code Online (Sandbox Code Playgroud)

要求是我们需要一个 IQueryable 返回,而不是消耗的结果。

ner*_*jma 10

您可以使用 EF Core 中的 Raw Sql 来完成此操作,EF6 中的方法类似,但您无法获得 IQueryable。下面两个例子。

实体框架核心

SQL 类型以将其用作列表过滤器:

CREATE TYPE [dbo].[Table1Type] AS TABLE(
    [Id] [int] NULL,
    [Name] [nchar](10) NULL
)
Run Code Online (Sandbox Code Playgroud)

SQL UDF:

CREATE FUNCTION [dbo].[Func1] 
(   
    @Ids Table1Type readonly
)
RETURNS TABLE 
AS
RETURN
(
    SELECT * from Table1 where id in (select Id from @Ids)
)
Run Code Online (Sandbox Code Playgroud)

EF 上下文:

public class MyContext : DbContext
{
    public DbSet<Table1> Table1 { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

DTO 匹配 sql 类型(为了简单起见也与表相同):

public class Table1
{
    public int Id { get; set; }
    public string Name { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

例子:

static void Main(string[] args)
{
    using (var context = new MyContext())
    {
        // Declare de Structure filter param
        var dt = new DataTable();

        DataTable table = new DataTable();
        table.Columns.Add(new DataColumn("Id", typeof(int)));
        table.Columns.Add(new DataColumn("Name", typeof(string)));
        DataRow row = table.NewRow();
        row["Id"] = 1;
        row["Name"] = "Item";
        table.Rows.Add(row);
        var param = new SqlParameter("@Ids", table) { TypeName = "dbo.Table1Type", SqlDbType = SqlDbType.Structured };

        IQueryable<Table1> query = context.Table1.FromSqlRaw("SELECT * FROM dbo.func1(@Ids)", param);
        var result = query.ToList();
    }
}
Run Code Online (Sandbox Code Playgroud)

实体框架6

您无法获取 IQueryable,但可以链接到生成的 IEnumerable。

static void Main(string[] args)
{
    using (var context = new MyContext())
    {
        context.Database.Log = s => System.Diagnostics.Debug.WriteLine(s);

        // Declare de Structure filter param
        var dt = new DataTable();

        DataTable table = new DataTable();
        table.Columns.Add(new DataColumn("Id", typeof(int)));
        table.Columns.Add(new DataColumn("Name", typeof(string)));
        DataRow row = table.NewRow();
        row["Id"] = 1;
        row["Name"] = "Item";
        table.Rows.Add(row);
        var param = new SqlParameter("@Ids", table) { TypeName = "dbo.Table1Type", SqlDbType = SqlDbType.Structured };

        var query = context.Table1.SqlQuery("SELECT * FROM dbo.func1(@Ids)", param);

        var result = query.ToList();
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 这适用于 EF Core,而错误消息和 OP 代码的结构表明 EF6。 (2认同)
  • EF6 和 EF Core 是完全不同的系统。该问题专门针对 EF6 并请求服务器端“IQueryable”结果,该答案不满足该结果。如果你不能满足要求,正确的答案就是“不可能”。 (2认同)