开发者

Too many database calls when mapping to ViewModel

Hey everyone, I'm getting killed on database queries in my MVC3 app when I'm mapping my repository to a View Model. The View is fairly complicated, the main object is Assignment but the View returns data from a few of it's relationships too. The View Mode looks like this

    public int Id { get; set; }
    public DateTime? RelevantDate { get; set; }
    public String Name { get; set; }
    public String ProcessName { get; set; }
    public String Status { get; set; }
    public String Assignee { get; set; }
    public int AssigneeId { get; set; }
    public bool HasAttachment { get; set; }
    public bool IsGroupAssignee { get; set; }
    public bool Overdue { get; set; }
    public List<String> AvailableActions { get; set; }
    public Dictionary<String, String> AssignmentData { get; set; }
    public Dictionary<String, String> CompletionData { get; set; }
    public List<Transactions> Comments { get; set; }
    public List<Transactions> History { get; set; }
    public List<File> Files { get; set; }

There's a lot going on there, but all of the data is relevant to the View. In my repository I explicitly load all of the required relationships with .Include (I'm using Entity Framework) but the data doesn't actually get loaded until I start iterating over the list.

var _assignments = (from ctx.Assignments
                  .Include("Process").Include("Files")
                  .Include("AssignmentDataSet")
                  .Include("Transactions")
                  .where w.Tenant.Id == _tenantId  
                  select w);

In my controller I call a method on the repository that uses a query similar to this to get my data. A few variations but nothing too different from what's above.

Now, here's where I'm chewing up Database Transactions. I have to get this data into a ViewModel so I can display it.

private IList<AssignmentViewModel> CreateViewModel(IEnumerable<Assignment> aList)
    {
        var vList = new List<AssignmentViewModel>();

        foreach (var a in aList)
        {
            var assigneeId = a.Assignee;
            vList.Add(new AssignmentViewModel()
                          {
                              Id = a.Id,
                              AssigneeId = (int) a.Assignee,
                              HasAttachment = (a.Files.Count > 0),
                              Name = a.Name,
                              IsGroupAssignee = a.AssignedToGroup,
                              ProcessName = a.Process.Name,
                              RelevantDate = a.RelevantDate,
                              Status = a.Status,
                              AvailableActions = _assignmentRepository.GetAvailableActions(_user, a),
                              Assignee =
                                  _users.Where(i => i.Id == assigneeId).Select(v => v.FullName).
                                  FirstOrDefault(),
                              AssignmentData =
                                  a.AssignmentDataSet.Where(st => st.State == "Assign").ToDictionary(
                                      d => d.Name, d => d.Value),
                              CompletionData = a.AssignmentDataSet.Where(st => st.State == "Complete").ToDictionary(
                                  d => d.Name, d => d.Value),
                              Comments = a.Transactions.Where(t => t.Action == "New Comment").ToList(),
                              History = a.Transactions.Where(t => t.Action != "New Comment").ToList(),
                              Overdue =
                                  a.RelevantDate >= DateTime.UtcNow.AddHours(-5) || a.RelevantDate == null ||
                                  a.Status == "Complete" || a.Status == "Canceled"
                                      ? false
                                      : true
                          }开发者_JAVA技巧);


        }

        return vList;
    }

This is resulting in approx 2.5 db queries per Assignment. This View will return a max of 30 results. That's a lot of hits to my database. Needless to say, the page is dead slow. Response times of 5-7 seconds. I'm embarrassed by it! Am I just trying to do too much here, or am I just doing it wrong?

How can I optimize this?


Two things I can see:

HasAttachment = (a.Files.Count > 0),

Just use Any() instead so you don't have to iterate over all files assuming this is still an IEnumerable:

HasAttachment = a.Files.Any(),

The other thing is comments and history:

Comments = a.Transactions.Where(t => t.Action == "New Comment").ToList(),
History = a.Transactions.Where(t => t.Action != "New Comment").ToList(),

You can combine these calls by materializing the full list before you create the AssignmentViewModel and then just take the appropriate parts:

var transactions = a.Transactions.ToList();
vList.Add(new AssignmentViewModel()
{
  ..
  Comments = transactions.Where(t => t.Action == "New Comment").ToList(),
  History = transactions.Where(t => t.Action != "New Comment").ToList(), 
}

If you have to support this way of displaying your data you should consider a view and a corresponding entity in the database that selects all or most of the appropriate data for you using joins, so the effort you have to spend on bringing the data to the UI is much less.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜