EF 6.1 Database.SqlQuery投影到复杂类型(多对多关系)

时间:2016-05-31 14:17:21

标签: c# sql database performance entity-framework

在Entity Framework 6.1中是否可以使用 Database.SqlQuery 命令执行包含多对多关系的查询,并将其映射回DTO(使用中间DTO) - 我知道这不能一气呵成)?这样的行动会如何表现?

此示例是我目前面临的问题的非常简化的版本。我只对使用 Database.SqlQuery 可以(不能)完成的事情感兴趣。

我知道我可以使用导航属性(使用Linq),但我正在调查一个更复杂的查询的性能。这只是我试图实现的非常简化的版本。

数据库

enter image description here

DTO

 public class EventDto{

      public int EventId {get;set;}
      public string Name {get;set;}
      public string Slug {get;set;}

      public List<ArtistDto> Headliners {get;set;}
 }

 public class ArtistDto{

      public int ArtistId {get;set;}
      public string Name {get;set;}
      public string Bio {get;set;}
 }

Temp DTO

public class EventWithHeadlinersDto{

      public int EventId {get;set;}
      public string Name {get;set;}
      public string Slug {get;set;}

      public int ArtistId {get;set;}
      public string Name {get;set;}
      public string Bio {get;set;}
 }

代码

 return Context.Database.SqlQuery<EventWithHeadlinersDto>(@"
                                SELECT * FROM [Events] E
                                LEFT JOIN [Headliners] H ON E.EventId = H.EventId
                                LEFT JOIN [Artists] A ON H.ArtistId = A.ArtistId
                                WHERE E.eventid = @eventId",
                               new SqlParameter("eventId", eventId))
                               .ToListAsync();

1 个答案:

答案 0 :(得分:2)

需要一些编码(基本上复制EF查询实现过程),但可行。

首先,应修改临时DTO以包含所有必填字段,并考虑left joins(在需要时使用nullable类型):

public class EventWithHeadlinersDto
{
    // Event Info
    public int EventId { get; set; }
    public string EventName { get; set; }
    public string EventSlug { get; set; }
    // Artist Info
    public int? ArtistId { get; set; }
    public string ArtistName { get; set; }
    public string ArtistBio { get; set; }
}

然后你应该确保SQL SELECT包含所有必要的列,必要时使用别名来匹配DTO属性名称:

var sql = @"
    SELECT
       E.EventId, E.Name EventName, E.Slug EventSlug,
       A.ArtistId, A.Name ArtistName, A.Bio ArtistBio
    FROM [Events] E
    LEFT JOIN [Headliners] H ON E.EventId = H.EventId
    LEFT JOIN [Artists] A ON H.ArtistId = A.ArtistId
    WHERE E.EventId = @eventId";

然后执行sql查询并获得结果DTO设置:

var dataSet = await query.ToListAsync();

最后将其转换为所需的格式:

var eventMap = new Dictionary<int, EventDto>();
var artistMap = new Dictionary<int, ArtistDto>();
foreach (var entry in dataSet)
{
    EventDto @event;
    if (!eventMap.TryGetValue(entry.EventId, out @event))
    {
        @event = new EventDto
        {
            EventId = entry.EventId, 
            Name = entry.EventName,
            Slug = entry.EventSlug,
            Headliners = new List<ArtistDto>()
        };
        eventMap.Add(@event.EventId, @event);
    }
    if (entry.ArtistId != null)
    {
        ArtistDto artist;
        if (!artistMap.TryGetValue(entry.ArtistId.Value, out artist))
        {
            artist = new ArtistDto
            {
                ArtistId = entry.ArtistId.Value,
                Name = entry.ArtistName,
                Bio = entry.ArtistBio,
            };
            artistMap.Add(artist.ArtistId, artist);
        }
        @event.Headliners.Add(artist);
    }
}
var resultSet = eventMap.Values.ToList();

当然,在示例案例中,结果集只包含0或1个项目,但无论应用的过滤如何,上述内容都适用。