Суммировать одно поле в одной таблице и отображать результат из обоих в представлении?

#c# #asp.net-mvc #asp.net-mvc-4 #viewmodel

#c# #asp.net-mvc #asp.net-mvc-4 #viewmodel

Вопрос:

У меня есть две таблицы. Один с сообщениями и один с голосами. Обе таблицы содержат postID (PK / FK). Я хочу сгруппировать все сообщения в таблице голосования и суммировать postVote для каждого сообщения. Затем в представлении, которое я хочу, отобразите сообщение и общее количество голосов за каждое сообщение. Если по этому postID нет голосования, я хочу использовать 0 по умолчанию.

Например, если у меня есть эти данные в таблице post

  -------- --------- ------------- --------- 
| PostId | Message | MessageDate | User_Id |
 -------- --------- ------------- --------- 
| 1      | test 1  | 2016-01-01  | 1       |
 -------- --------- ------------- --------- 
| 2      | test 2  | 2016-01-01  | 1       |
 -------- --------- ------------- --------- 
| 3      | test 3  | 2016-01-01  | 1       |
 -------- --------- ------------- --------- 
 

Таблица голосов

  -------- ------------ --------- 
| PostId | PostVote   | User_Id |
 -------- ------------ --------- 
| 1      | 1          | 1       |
 -------- ------------ --------- 
| 1      | 1          | 2       |
 -------- ------------ --------- 
| 2      | -1         | 1       |
 -------- ------------ --------- 
 

Я хочу отобразить это в представлении

 Message  Vote
Test 1 -  2
Test 2 -  -1
Test 3 -  0
 

Поскольку два пользователя проголосовали 1 (1 1) за postID, один пользователь проголосовал -1 за postID 2, а на postID 3 нет голосов (поэтому в представлении по умолчанию должно быть 0).

Это моя модель post

 public class Post
{
    public Post()
    {
        this.Vote = new HashSet<Vote>();
    }

    public int PostId { get; set; }
    public string Message { get; set; }
    public DateTime MessageDate { get; set; }

    public virtual ApplicationUser User { get; set; }
    public virtual ICollection<Vote> Vote { get; set; }
}
 

Это моя модель голосования

 public class Vote
{
    [Key, Column(Order = 0)]
    public string ApplicationUserID { get; set; }

    [Key, Column(Order = 1)]
    public int PostId { get; set; }

    public virtual Post Post { get; set; }
    public virtual ApplicationUser ApplicationUser { get; set; }

    public int PostVote { get; set; }
}
 

Я думаю, я должен использовать Viewmodel и создал это

 public class ListPostsViewModel
{
    public List<Post> Posts { get; set; }
    public List<Vote> Votes { get; set; }
}
 

Затем я пытаюсь использовать свою ViewModel в контроллере, суммировать голоса на основе postID и объединить сообщения и голоса в представлении. Сейчас это мой контроллер.

 var posts = db.Posts.OrderByDescending(p => p.PostId).ToList();

var postsVM = posts.Select(post => new ListPostsViewModel { Posts = posts});

var votes = db.Votes.GroupBy(u => u.PostId)
                .Select(v =>
                                new
                                {
                                    PostId = v.Key,
                                    TotalVotes = v.Sum(w => w.PostVote) //sum the votes
                                }).ToList();

var votesVM = posts.Select(post => new ListPostsViewModel { Votes = votes });
 

И я получаю эту ошибку на votesVM

Невозможно неявно преобразовать тип ‘System.Коллекции.Generic.List<Анонимный тип #1> «в» систему.Коллекции.Generic.List<FreePost.Models.Vote>’

Комментарии:

1. Ваш запрос создает коллекцию анонимных объектов. Вам нужно спроецировать запрос в используемую вами модель .Select(v => new SomeModel { PostId = v.Key, ... , но ваш текущий Vote класс не имеет свойства для TotalVotes , поэтому вам нужна новая модель. И неясно, как вы ListPostsViewModel сможете сгенерировать это представление — ваша модель должна быть коллекцией модели со свойствами string Message и int TotalVotes

2. Вам нужен один запрос с объединением (по PostId полю) и выберите Message поле из Post таблицы и сумму PostVote полей из Votes таблицы.

3. Не могли бы вы привести мне пример, пожалуйста?

4. Нужно немного поспать, но если вы еще не разобрались с этим, добавлю ответ утром

5. Я предполагаю, что мой контроллер (и viewmodel) ошибочны. Я не хочу иметь свойство для TotalVotes, это вычисленное значение из голосов для просмотра, и я ничего не сохраняю в базе данных. TotalVotes — это сумма всех голосов с одинаковым идентификатором postID.

Ответ №1:

Исключение вызвано тем, что ваш var votes = ... запрос генерирует коллекцию анонимных объектов, которые затем вы пытаетесь присвоить свойству, которое является List<Vote> (они не одного типа).

Чтобы сгенерировать желаемое представление, ваша модель представления должна быть

 public class PostVoteVM
{
    public string Message { get; set; }
    public int TotalVotes { get; set; }
}
 

так что в представлении вы можете использовать

 @model IEnumerable<PostVoteVM>
<table>
    @foreach(var post in Model)
    {
        <tr>
            <td>@post.Message</td>
            <td>@post.TotalVotes </td>
        </tr>
    }
</table>
 

Если ваши модели имеют правильные свойства навигации, вам нужен только один запрос, и результаты запроса проецируются в коллекцию модели представления

 var model = db.Posts.Include(p => p.Vote) // Include may not be required
    .OrderByDescending(p => p.PostId)
    .Select(p => new PostVoteVM()
    {
        Message = p.Message,
        TotalVotes = p.Vote.Sum(v => v.PostVote)
    });
return View(model);