Model count = null - asp.net-mvc

I'm rewriting this question:
I have 2 models. Entry and Topic.
public class Entry
{
public int EntryId { get; set; }
public int UserId { get; set; }
public int TopicId { get; set; }
public String EntryQuestion { get; set; }
public String EntryAnswer { get; set; }
public int EntryReview { get; set; }
public String QuestionValidationURL { get; set; }
public virtual ICollection<Topic> TopicList { get; set; }
}
public class Topic
{
public int TopicId { get; set; }
public String TopicName { get; set; }
}
I followed an example on ASP.Net/MVC to set up my models this way.
What I would like to do is for every entry item I have a TopicId, but then I'd like to convert that to a TopicName by accessing my TopicList.
My question is, how do I load TopicList?
In the examples I'm following I'm seeing something about LazyLoading and EagerLoading, but it doesn't seem to be working.
I tried doing the following from my Entry controller:
db.Entries.Include(x => x.TopicList).Load();
But that still gives me a TopicList of 0 (which is better than null)
How can I do this?
In my view I'm binding to the Entries like this:
#model IEnumerable<projectInterview.Models.Entry>
I would like to access the TopicList here:
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.TopicId)
</td>
...
</tr>
I'd like to use the TopicId in this loop and display the TopicName that is part of the object in the collection.

I'm assuming you're following an Entity Framework example. You're trying to create a one-to-many relationship, as far as I can tell, although I'm unsure about which end is which.
In the general case, to establish a one-to-many relationship, you have to do something like this:
public class One
{
[Key]
public int Id { get; set; }
public virtual ICollection<Many> Many { get; set; }
}
public class Many
{
[Key]
public int Id { get; set; }
[ForeignKey("One")]
public int OneId { get; set; }
public virtual One One { get; set; }
}
If what you're trying to do is have one Entry relating to many Topic objects, then you're almost there but you're lacking something.
For the ICollection<Topic> to actually contain anything, the (many) Topic objects need to have a foreign key to the (one) Entry. (It also doesn't hurt to explicitly mark the primary key on both sides, rather than relying on the EF conventions.)
public class Topic
{
[Key]
public int TopicId { get; set; }
public String TopicName { get; set; }
[ForeignKey("Entry")]
public int EntryId { get; set; }
public virtual Entry Entry { get; set; }
}
public class Entry
{
[Key]
public int EntryId { get; set; }
public int UserId { get; set; }
public int TopicId { get; set; }
public String EntryQuestion { get; set; }
public String EntryAnswer { get; set; }
public int EntryReview { get; set; }
public String QuestionValidationURL { get; set; }
public virtual ICollection<Topic> TopicList { get; set; }
}
Now TopicList should be an actual and populated collection, without the need to do an Include.
If, on the other hand, you want one Topic relating to many Entry objects, then you have it a little backwards. The correct way would be:
public class Topic
{
[Key]
public int TopicId { get; set; }
public String TopicName { get; set; }
public virtual ICollection <Entry> Entries { get; set; }
}
public class Entry
{
[Key]
public int EntryId { get; set; }
public int UserId { get; set; }
public String EntryQuestion { get; set; }
public String EntryAnswer { get; set; }
public int EntryReview { get; set; }
public String QuestionValidationURL { get; set; }
[ForeignKey("Topic")]
public int TopicId { get; set; }
public virtual Topic Topic { get; set; }
}
In this case, you may or may not use db.Entries.Include(x => x.Topic) depending on whether you want them loaded all at once or one-by-one on demand. Regardless of what you choose, the following expression should return the proper value:
myEntry.Topic.TopicName

If I understand you correctly you have added the list of Topics to the Entry just to get the name of the topic when displaying the entry. The best way to do this is to actually have a Topic property in your entry model. So your model would look like this:
public class Entry
{
public int EntryId { get; set; }
public int UserId { get; set; }
public int TopicId { get; set; }
public String EntryQuestion { get; set; }
public String EntryAnswer { get; set; }
public int EntryReview { get; set; }
public String QuestionValidationURL { get; set; }
//Change this.....
public virtual Topic Topic { get; set; }
}
Then in your view you would use (assuming the Model is an IEnumerable):
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => modelItem.Topic.TopicName )
</td>
...
</tr>
This link has a great example of how to do this:
http://weblogs.asp.net/manavi/archive/2011/03/28/associations-in-ef-4-1-code-first-part-2-complex-types.aspx

In my opinion problem is with casting. In view you have IEnumerable<projectInterview.Models.Entry> while Topics is ICollection<Topic>, which is a collection of different type

Topics = null means there are no Topics in the list to iterate over. How do you fill them? Your view expects IEnumerable how do you cast your topics to the entries?
Based on the original question I've added a small working example, maybe it helps you to find your bug.
Controller:
public class TestController : Controller
{
public ActionResult Index()
{
var viewModel = new ViewModel()
{
Topics = new List<Topic>()
};
viewModel.Topics.Add(new Topic() { header = "test" });
viewModel.Topics.Add(new Topic() { header = "test2" });
return View(viewModel);
}
}
Model:
public class ViewModel
{
public virtual ICollection<Topic> Topics { get; set; }
public int getCount()
{
return Topics.Count;
}
}
public class Topic
{
public string header { get; set; }
}
View:
#model testProject.Models.ViewModel
#{
ViewBag.Title = "Index";
}
<h2>Index</h2>
#Model.getCount()
#foreach(var item in Model.Topics)
{
<div>#item.header</div>
}
Output:
Index
2
test
test2

It seems that you are not initializing your Topics anywhere in the code. If the collection is null it means it is not initialized. If you instantiate it with
ICollection<Topic> Topics = new List<Topic>();
Once initialized you should receive zero when calling Topics.Count. If you do not make a call to a database it will stay zero.
In your case check whether you are instantiating the Topics.

Related

CS0411: the type arguments for method cannot be inferred from the usage mvc

I have looking on SO but I didn't find a solution and I'm totally lost the correct way to fix this.
What's happening?
I'm getting error CS0411 in a view: the type arguments for method cannot be inferred from the usage mvc
I'm using a Tuple in that view to use 2 models.
var model = new Tuple<IList<TimeTableInsertModel>, List<Ticket>>(ttc.getTimeTableDetails(id), ticket);
#model Tuple<IList<TimeTableInsertModel>, List<Ticket>>
And I want to use the field TicketQuantity.
#Html.HiddenFor(item.TicketQuantity)
But that's give the error. I can use the property in the view without any #Html functions.
The views:
public class TimeTableInsertModel
{
[Key]
public int TimeTableId { get; set; }
public int MovieId { get; set; }
public int RoomId { get; set; }
public int SeatsAvaible { get; set; }
public DateTime StartTime { get; set; }
public DateTime EndTime { get; set; }
public virtual Movie Movie { get; set; }
public virtual ICollection<Reservation> Reservations { get; set; }
public virtual Room Room { get; set; }
public int TicketQuantity { get; set; }
}
public partial class Ticket
{
public Ticket()
{
ReservationTickets = new HashSet<ReservationTicket>();
}
public int TicketId { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
public virtual ICollection<ReservationTicket> ReservationTickets { get; set; }
}
I'm assuming for the moment you're looping through an IEnumerable like this:
#foreach (var item in Model.Item1)
{
...
Html.HiddenFor(item.TicketQuantity);
...
}
The HiddenFor method takes a delegate as an argument, so instead of this:
#Html.HiddenFor(item.TicketQuantity)
you need to do something like this:
#Html.HiddenFor( m = > item.TicketQuantity)

Populating navigation properties of navigation properties

How do I populate a navigation property with specific value?
I have 3 models, Game, UserTeam, User, defined below. I have a razor view which uses the model IEnumerable. This view loops over the Games, and within that loop, loops over the UserTeams. So far, so good.
Within the UserTeam loop, I want to access the User properties, but they are null. How do I populate the User navigation property for each UserTeam object? Do I need a constructor with a parameter in the UserTeam model?
Models
public class Game
{
public Game()
{
UserTeams = new HashSet<UserTeam>();
}
public int Id { get; set; }
public int CreatorId { get; set; }
public string Name { get; set; }
public int CurrentOrderPosition { get; set; }
public virtual UserProfile Creator { get; set; }
public virtual ICollection<UserTeam> UserTeams { get; set; }
}
public class UserTeam
{
public UserTeam()
{
User = new UserProfile();
}
public int Id { get; set; }
public int UserId { get; set; }
public int GameId { get; set; }
public int OrderPosition { get; set; }
public virtual UserProfile User { get; set; }
public virtual Game Game { get; set; }
public virtual IList<UserTeam_Player> UserTeam_Players { get; set; }
}
public class UserProfile
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int UserId { get; set; }
public string UserName { get; set; }
public string test { get; set; }
public UserProfile()
{
UserTeams = new HashSet<UserTeam>();
}
public virtual ICollection<UserTeam> UserTeams { get; set; }
[ForeignKey("CreatorId")]
public virtual ICollection<Game> Games { get; set; }
}
Loop in my Razor view (Model is IEnumerable)
#foreach (var item in Model) {
#foreach (var userteam in item.UserTeams) {
#Html.ActionLink("Join game as"+userteam.User.UserName, "JoinGame", new { gameid = item.Id, userid=userteam.UserId })
}
}
Method in my repository that returns the Games
public IEnumerable<Game> GetAllGames()
{
using (DataContext)
{
var gm = DataContext.Games.Include("UserTeams").ToList();
return gm;
}
}
You would need to include this in your repository method. If you are using eager loading then it would be something like
var gm = DataContext.Games
.Include(x => x.UserTeams)
.Include(x => x.UserTeams.Select(y => y.User))
.ToList();
I have not done this without using LINQ for my queries, but I assume it would be something like:
var gm = DataContext.Games.Include("UserTeams.User").ToList();
Hopefully this helps you out

EF code first: using Linq with many-to-many relationship

I am using MVC 4 with EF code first approach. I have two simple objects. These are their POCO classes:
public class Activity
{
//Primitive Properties
[HiddenInput]
public int Id { get; set; }
[Required]
public int LengthInMinutes { get; set; }
public string AdditionalInfo { get; set; }
[Required]
public bool Archive { get; set; }
//Navigation Properties
public virtual User User { get; set; }
public virtual ActivitySet ActivitySet { get; set; }
public virtual ICollection<Company> Companies { get; set; }
public virtual ICollection<Description> Descriptions { get; set; }
}
public class Company
{
//Primitive Properties
[HiddenInput]
public int Id { get; set; }
[Required]
public string Title { get; set; }
[Required]
public bool Archive { get; set; }
//Navigation Properties
public virtual ICollection<Activity> Activities { get; set; }
}
Now, I have generic list of activities which I iterate through using foreach loop. While looping I want to write a name for each Company related to the activity from the list. This is a code I came up with:
#foreach (Activity a in Model)
{
<p>#a.Companies.Where(d => d.Activities.FirstOrDefault(y => y.Id == a.Id)).Single()</p>
}
Unfortunately it gives me compilation error when I build the project. How can I then access details of the elements with many-to-many relationship
You could try it like so:
#foreach (Activity a in Model)
{
<p>#string.Join(",", a.Companies.Select(c => c.Title))</p>
}
It should give a list of all company titles of a given activity separated by a comma.

Need to map from two objects into a single one

I have the following entity model:
public class Project
{
[Key]
public int ProjectID { get; set; }
public string Title { get; set; }
public string Slug { get; set; }
public string Content { get; set; }
public string Category { get; set; }
public string Client { get; set; }
public int Year { get; set; }
// more attributes here...
}
I would like to prepare a view model (specific for my view). Here is the view model:
public class ProjectListViewModel
{
public IEnumerable<ProjectInfos> ProjectList { get; set; }
public PagingInfo Paging { get; set; }
public class ProjectInfos
{
public string Title { get; set; }
public string Slug { get; set; }
public string Content { get; set; }
public string Category { get; set; }
public string Client { get; set; }
public int Year { get; set; }
}
public class PagingInfo
{
public int TotalItems { get; set; }
public int ItemsPerPage { get; set; }
public int CurrentPage { get; set; }
public int TotalPages { get; set; }
}
}
In my controller, I would like to prepare the view model by filling it with 2 different objects:
List of projects
Paging information
Here is my controller:
public ViewResult List(string category, int page = 1)
{
IEnumerable<Project> projectList = m_Business.GetProjects(category, page, 10);
PagingInfo pagingInfo = m_Business.GetPagingInfo(category, page, 10);
// Here I need to map !!
ProjectListViewModel viewModel = .....
return View(viewModel);
}
So how can I proceed in my controller? I know we can use automapper to map from one object to another but here I need to map from two objects into a single one.
Thanks.
You can extend AutoMapper to map multiple objects.
Here is a blog which provides some sample cope.
Then you can use code like this:
var personViewModel = EntityMapper.Map<PersonViewModel>(person, address, comment);

Automapper list to int (count)

I'm creating ViewModels in my MVC application. We are using automapper for domain model to view model conversion. My question is I keep getting a circular reference error when doing ajax stuff in MVC (seems like it's the JavaScriptSerializer that's causing problems), so instead of getting back a list of Projects, I just need the count (because that's all my view model needs). Here is a sample of the hierarchy. Thanks in advance for any advice!
public class ProjectViewModel
{
public int ProjectID { get; set; }
[Required]
[UIHint("Project Name")]
public string Name { get; set; }
public ICollection<ProjectGroupViewModel> ProjectGroups { get; set; }
}
public class ProjectGroupViewModel
{
public int ProjectGroupID { get; set; }
[Required]
public string Name { get; set; }
//THIS is what I Want to have as int ProjectCount
public ICollection<ProjectViewModel> Projects { get; set; }
}
public class ProjectGroupViewModel
{
public int ProjectGroupID { get; set; }
[Required]
public string Name { get; set; }
//THIS is what I Want to have as int ProjectCount
public int ProjectsCount { get; set; }
}
AutoMapper.Mapper.CreateMap<ProjectGroup, ProjectGroupViewModel>()
.ForMember(x => x.ProjectsCount, o => o.MapFrom(x => x.Projects.Count()))

Resources