Преобразование выражения LINQ в хранимую процедуру SQL Server

В серверной части .NET моего мобильного приложения Azure мне нужно найти все строки, в которых столбец, состоящий из строки с разделителями-запятыми, содержит все строки в списке. Я использую следующее протестированное выражение LINQ:

List<Items> filteredItems = _context.Items
                                    .Where(item => colorsToFilter.All(color => item.Colors.Contains(color)))
                                    .ToList();

Однако EF6 не поддерживает .All в выражении LINQ. Поэтому я пытаюсь преобразовать это выражение в хранимую процедуру SQL, которая принимает список строк в качестве параметра.

Примеры столбцов из обеих таблиц, используемых в запросе:

Colors:                            ColorsToFilter:

1     blue,green,red               1     blue
2     green                        2     green
3     blue,green,red,black         3     red
4     orange,blue,green,red,gray

Все предметы в ColorsToFilter (List<string>) должны быть в Colors. Таким образом, в этом примере будут возвращены строки 1, 3 и 4.

Как бы я сделал это в SQL, используя хранимую процедуру с параметром таблицы, представляющим список?

Вам нужно начать свой SP ... написание полного SP - слишком широкий вопрос для SO.

Dale K 24.04.2019 23:11

Является ли ColorsToFilter / colorsToFilterList<string> или таблицей, или IQueryable из базы данных в вашем выражении LINQ? Как его можно протестировать, если он не работает в EF6? Лучше всего использовать средство записи Expression для создания предиката, приемлемого для EF, при условии, что colorsToFilter — это короткий список.

NetMage 24.04.2019 23:35

Я протестировал его в проекте C# локально не в базе данных, а в локальном представлении таблицы. ColorsToFilter — это List<string>, который будет передан в хранимую процедуру с использованием параметра таблицы.

AEON 24.04.2019 23:53
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
4
3
940
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Поскольку вы используете All, это на самом деле просто — вы можете просто объединить Where условия в своем запросе и остаться в EF:

var q = _context.Items.AsQueryable();
foreach (var cf in colorsToFilter) {
    q = q.Where(item => item.Colors.Contains(cf));
}

var filteredItems = q.ToList();

Снова используя LINQ, вы можете сделать это в одном выражении:

var filteredItems = colorsToFilter.Aggregate(_context.Items.AsQueryable(), (q, f) => q.Where(item => item.Colors.Contains(f))).ToList();

Обратите внимание, что Contain будет принимать подцвета - если это неприемлемо, вам нужно использовать более жесткие условия, такие как (","+item.Colors+",").Contains(","+cf+","), чтобы, например. синий не будет соответствовать голубому.

NetMage 25.04.2019 19:29

Спасибо, это был просто общий пример, основанный на том, что я пытаюсь сделать. К счастью, у меня не будет этой проблемы с данными, которые я использую, но это полезно знать.

AEON 26.04.2019 04:08

Лично я бы расположил структуру своей таблицы так, чтобы данные, которые я хотел запросить таким образом, не находились в списке csv в столбце таблицы.

Если вы решили использовать хранимую процедуру sql, вы можете использовать приведенную ниже. Вы можете создать тип таблицы и передать его в приложение, затем вы можете использовать функцию string_split, чтобы превратить ваш список csv в таблицу, которую вы можете запросить, и оператор all, чтобы проверить, есть ли все в вашем параметре таблицы. Последний бит, когда я вызываю процедуру, которую вы бы не сделали, вы бы вместо этого вызвали хранимую процедуру в своем С#, в С# вы можете создать таблицу данных с одним строковым столбцом с именем StringValue, заполнить его своим списком цветов фильтра и отправить его как sql (обратите внимание, что вам нужно указать имя вашего пользовательского типа, иначе вы получите сообщение об ошибке.)

   CREATE DATABASE Mycolours;
go
USE Mycolours
GO

/****** Object:  Table [dbo].[Colours]    Script Date: 24/04/2019 23:13:45 ******/
SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

CREATE TABLE [dbo].[Colours](
    [ColourID] [int] NOT NULL,
    [ColourList] [nvarchar](50) NOT NULL
) ON [PRIMARY]
GO
GO
insert into [Colours](ColourID,ColourList) values(1,'blue,green,red'),(2,'green'),(3,'blue,green,red,black'),(4,'orange,blue,green,red,gray')
/****** Object:  StoredProcedure [dbo].[SPGetAllMyColours]    Script Date: 24/04/2019 23:14:10 ******/
SET ANSI_NULLS ON
GO
GO

/****** Object:  UserDefinedTableType [dbo].[StringValues]    Script Date: 24/04/2019 23:14:32 ******/
CREATE TYPE [dbo].[StringValues] AS TABLE(
    [StringValue] [nvarchar](50) NOT NULL,
    PRIMARY KEY CLUSTERED 
(
    [StringValue] ASC
)WITH (IGNORE_DUP_KEY = OFF)
)
GO


SET QUOTED_IDENTIFIER ON
GO

-- =============================================
-- Author:      <Author,,Name>
-- Create date: <Create Date,,>
-- Description: <Description,,>
-- =============================================
CREATE PROCEDURE [dbo].[SPGetAllMyColours]
    @colours dbo.StringValues READONLY
AS
BEGIN
    select * from colours where not exists(select StringValue from @colours where StringValue = all(select value from string_split(colours.ColourList,',')));
END
GO



GO

DECLARE @return_value int
declare @filterColours dbo.StringValues;

insert into @filterColours(StringValue) values('blue'),('green'),('red');

EXEC    @return_value = [dbo].[SPGetAllMyColours] @filterColours

SELECT  'Return Value' = @return_value

GO

Я не был уверен, но представитель Microsoft, с которым я связался, сказал, что я не смогу сделать это с помощью LINQ и вместо этого должен рассмотреть возможность использования хранимой процедуры. Спасибо вам за помощь.

AEON 25.04.2019 04:14

Другие вопросы по теме