Mapping Linq Query results to a DTO class

前端 未结 4 1076
鱼传尺愫
鱼传尺愫 2020-12-17 21:43

I want to get records from the database using EF and assign the values to a DTO class.Consider the following tables for a Linq query.

TableA,TableB, TableC

F

相关标签:
4条回答
  • 2020-12-17 21:55

    UPDATE

    As others pointed out, flattening the results (as shown below) is not needed when working with Entity Framework 4.0, since it can translate the LINQ query to an efficient flattened result for you. Therefore, the following code is only needed when working with LINQ to SQL (or possibly other LINQ providers). Note that I have only tested this with EF over SQL Server and not over Oracle, since this behavior could be LINQ provider specific, which means that the Oracle provider (still in beta) or the commercial Devart provider for Oracle could still be doing N + 1.


    What you are trying to do is to get a set of objects that are structured like a tree. Without any special care, you will be triggering many queries to the database. With one level of nesting you would be triggering N + 1 queries, but since your nesting is two levels deep, you will be triggering M x (N + 1) + 1 queries, which will almost certainly be very bad for performance (no matter what the size of your data set is). What you want is to make sure that there is only a single query sent to the database. To ensure this, you must create an intermediate query that flattens the result, just as you would have done in the good old SQL days, to retrieve tree like data :-). Take a look at the following example:

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

    The next step is to transform that in-memory data structure (using that anonymous type) into the tree structure of DTO objects:

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

    As you can see, the first part of the solution is actually the 'old' way of doing this, way back when we would still write our SQL queries by hand. Nice however is, that once we get this typed set of in-memory data, we can leverage LINQ (to Objects) again to get this data in the structure we want.

    Note that this also allows you to do paging and sorting. This will be a bit more tricky, but certainly not impossible.

    0 讨论(0)
  • 2020-12-17 22:03

    I would make a factory method, ie: TableA_DTO CreateDTO(TableAItem item);

    Using this, you could just rewrite your query as:

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

    This would give you the collection of "DTO" objects directly.

    That being said, if you're using Entity Framework, the EF Code First added in recent versions might be more useful in this case.

    0 讨论(0)
  • 2020-12-17 22:08

    First thing, I just need to ask whether you can use Entity Framework 4.1 and POCOs (DbContext) and avoid the need for DTO's altoghther?

    Assuming that the answer is no, that must be because you are not pulling back all of the fields, or you are somehow altering the "shape" of the data.

    In that case, you could change your LINQ query to look something like this:

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

    The benefit of doing it this way: If you turn on SQL Profiler, you should see that only the columns that you request make it into the actual SQL query. If you query it all first and then pull the values, all of the columns will be pulled down the wire.

    0 讨论(0)
  • 2020-12-17 22:10

    I would change your DTO from List to IEnumerable and than do everything in a LINQ query.

    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
                        }
                }
        };
    
    0 讨论(0)
提交回复
热议问题