Fred Johnson
Fred Johnson

Reputation: 2695

How do I get an item from an icollection via index when in the class contrusctor I set the icollection as a list?

I have a set of messages each defined by:

namespace DatabaseDesign.Models
{
    public class PrivateMessageDetail
    {
        public int PrivateMessageDetailId { get; set; }
        public DateTime MessageDate { get; set; }
        public string FromUser { get; set; } // Don't need ToUser, it's already in header
        public string Message { get; set; }

        public int PrivateMessageHeaderId { get; set; }
        public virtual PrivateMessageHeader PrivateMessageHeader { get; set; }
    }
}

The collection of messages between two users are held within the Messages iCollection, which is redefined (I think? ) As a list in the contructor of this class:

namespace DatabaseDesign.Models
{
    public class PrivateMessageHeader
    {
        public int PrivateMessageHeaderId { get; set; }
        public PrivateMessageHeader() { this.Messages = new List<PrivateMessageDetail>();  }
        public DateTime TimeStamp { get; set; } // Date of the start of thread
        public string User1 { get; set; }
        public string User2 { get; set; }  // this could be made to a list to allow multiples

        public int numberOfMessages { get; set; }
        public virtual ICollection<PrivateMessageDetail> Messages { get; set; }
    }
}

In my Details controller, I want to viewBag return the private message history (i.e. an instance of PrivateMessageHeader )

    public ActionResult Details()//string userTo, string userFrom)
    {
        string userTo = "mike", userFrom = "tim";

        var MsgHistory = PrivateMessageRepository.GetMessageHistoryByID(userTo, userFrom);
        string MsgStr = "";
        foreach (var msg in MsgHistory.Messages)
        {
            //how do I get the message & user name from each MessageDetail in the Messages List of the MessageHeader class?
            //MsgStr += MsgHistory.Messages[msg].
        }
        return View();   
    }

The line

var MsgHistory = PrivateMessageRepository.GetMessageHistoryByID(userTo, userFrom);

returns the correct PrivateMessageHeader (i.e. the message history between 2 users). I can't seem to get the data out though.

Attempt no.1: I passed the whole thing through the viewbag and attempted to take the data out that way. After 5 minutes of frustration I decided this probably wasn't the best way of doing it anyway. Surely it is more efficient to just store the data as a string and viewbag that bad boy?

So, I went on and did what is currently shown in the Details controller, and messed around trying to access the PrivateMessageDetails held within the Messages List/iCollection. It won't let me index them though in the foreach loop, saying that the iCollection can't be referenced in that way.... but in the contructor I define it as a list???

My queries:

  1. These are entity framework 6 classes. If I define the public virtual ICollection Messages { get; set; } As public virtual List Messages { get; set; } will it still work correctly?

  2. If so, why do all the MSDN examples use ICollection?

  3. Is there an efficient way to take the messages and "userFrom" variables from the iCollection?

This is what I wanted to do:

var MsgHistory = PrivateMessageRepository.GetMessageHistoryByID(userTo, userFrom);
        string MsgStr = "";
        foreach (var msg in MsgHistory.Messages)
        {
            MsgStr += MsgHistory.Messages[msg].UserFrom +": "+MsgHistory.Messages[msg].Message+"<br>";
        }
        Viewbag.Message = MsgStr;
        return View();

Upvotes: 2

Views: 3754

Answers (1)

p.s.w.g
p.s.w.g

Reputation: 149010

There's no need to try to access MsgHistory.Messages as though it was a list. The iterator variable in a foreach-loop is the current item in the list that you're looping through. So you don't need to get that item from the list. Try this:

foreach (var msg in MsgHistory.Messages)
{
    MsgStr += msg.UserFrom + ": " + msg.Message + "<br>";
}

However, I'd strongly recommend not constructing HTML in an MVC controller. This is what views are for. Instead, use your controllers to construct a view model, then pass it to the view, like this:

public ActionResult Details()//string userTo, string userFrom)
{
    var MsgHistory = PrivateMessageRepository.GetMessageHistoryByID(userTo, userFrom);
    var viewModel = MsgHistory.Messages.ToList();
    return View(viewModel);   
}

And construct the HTML in the view like this:

@model IEnumerable<DatabaseDesign.Models.PrivateMessageDetail>
...
@foreach(var msg in Model) {
    @:@msg.UserFrom: @msg.Messages<br>
}

Or possibly use an anonymous type, like this:

public ActionResult Details()//string userTo, string userFrom)
{
    var MsgHistory = PrivateMessageRepository.GetMessageHistoryByID(userTo, userFrom);
    var viewModel = MsgHistory.Messages.Select(x => new { x.UserFrom, x.Message }).ToList();
    return View(viewModel);   
}

With a dynamic view model:

@model IEnumerable<dynamic>
...
@foreach(var msg in Model) {
    @:@msg.UserFrom: @msg.Messages<br>
}

Upvotes: 0

Related Questions