EF: передача параметра с табличным значением в пользовательскую функцию из C#

#c# #sql-server #entity-framework #entity-framework-6 #table-valued-parameters

#c# #sql-сервер #сущность-фреймворк #entity-framework-6 #параметры с табличным значением #entity-framework

Вопрос:

У меня есть пользовательская функция в SQL Server, которая принимает TVP (параметр с табличным значением) в качестве параметра. В EF, как мне вызвать такую функцию из C # ?

Я попытался использовать этот метод ObjectContext.CreateQuery<> , но получил следующую ошибку:

Параметр ‘param’ функции ‘QueryByParam’ недопустим. Параметры могут быть только того типа, который может быть преобразован в скалярный тип 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);
}
  

Требование состоит в том, что нам нужен IQueryable back, а не потребляемый результат.

Комментарии:

1. Возможно, эта информация будет полезной entityframeworkcore.com/knowledge-base/57056492 /…

2. В обычном случае мы получаем возврат как IQueryable. Но он не работает, когда входным параметром является Tvp. Для нас входным параметром является tvp (например;- IEnumerable<int>). Нам нужен вывод в виде IQueryable.

3. Попробуйте эту информацию: c-sharpcorner.com/uploadfile/78607b /… .

4. @MikeJ, не связано. Голда спрашивает о случае функции с табличным значением.

5. Я думаю, что простая истина заключается в том, что ObjectParameter никогда не может быть TVP.

Ответ №1:

Вы можете сделать это с помощью необработанного Sql en EF Core, аналогичного подхода в EF6, но вы не можете получить IQueryable. Оба примера ниже.

Ядро структуры сущностей

Введите SQL, чтобы использовать его в качестве фильтра списка:

 CREATE TYPE [dbo].[Table1Type] AS TABLE(
    [Id] [int] NULL,
    [Name] [nchar](10) NULL
)
  

SQL UDF:

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

Контекст EF:

 public class MyContext : DbContext
{
    public DbSet<Table1> Table1 { get; set; }
}
  

DTO для соответствия типу sql (также совпадает с table для простоты):

 public class Table1
{
    public int Id { get; set; }
    public string Name { get; set; }
}
  

Пример:

 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();
    }
}
  

Entity Framework 6

Вы не можете получить IQueryable, но вы можете выполнить linq с результирующим 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();
    }
}
  

Комментарии:

1. @golda, это то, что ты ищешь? Может быть, вы можете немного уточнить, если этот ответ вам не поможет.

2. Это относится к ядру EF, в то время как сообщение об ошибке и структура операционного кода указывают EF6.

3. @IvanStoev, я отредактировал ответ для аналогичного ответа в EF6, однако, не совсем так, как нужно, поскольку вы не можете получить IQueryable из SqlQuery.

4. Спасибо @nerlijma. Приятно знать, что это можно сделать в EFCore.

5. EF6 и EF Core — это совершенно разные системы. Вопрос конкретно нацелен на EF6 и запрашивает результат на стороне сервера IQueryable , который этот ответ не удовлетворяет. Если вы не можете удовлетворить требованиям, правильный ответ просто «Невозможно».