Ядро Entity Framework — Получение нескольких строк из одной таблицы в качестве одного DTO с несколькими свойствами

#c# #entity-framework #entity-framework-core

Вопрос:

Можно ли выделить две строки в один анонимный объект DTO с двумя свойствами? С такой моделью, как:

 public class Document
{
    public int Id { get; set; }
    public string Text { get; set; }
    // Other properties
}
 

Я пишу метод, который находит разницу между двумя версиями документа:

 public Task<string> CompareVersions(int initialId, int finalId)
 

Поэтому мне нужно получить текст ровно двух Documents по идентификатору, и мне нужно знать, кто из них был кем.

В настоящее время я строю Dictionary<int, string> , делая:

 var dto = await _context.Documents
    .Where(doc => doc.Id == initialId
               || doc.Id == finalId)
    .ToDictionaryAsync(x => x.Id, x => x.Text);
 

а потом позвонил dto[initialId] , чтобы получить сообщение. Однако это кажется очень громоздким. Есть ли какой-либо способ взять два идентификатора и выбрать их в один DTO в форме

 {
    InitialText,
    FinalText
}
 

Ответ №1:

Вы должны использовать SelectMany

 var query = 
   from initial in _context.Documents
   where initial.Id = initialId
   from final in _context.Documents
   where final.Id = finalId
   select new 
   {
      InitialText = initial.Text,
      FinalText = final.Text
   };

var result = await query.FirstOrDefaultAsync();
 

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

1. Хотя я предпочитаю стиль lambda стилю запросов, эта версия настолько элегантнее, что я с радостью приму ее.

Ответ №2:

Aggregate тоже может это сделать

 var dto = (await _context.Documents
.Where(doc => doc.Id == initialId || doc.Id == finalId).ToListAsync())
.Aggregate(
  new { InitialText = "", FinalText = "" }, 
  (seed, doc) => { 
    if(doc.Id == initialId) 
      seed.InitialText = doc.Text; 
    else
      seed.FinalText = doc.Text;
  }
);
 

Я не уверен, что мне это нравится больше, чем ваш подход к составлению словаря, но с фактическим dto в конце, а не со словарем:

 var d = await _context.Documents
.Where(doc => doc.Id == initialId || doc.Id == finalId)
.ToDictionaryAsync(x => x.Id, x => x.Text);
var dto = new { InitialText = d[initialId], FinalText = d[finalId] };
 

Вы также могли бы, возможно, просто:

 var dto = new { 
  InitialText = await context.Documents
.FindAsync(initialId),
  FinalText = await context.Documents
.FindAsync(finalId)
};