Отображение результатов Linq Query в класс DTO - PullRequest
9 голосов
/ 17 февраля 2012

Я хочу получить записи из базы данных, используя EF, и присвоить значения классу DTO. Рассмотрим следующие таблицы для запроса Linq.

TableA, TableB, TableC

Для каждогоТаблица TableA есть несколько записей в TableB.Для каждой записи TableB есть несколько записей в TableC.Теперь мои DTO выглядят так:

public class TableA_DTO
{
    public int tableA_rowid { get; set; }
    //remaining tableA field definitions

    public List<TableB_DTO> TableB_records { get; set; }
}

public class TableB_DTO
{
    public int tableB_rowid { get; set; }
    //remaining tableB  field definitions

    public List<TableC_DTO> TableC_records { get; set; }
}

public class TableC_DTO
{
    public int tableC_rowid { get; set; }
    //remaining tableC field definitions
}

мой запрос linq выглядит примерно так

var qry = from ent in TableA
          select ent;

В моем классе отображения я перебираю элементы в результате запроса так:

    foreach (var dataitem in query)
    {
        TableA_DTO dto = new TableA_DTO();
        dto.tableA_rowid =  dataitem.ID;
        //remaining field definitions here
    }

Теперь это работает для всех полей в TableA, где он извлекает одну запись из базы данных и устанавливает необходимые свойства в TableA_DTO для каждого поля в таблице TableA.Я также хочу заполнить все соответствующие записи в TableB в поле свойства TableA именем TableB_records, а также в TableB_DTO все соответствующие записи из TableC в свойстве TableB_DTO именем TableC_records

Можно ли это сделать?Что мне нужно изменить?Это запрос linq или способ, которым я делаю свое отображение

Спасибо за ваше время ...

Ответы [ 4 ]

6 голосов
/ 17 февраля 2012

Я бы изменил ваш DTO с List на IEnumerable, а затем сделал все в запросе LINQ.

var query = 
    from ent in TableA
    select new TableA_DTO
    {
        TableAProperty = a.Property,
        TableB_records = 
            from b in TableB
            where ent.Key == b.Key
            select new TableB_DTO
            {
                TableBProperty = b.Property,
                TableC_records =
                    from c in TableC
                    where b.Key == c.Key
                    select new TableC_DTO
                    {
                        TableCProperty = c.Property
                    }
            }
    };
4 голосов
/ 17 февраля 2012

Во-первых, мне просто нужно спросить, можете ли вы использовать Entity Framework 4.1 и POCO (DbContext) и избежать необходимости использования DTO?

Предполагая, что ответ отрицательный, это должно быть потому, что вы не вытягиваете все поля или вы каким-то образом изменяете «форму» данных.

В этом случае вы можете изменить запрос LINQ, чтобы он выглядел примерно так:

from t in table
where ...
select new DTOA()
{
  TheDtoProperty = theTableProperty,
  AndSoOn = AndSoOn
};

Преимущество такого подхода: если вы включите SQL Profiler, вы должны увидеть, что только те столбцы, которые вы запрашиваете, превращают его в настоящий запрос SQL. Если вы сначала запросите все это, а затем извлечете значения, все столбцы будут вытянуты по проводам.

0 голосов
/ 17 февраля 2012

UPDATE

Как отмечали другие, при работе с Entity Framework 4.0 выравнивание результатов (как показано ниже) не требуется, поскольку оно может преобразовать запрос LINQ в эффективный для вас плоский результат. Поэтому следующий код необходим только при работе с LINQ to SQL (или, возможно, другими поставщиками LINQ). Обратите внимание, что я проверял это только с EF поверх SQL Server, а не над Oracle, поскольку такое поведение может зависеть от поставщика LINQ, что означает, что поставщик Oracle (все еще в бета-версии) или коммерческий поставщик Devart для Oracle все еще может выполнять N + 1.


То, что вы пытаетесь сделать, это получить набор объектов, которые структурированы как дерево. Без особой заботы вы будете вызывать много запросов к базе данных. С одним уровнем вложенности вы будете запускать N + 1 запросов, но так как ваша вложенность имеет два уровня глубины, вы будете запускать M x (N + 1) + 1 запросов, что почти наверняка будет очень плохо для производительности (независимо от размера вашего набора данных). Вам нужно убедиться, что в базу данных отправлен только один запрос. Чтобы убедиться в этом, вы должны создать промежуточный запрос, который выравнивает результат, как вы это делали в старые добрые времена SQL, для извлечения данных, подобных дереву: -). Взгляните на следующий пример:

var records =
    from record in db.TableC
    where ... // any filtering can be done here
    select record;

// important to call ToArray. This ensures that the flatterned result
// is pulled in one single SQL query.
var results = (
    from c in records
    select new
    {
        tableA_rowid = c.B.A.Id,
        tableA_Prop1 = c.B.A.Property1,
        tableA_Prop2 = c.B.A.Property2,
        tableA_PropN = c.B.A.PropertyN,
        tableB_rowid = c.B.Id,
        tableB_Property1 = c.B.Property1,
        tableB_Property2 = c.B.Property2,
        tableB_PropertyN = c.B.PropertyN,
        tableC_rowid = c.Id,
        tableC_Property1 = c.Property1,
        tableC_Property2 = c.Property2,
        tableC_PropertyN = c.PropertyN,
    })
    .ToArray();

Следующим шагом является преобразование этой структуры данных в памяти (с использованием этого анонимного типа) в древовидную структуру объектов DTO:

// translate the results to DTO tree structure
TableA_DTO[] dtos = (
    from aresult in results
    group aresult by aresult.tableA_rowid into group_a
    let a = group_a.First()
    select new TableA_DTO
    {
        tableA_rowid = a.tableA_rowid,
        tableA_Prop1 = a.tableA_Prop1,
        tableA_Prop2 = a.tableA_Prop2,
        TableB_records = (
            from bresult in group_a
            group bresult by bresult.tableB_rowid into group_b
            let b = group_b.First()
            select new TableB_DTO
            {
                tableB_rowid = b.tableB_rowid,
                tableB_Prop1 = b.tableB_Prop1,
                tableB_Prop2 = b.tableB_Prop2,
                TableC_records = (
                    from c in group_b
                    select new TableC_DTO
                    {
                        tableC_rowid = c.tableC_rowid,
                        tableC_Prop1 = c.tableC_Prop1,
                        tableC_Prop2 = c.tableC_Prop2,
                    }).ToList(),
            }).ToList()
     })
    .ToArray();

Как видите, первая часть решения на самом деле является «старым» способом сделать это, еще тогда, когда мы все еще писали наши SQL-запросы вручную. Приятно, однако, что как только мы получим этот типизированный набор данных в памяти, мы снова сможем использовать LINQ (для объектов), чтобы получить эти данные в нужной нам структуре.

Обратите внимание, что это также позволяет вам выполнять разбиение по страницам и сортировку. Это будет немного сложнее, но, конечно, не невозможно.

0 голосов
/ 17 февраля 2012

Я бы сделал заводской метод, т.е.: TableA_DTO CreateDTO(TableAItem item);

Используя это, вы можете просто переписать ваш запрос как:

IEnumerable<TableA_DTO> = TableA.AsEnumerable().Select(CreateDTO);

Это даст вам коллекцию объектов "DTO" напрямую.

При этом, если вы используете Entity Framework, EF Code First , добавленный в последних версиях, может быть более полезным в этом случае.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...