将Linq查询结果映射到DTO类

时间:2012-02-17 17:50:18

标签: c# linq linq-to-sql linq-to-entities

我想使用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中的每个字段设置所需的属性。我还要在TableA属性字段中使用名称TableB_records填充TableB中的所有匹配记录,并在TableB_DTO中填充TableB_DTO属性中TableC的所有匹配记录,名称为TableC_records

可以这样做吗?我需要改变什么?是linq查询还是我的映射方式

感谢您的时间......

4 个答案:

答案 0 :(得分:6)

我会将您的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
                    }
            }
    };

答案 1 :(得分:4)

首先,我只需要询问您是否可以使用Entity Framework 4.1和POCO(DbContext)并避免使用DTO的altoghther吗?

假设答案是否定的,那一定是因为你没有撤回所有字段,或者你在某种程度上改变了数据的“形状”。

在这种情况下,您可以将LINQ查询更改为如下所示:

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

这样做的好处:如果打开SQL事件探查器,您应该看到只有您请求的列才能进入实际的SQL查询。如果先查询全部然后再拉取值,则所有列都将被拉下线。

答案 2 :(得分:0)

我会制作一个工厂方法,即:TableA_DTO CreateDTO(TableAItem item);

使用此功能,您只需将查询重写为:

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

这将直接为您提供“DTO”对象的集合。

话虽如此,如果你使用的是Entity Framework,那么在这种情况下,最近版本中添加的EF Code First可能会更有用。

答案 3 :(得分:0)

<强>更新

正如其他人所指出的那样,在使用Entity Framework 4.0时不需要展平结果(如下所示),因为它可以将LINQ查询转换为有效的展平结果。因此,仅在使用LINQ to SQL(或可能是其他LINQ提供程序)时才需要以下代码。请注意,我仅使用EF over SQL Server而不是Oracle对此进行了测试,因为此行为可能是LINQ提供程序特定的,这意味着Oracle提供程序(仍处于测试阶段)或Oracle的商业Devart提供程序仍可能正在执行N + 1。


你要做的是获得一组结构像树的对象。没有任何特别注意,您将触发对数据库的许多查询。使用一级嵌套,您将触发 N + 1 查询,但由于您的嵌套深度为两级,因此您将触发 M x(N + 1)+ 1 查询,几乎肯定会对性能非常不利(无论数据集的大小是多少)。您想要的是确保只有一个查询发送到数据库。为了确保这一点,您必须创建一个中间查询来平坦化结果,就像在旧的SQL日期一样,检索树状数据: - )。看一下以下示例: / p>

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(到Objects)来获得我们想要的结构中的数据。

请注意,这也允许您进行分页和排序。这会有点棘手,但肯定不是不可能。