Я пытаюсь запросить представление SQL Server с помощью Entity Framework и вернуть только те строки, которые различаются по нескольким столбцам.
Я пробовал решение на основе этого ответа (GroupBy
, а затем Select(g => g.FirstOrDefault())
, но я все еще получаю дублирующиеся строки.
Структура таблицы (это довольно сложное представление в реальной базе данных, но окончательный вывод похож по структуре на этот пример):
CREATE TABLE Example (
ID_A BIGINT,
ID_B BIGINT,
ID_C BIGINT,
Type_A NVARCHAR(50),
Type_B NVARCHAR(50),
ID_Z BIGINT,
Foo NVARCHAR(200),
Bar NVARCHAR(200)
)
Пример данных:
INSERT INTO Example (ID_A, ID_B, ID_C, Type_A, Type_B, ID_Z, Foo, Bar)
VALUES (1, 1, 1, 'TypeA1', 'TypeB1', 1, 'foo1', 'bar1'), -- This row and the next one represent the same main record (1) joined to different "Z" records (1 and 2)
(1, 1, 1, 'TypeA1', 'TypeB1', 2, 'foo1', 'bar1'),
(2, 1, 2, 'TypeA2', 'TypeA2', 1, 'foo2', 'bar2'), -- This row and the next two represent the same main record (2) joined to different "Z" records (1, 2 and 3)
(2, 1, 2, 'TypeA2', 'TypeA2', 2, 'foo2', 'bar2'),
(2, 1, 2, 'TypeA2', 'TypeA2', 3, 'foo2', 'bar2')
Класс объекта:
public class ExampleEntity
{
[Key]
public long ID_A { get; set; }
public long ID_B { get; set; }
public long ID_C { get; set; }
public string Type_A { get; set; }
public string Type_B { get; set; }
public long? ID_Z { get; set; }
public string Foo { get; set; }
public string Bar { get; set; }
Конфигурация объекта:
public class ExampleEntityConfiguration : EntityTypeConfiguration<ExampleEntity>
{
public ExampleEntityConfiguration()
{
// Properties
this.Property(t => t.ID_A)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
// Table & Column Mappings
this.ToTable("Example");
this.Property(t => t.ID_A).HasColumnName("ID_A");
this.Property(t => t.ID_B).HasColumnName("ID_B");
this.Property(t => t.ID_C).HasColumnName("ID_C");
this.Property(t => t.Type_A).HasColumnName("Type_A");
this.Property(t => t.Type_B).HasColumnName("Type_B");
this.Property(t => t.ID_Z).HasColumnName("ID_Z");
this.Property(t => t.Foo).HasColumnName("Foo");
this.Property(t => t.Bar).HasColumnName("Bar");
}
}
ID_A
, ID_B
, ID_C
, Type_A
и Type_B
идентифицируют "основной" объект, а ID_Z
идентифицирует объединенный объект "Z". Foo
и Bar
являются неуникальными столбцами данных, которые необходимобыть включены в окончательные результаты.
Для каждой комбинации основных значений идентификатора / типа может быть несколько значений ID_Z
. Мне нужно отфильтровать по значениям ID_Z
и затем вернуть отдельные значения основного объекта(на основе значений идентификатора / типа).
Я пробовал запрос, подобный следующему:
// The `ID_Z` values to filter on
var zIDs = new List<long> { 1, 2 };
var result = context.Set<ExampleEntity>()
.Where(e => zIDs.Contains(e.ID_Z))
.GroupBy(e => new { e.ID_A, e.ID_B, e.ID_C, e.Type_A, e.Type_B })
.Select(g => g.FirstOrDefault())
.ToList();
Что приводит к этому SQL:
SELECT
[Limit1].[ID_A] AS [ID_A],
[Limit1].[ID_B] AS [ID_B],
[Limit1].[ID_C] AS [ID_C],
[Limit1].[Type_A] AS [Type_A],
[Limit1].[Type_B] AS [Type_B],
[Limit1].[ID_Z] AS [ID_Z],
[Limit1].[Foo] AS [Foo],
[Limit1].[Bar] AS [Bar]
FROM (
SELECT [Extent1].[ID_A] AS [ID_A], [Extent1].[ID_B] AS [ID_B], [Extent1].[ID_C] AS [ID_C], [Extent1].[Type_A] AS [Type_A], [Extent1].[Type_B] AS [Type_B]
FROM [dbo].[Example] AS [Extent1] WITH (NOLOCK)
WHERE
([Extent1].[ID_Z] IN (cast(1 as bigint), cast(2 as bigint))) AND ([Extent1].[ID_Z] IS NOT NULL)
) AS [Filter1]
OUTER APPLY (
SELECT TOP (1)
[Extent2].[ID_A] AS [ID_A],
[Extent2].[ID_B] AS [ID_B],
[Extent2].[ID_C] AS [ID_C],
[Extent2].[Type_A] AS [Type_A],
[Extent2].[Type_B] AS [Type_B],
[Extent2].[ID_Z] AS [ID_Z],
[Extent2].[Foo] AS [Foo],
[Extent2].[Bar] AS [Bar]
FROM [dbo].[Example] AS [Extent2] WITH (NOLOCK)
WHERE
([Extent2].[ID_Z] IN (cast(1 as bigint), cast(2 as bigint))) AND ([Extent2].[ID_Z] IS NOT NULL) AND
([Filter1].[ID_A] = [Extent2].[ID_A]) AND
([Filter1].[ID_B] = [Extent2].[ID_B]) AND
([Filter1].[ID_C] = [Extent2].[ID_C]) AND
(([Filter1].[Type_A] = [Extent2].[Type_A]) OR (([Filter1].[Type_A] IS NULL) AND ([Extent2].[Type_A] IS NULL))) AND
(([Filter1].[Type_B] = [Extent2].[Type_B]) OR (([Filter1].[Type_B] IS NULL) AND ([Extent2].[Type_B] IS NULL)))
) AS [Limit1]
Но это, похоже, возвращает все строки, соответствующие фильтру Z_ID
(что приводит к дублированию "основных" значений)возвращать только первую строку для каждого набора «основных» значений ID / типа.
Если я материализую (ToList
) запрос сразу после GroupBy
, я, кажется, получаю правильные группировки;но я хотел бы запустить все это в БД и избегать использования запросов LINQ to Objects.
Как я могу создать этот запрос?