Link in partial Razor Page fires wrong OnGet - asp.net-core

I am using a razor page to display an ItemT model and at the bottom I inserted a partial view to show all properties (which are not assigned to this model in a n:m relation).
The PageModel 'FreePropertiesToItemT' has two public properties:
public ItemT ItemT { get; set; } // the one Item to show
public IList<PropertyIndexViewModel> FreeProperties { get; set; } // all free properties
An OnGetAsync(int? id) Method is called which works fine. The Page shows all data correctly.
The view displays a link for every Property:
<a asp-page-handler="addProperty" asp-route-id="#item.PropertyID">add</a>
This creates the link:
add
This is the correct link (I think). The route, id value and the handler are correct because there is a second OnGet Method in the PageModel:
public async Task<IActionResult> OnGetaddPropertyAsync(int? id)
However, the link only calls OnGetAsync (and not OnGetaddProppertyAsync) every time and, of course, for every Property!
What am I missing?
Model of ItemT:
public class ItemT
{
[Key]
public int ItemTID { get; set; }
[Required]
[StringLength(100, MinimumLength = 1)]
[Display(Name = "ItemT")]
public string Title { get; set; }
public bool isActive { get; set; } = true;
public virtual ICollection<ItemTProperty> ItemTProperties { get; set; }
}
ViewModel of free properties:
public class PropertyIndexViewModel
{
[Key]
public int PropertyID { get; set; }
[Required]
[StringLength(100, MinimumLength = 1)]
public string Title { get; set; }
public bool DefaultsOnly { get; set; }
[Display(Name = "Unit")]
public string Unit { get; set; }
[Display(Name = "Valuetype")]
public string Valuetype { get; set; }
}
The Page to list one ItemT:
#page
#model Inventory.Areas.Inventory.Pages.ItemTs.FreePropertiesToItemTModel
#{
ViewData["Title"] = "FreePropertiesToItemT";
}
<h1>Free Properties</h1>
<div>
<h4>ItemT</h4>
<hr />
<dl class="row">
<dt class="col-sm-2">
#Html.DisplayNameFor(model => model.ItemT.Title)
</dt>
<dd class="col-sm-10">
#Html.DisplayFor(model => model.ItemT.Title)
</dd>
<dt class="col-sm-2">
#Html.DisplayNameFor(model => model.ItemT.isActive)
</dt>
<dd class="col-sm-10">
#Html.DisplayFor(model => model.ItemT.isActive)
</dd>
</dl>
</div>
<div>
<a asp-page="./Edit" asp-route-id="#Model.ItemT.ItemTID">Edit</a> |
<a asp-page="./Index">Back to List</a>
</div>
<p></p>
<div>
#{
ViewData["FreeProperties"] = true;
}
<partial name="../Properties/_Properties.cshtml" model="Model.FreeProperties" />
</div>
The Partial which is loaded:
#using Inventory.DAL.ViewModels
#model IList<PropertyIndexViewModel>
<table class="table">
<thead>
<tr>
<th>
#Html.DisplayNameFor(model => model[0].Title)
</th>
<th>
#Html.DisplayNameFor(model => model[0].DefaultsOnly)
</th>
<th>
#Html.DisplayNameFor(model => model[0].Unit)
</th>
<th>
#Html.DisplayNameFor(model => model[0].Valuetype)
</th>
<th></th>
</tr>
</thead>
<tbody>
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.Title)
</td>
<td>
#Html.DisplayFor(modelItem => item.DefaultsOnly)
</td>
<td>
#Html.DisplayFor(modelItem => item.Unit)
</td>
<td>
#Html.DisplayFor(modelItem => item.Valuetype)
</td>
<td>
#if (ViewBag.FreeProperties != null)
{
<a asp-page-handler="addProperty" asp-route-id="#item.PropertyID">add</a>
}
</td>
</tr>
}
</tbody>
</table>
And the c# code behind the page:
namespace Inventory.Areas.Inventory.Pages.ItemTs
{
public class FreePropertiesToItemTModel : PageModel
{
private readonly IUnitOfWork _uow;
public FreePropertiesToItemTModel(IUnitOfWork uow)
{
_uow = uow;
}
public ItemT ItemT { get; set; }
public IList<PropertyIndexViewModel> FreeProperties { get; set; }
public async Task<IActionResult> OnGetAsync(int? id)
{
if (id == null)
{
return NotFound();
}
ItemT = await _uow.ItemTRepo.getById((int)id);
if (ItemT == null)
{
return NotFound();
}
FreeProperties = await _uow.PropertyRepo.getFreePropertiesForItemT((int)id);
return Page();
}
public async Task<IActionResult> OnGetaddPropertyAsync(int? id)
{
if( id == null)
{
return NotFound();
}
if(ItemT == null) { return NotFound(); }
await _uow.ItemTRepo.addProperty(ItemT.ItemTID, (int)id);
await _uow.Commit();
return Page();
}
}
}

The issue is that your handler name error ,change it like below:
public async Task<IActionResult> OnGetAddPropertyAsync(int? id)
The first letter of handler name must be capitalized , otherwise handler=addProperty in the url is treated as a query-string parameter not a handler name.

Related

ASP.NET Core how to display images from wwwroot/Photos

I have images in wwwroot/photos dir and I want to display the images in a table after adding a new item (animal), along other details from my database.
Here is the Index page, showing my data without images displaying-
All my images are in wwwroot/photos dir.
I've tried to print the value of PhotoUrl on Index View and saw that it get this path -
wwwroot\Photos\Cats\Leon.jpeg
So what is the right way show my images ?
here are the relevent parts of my code:
Storage Service-
public class StorageService : IStorageService
{
readonly IHostingEnvironment _hostingEnvironment;
public StorageService(IHostingEnvironment hostingEnvironment)
{
_hostingEnvironment = hostingEnvironment;
}
public string AbsolutePath => _hostingEnvironment.WebRootPath;
}
Image Service-
public class ImageService : IImageService
{
readonly IStorageService _storageService;
public ImageService(IStorageService storageService)
{
_storageService = storageService;
}
//puts Image folder on wwwroot
public string ImageDir => Path.Combine(_storageService.AbsolutePath, "Photos");
//puts the category name under Image folder
public string CategoryDir(string name) => Path.Combine(ImageDir, name);
public string GetFullImageUrl(Animal animal, IFormFile imageFile)
{
var fileName = $"{imageFile.FileName}";
return Path.Combine(CategoryDir(animal.Category.Name), fileName ?? "");
}
public Task EnsureDirCreated(Category category)
{
Directory.CreateDirectory(CategoryDir(category.Name));
return Task.CompletedTask;
}
public async Task<(bool,string)> UploadImage(IFormFile imageFile, Animal animal)
{
if (imageFile != null && imageFile.Length > 0)
{
var fileName = $"{imageFile.FileName}";
//create file path
var categoryPath = CategoryDir(animal.Category.Name);
await EnsureDirCreated(animal.Category);
string fullPath = Path.Combine(categoryPath, fileName);
using (var fileStream = new FileStream(fullPath, FileMode.Create))
{
await imageFile.CopyToAsync(fileStream);
}
return (true,fullPath);
}
return (false,String.Empty);
}
}
Animal Service-
public async Task<Animal> AddAnimalAsync(Animal animal, IFormFile image)
{
animal.Category = await _categoryService.GetAsync(animal.CategoryId);
var (isSuccess, imageName) = await _imageService.UploadImage(image, animal);
if (isSuccess)
{
animal.PhotoUrl= imageName;
_animalRepository.Add(animal);
return animal;
}
return null;
}
CreaeAnimal ViewModel-
public class CreateAnimalViewModel
{
public Animal Animal { get; set; }
public IFormFile Photo { get; set; }
}
Controllers-
public async Task<IActionResult> Index()
{
var petShopData = _animalService.GetAnimalWithCategoryAsync();
return View(await petShopData);
}
public async Task<IActionResult> CreateAnimal()
{
var categories = await _categoryService.GetAnimalCategory();
ViewBag.Categories = categories.Select(c => new SelectListItem(c.Name, c.CategoryId.ToString())).ToList();
return View();
}
[HttpPost]
public async Task<IActionResult> CreateAnimal([FromForm] CreateAnimalViewModel vm)
{
await _animalService.AddAnimalAsync(vm.Animal, vm.Photo);
return RedirectToAction("Index");
}
Index View-
#model IEnumerable<PetShop.Data.Models.Animal>
#{
ViewBag.Title = "Index";
}
<h1>Index</h1>
<p>
<a asp-action="CreateAnimal">Create New</a>
</p>
<table class="table">
<thead>
<tr>
<th>
#Html.DisplayNameFor(model => model.Name)
</th>
<th>
#Html.DisplayNameFor(model => model.BirthDate)
</th>
<th>
#Html.DisplayNameFor(model => model.Description)
</th>
<th>
#Html.DisplayNameFor(model => model.PhotoUrl)
</th>
<th>
#Html.DisplayNameFor(model => model.Category)
</th>
<th></th>
</tr>
</thead>
<tbody>
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.Name)
</td>
<td>
#Html.DisplayFor(modelItem => item.BirthDate)
</td>
<td>
#Html.DisplayFor(modelItem => item.Description)
</td>
<td>
#*#Html.DisplayFor(modelItem => item.PhotoUrl)*#
<img src="#item.PhotoUrl" alt="..." style="width:18rem"/>
</td>
<td>
#Html.DisplayFor(modelItem => item.Category.Name)
</td>
<td>
<a asp-action="Edit" asp-route-id="#item.AnimalId">Edit</a> |
<a asp-action="Details" asp-route-id="#item.AnimalId">Details</a> |
<a asp-action="Delete" asp-route-id="#item.AnimalId">Delete</a>
</td>
</tr>
}
</tbody>
</table>
add the codes in your startup class:
app.UseStaticFiles();
if you added the codes,you could try to view your pic and check if it exist as below:

Get data from Ajax in ASP.NET Core not working?

I'm new at ASP.NET Core, and this is how I get data from Ajax.
I have a View Model like this
public class RequestSearchDefaultModel
{
public string FromToDate { get; set; }
public int Status { get; set; }
public List<SelectListItem> StatusType { get; set; }
public DateTime FromDate { get; set; }
public DateTime ToDate { get; set; }
public IEnumerable<RequestViewModel> requestViewModel { get; set; }
}
When I search the form, I use Ajax to get the data
function OpenSearch() {
var stt = $('#StatusDropDown').val();
var daterange = $('#daterange').val();
var url = "#Url.Action("Search","RequestApproval")";
var model = { FromToDate: daterange, Status: stt };
$.ajax({
type: "POST",
data: JSON.stringify(model),
url: url,
contentType: "application/json",
}).done(function (res) {
console.log("here");
$('#myTable').html(res);
})
}
This is controller
[HttpPost]
public async Task<IActionResult> Search([FromBody] RequestSearchDefaultModel m)
{
m.FromDate = DateTime.Parse(m.FromToDate.Substring(0, 10)).Add(new TimeSpan(00, 0, 0));
m.ToDate = DateTime.Parse(m.FromToDate.Substring(14)).Add(new TimeSpan(23, 59, 59));
var request = _headerService.Search();
m.requestViewModel = _mapper.Map<IEnumerable<RequestViewModel>>(request);
return PartialView("_RequestBody", m);
}
This is the view
<div class="card-body">
<div class="table-responsive" id="myTable">
#Html.Partial("_RequestBody")
</div>
</div>
And this is the partial view
#model RequestSearchDefaultModel
<table class="table table-striped">
<thead>
<tr>
<th>Seq.</th>
<th>Title</th>
<th>Status</th>
<th>Draft Date</th>
<th>Final Approval Date</th>
</tr>
</thead>
<tbody id="RequestBody">
#if (Model.requestViewModel == null)
{
}
else
{
foreach (var item in Model.requestViewModel)
{
<tr>
<td>#item.ID</td>
<td>#item.DocumentNo</td>
<td>
<a asp-action="Edit" asp-route-id="#item.ID">#item.Title</a>
</td>
<td>#item.DraftDate</td>
</tr>
}
}
</tbody>
</table>
But I can't get the data, I don't know where I wrong, so please help. Thanks in advance.

How to get related data with many-to-many relationship table?

I have three tables. The first one is Movie, the second one Category, and the third one MovieCategory. I listed movies, but I want to choose a category and then list the movies for each category.
How do I make the controller? I've included my business objects, view, and current controller below.
Movie Entity
public class Movie : IEntity
{
public int Id { get; set; }
public string Name { get; set; }
public string Summary { get; set; }
public string Director { get; set; }
public string Banner { get; set; }
public ICollection<MoviesCategory> MoviesCategory { get; set; }
}
Category Entity
public class Category : IEntity
{
public int Id { get; set; }
public string CategoryName { get; set; }
public ICollection<MoviesCategory> MoviesCategory { get; set; }
}
MovieCategory Entity
public class MoviesCategory : IEntity
{
public int Id { get; set; }
public int MovieId { get; set; }
public int CategoryId { get; set; }
public Category Category { get; set; }
public Movie Movie { get; set; }
}
Controller
public IActionResult List()
{
var movies = _movieService.GetAll();
MovieListViewModel movieListViewModel = new MovieListViewModel()
{
Movies = movies
};
return View(movieListViewModel);
}
View
#foreach (var item in Model.Movies)
{
<tr>
#Html.HiddenFor(modelItem => item.Id)
<td>
#Html.DisplayFor(modelItem => item.Id)
</td>
<td>
#Html.DisplayFor(modelItem => item.Name)
</td>
<td>
#if (!string.IsNullOrEmpty(item.Summary) && item.Summary.Length > 35)
{
<p>#(item.Summary.Substring(0,35))</p>
}
</td>
<td>
#Html.DisplayFor(modelItem => item.Director)
</td>
<td>
<img src="~/images/#item.Banner" width="105" height="140" class="img-thumbnail" />
</td>
<td>
<i title="Edit" class="fas fa-edit" style="color:coral"></i>
<i title="Detail" class="fas fa-info-circle" style="color:cornflowerblue"></i>
<td>
<tr>
}
In order to get a Category and its related Movies do the following:
Create a view model that holds the data for Category and Movies in order to have a strongly typed view:
public class MovieData
{
public Category Category { get; set; }
public IEnumerable<Movies> Movies { get; set; }
}
Then in your controller you query the database like this:
public async Task<IActionResult> MovieDataAction(int catId)
{
var data = await context
.Categories
.Where(c => c.Id == catId) //Category Id
.Include(c => c.MoviesCategory)
.ThenInclude(p => p.Movie)
.Select(cat => new { Category = cat, Movies = cat.MoviesCategory.Select(w => w.Movie) })
.FirstOrDefaultAsync();
var vData = new MovieData(){
Category = data.Category,
Movies = data.Movies
};
return View(vData);
}
I made a demo based on your description like below:
View:
#model MovieListViewModel
#{
ViewData["Title"] = "List";
}
<h1>List</h1>
#Html.DropDownListFor(modelItem => modelItem.CategoryId, Model.Categories, "Select Category",
new { #class = "form-control" })
<table>
#foreach (var item in Model.Movies)
{
<tr>
#Html.HiddenFor(modelItem => item.Id)
<td>
#Html.DisplayFor(modelItem => item.Id)
</td>
<td>
#Html.DisplayFor(modelItem => item.Name)
</td>
<td>
#if (!string.IsNullOrEmpty(item.Summary) && item.Summary.Length > 35)
{
<p>#(item.Summary.Substring(0,35))</p>
}
</td>
<td>
#Html.DisplayFor(modelItem => item.Director)
</td>
<td>
<i title="Edit" class="fas fa-edit" style="color:coral"></i>
<i title="Detail" class="fas fa-info-circle" style="color:cornflowerblue"></i>
<td>
</tr>
}
</table>
#section scripts{
<script>
$("#CategoryId").on("change", function () {
var id = $(this).val();
window.location.href = "/Movie/List?id=" + id;
})
</script>
}
Controller:
public IActionResult List(int? id)
{
var movies = _context.Movies.ToList();
var categories = _context.Categories.ToList();
if(id != null)
{
movies = _context.MoviesCategories.Where(c => c.CategoryId == id).Select(m => m.Movie).ToList();
}
MovieListViewModel movieListViewModel = new MovieListViewModel()
{
CategoryId = id ?? 0,
Categories = new List<SelectListItem>(),
Movies = movies
};
foreach(var category in categories)
{
movieListViewModel.Categories.Add(new SelectListItem { Text = category.CategoryName, Value = category.Id.ToString() });
}
return View(movieListViewModel);
}
Result:

error while display role name in a view

i , Actually want to display a role name in place of role id
so i'm doing this
these are my model's
[Table("User")]
public class User
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int UserId { get; set; }
[Required(ErrorMessage = "Please Provide Fullname", AllowEmptyStrings = false)]
[Display(Name = "Full Name")]
public string Full_Name { get; set; }
[Required(ErrorMessage = "Please Provide Username", AllowEmptyStrings = false)]
public string Username { get; set; }
[Required(ErrorMessage = "Please provide password", AllowEmptyStrings = false)]
[DataType(System.ComponentModel.DataAnnotations.DataType.Password)]
public string Password { get; set; }
[Required(ErrorMessage = "Please Select User type", AllowEmptyStrings = false)]
[Display(Name = "USER TYPE")]
public int ROLEID { get; set; }
[Required(ErrorMessage = "Please Select Login Status", AllowEmptyStrings = false)]
[Display(Name = "Login Status")]
public string Login_Status { get; set; }
public List<Role> role { get; set; }
UserDB db = new UserDB();
public Role getname(int id) {
return db.roles.SingleOrDefault(d => d.ROLEID == id);
}
}
[Table("ROLE")]
public class Role
{
public int ROLEID { get; set; }
public string ROLENAME { get; set; }
}
public class UserRole
{
public List<User> Ruser { get; set; }
public List<Role> Rrole { get; set; }
}
this is my controller
public ActionResult Userlist(int? page)
{
UserDB dc = new UserDB();
List<User> users = dc.users.ToList();
UserRole userrole = new UserRole();
userrole.Ruser = users;
return View(userrole);
}
this is my view
#model PHARMACY.Models.UserRole
<table class="table table-striped table-hover ">
<tr>
<th>
#Html.DisplayNameFor(model => model.Ruser.First().Full_Name)
</th>
<th>
#Html.DisplayNameFor(model => model.Ruser.First().Username)
</th>
<th>
#Html.DisplayNameFor(model => model.Ruser.First().Password)
</th>
<th>
#Html.DisplayNameFor(model => model.Rrole.First().ROLENAME)
</th>
<th>
#Html.DisplayNameFor(model => model.Ruser.First().Login_Status)
</th>
<th></th>
</tr>
#foreach (var item in Model.Ruser) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.Full_Name)
</td>
<td>
#Html.DisplayFor(modelItem => item.Username)
</td>
<td>
#Html.DisplayFor(modelItem => item.Password)
</td>
<td>
#{
var name = Model.Rrole.Where(i => i.ROLEID == item.ROLEID);
#Html.DisplayFor(m => m.Rrole.Where(i => i.ROLEID == item.ROLEID).FirstOrDefault());
}
</td>
<td>
#Html.DisplayFor(modelItem => item.Login_Status)
</td>
<td>
<p class="label label-info">#Html.ActionLink("Edit", "EditUser", new { id=item.UserId,})</p> |
<p class="label label-danger">#Html.ActionLink("Delete", "DeleteUser", new { id=item.UserId })</p>
</td>
</tr>
}
</table>
But it through an error that "Value cannot be null.
Parameter name: source"
If you have no users in your database, this line will cause the error:
#Html.DisplayNameFor(model => model.Ruser.First().Full_Name)
You are not handling the case where model.Ruser is empty.

For each student make comma-separated list of all courses for which he enlisted

Could somebody help me for each student make comma-separated list of all courses for which he enlisted?
I need to modify a Contoso University application with a comma-separated list.
Student Controller:
public ActionResult Index(string studentEnrollment, string sortOrder, string currentFilter, string searchString, int? page)
{
ViewBag.CurrentSort = sortOrder;
ViewBag.NameSortParm = String.IsNullOrEmpty(sortOrder) ? "name_desc" : "";
ViewBag.DateSortParm = sortOrder == "Date" ? "date_desc" : "Date";
if (searchString != null)
{
page = 1;
}
else
{
searchString = currentFilter;
}
ViewBag.CurrentFilter = searchString;
var students = studentService.GetAll();
var studentViewModel = Mapper.Map<IEnumerable<Student>, IEnumerable<StudentViewModel>>(students);
if (!String.IsNullOrEmpty(searchString))
{
students = students.Where(s => s.LastName.ToUpper().Contains(searchString.ToUpper())
|| s.FirstMidName.ToUpper().Contains(searchString.ToUpper()));
}
switch (sortOrder)
{
case "name_desc":
students = students.OrderByDescending(s => s.LastName);
break;
case "Date":
students = students.OrderBy(s => s.EnrollmentDate);
break;
case "date_desc":
students = students.OrderByDescending(s => s.EnrollmentDate);
break;
default:
students = students.OrderBy(s => s.LastName);
break;
}
int pageSize = 3;
int pageNumber = (page ?? 1);
return View(students.ToPagedList(pageNumber, pageSize));
}
Course Controller:
public ActionResult Index(int? page)
{
int pageNumber = page ?? 1;
var courses = courseService.GetAll();
var coursesViewList = Mapper.Map<IEnumerable<Course>, IEnumerable<CourseViewModel>>(courses);
var model = coursesViewList.ToPagedList(pageNumber, PageSize);
return View(model);
}
Student Model:
public int ID { get; set; }
[Display(Name = "Last Name")]
public string LastName { get; set; }
[Display(Name = "First Name")]
public string FirstMidName { get; set; }
[Display(Name = "Enrollment Date")]
[DisplayFormat(DataFormatString = "{0:g}", ApplyFormatInEditMode = true)]
public DateTime EnrollmentDate { get; set; }
[Display(Name = "Date Of Birth")]
[DisplayFormat(DataFormatString = "{0:dd/MM/yyyy}", ApplyFormatInEditMode = true)]
public DateTime DateOfBirth { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
public List<Comment> Comments { get; set; }
Course Model:
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int CourseID { get; set; }
[Required]
public string Title { get; set; }
public int Credits { get; set; }
public int StudentId { get; set; }
public virtual Student Student { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
View:
#model PagedList.IPagedList<ContosoUniversity.Models.Student>
#using PagedList.Mvc;
<link href="~/Content/PagedList.css" rel="stylesheet" type="text/css" />
#{
ViewBag.Title = "Students";
}
<h2>Students</h2>
<p>
#Html.ActionLink("Create New", "Create")
</p>
#using (Html.BeginForm("Index", "Student", FormMethod.Get))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<p>
Find by name: #Html.TextBox("SearchString", ViewBag.CurrentFilter as string)
<input type="submit" value="Filter" />
</p>
}
<table class="table">
<tr>
<th>
#Html.ActionLink("Last Name", "Index", new { sortOrder = ViewBag.NameSortParm })
</th>
<th>
First Name
</th>
<th>
#Html.ActionLink("Enrollment Date", "Index", new { sortOrder = ViewBag.DateSortParm })
</th>
<th>
Date Of Birth
</th>
<th>
Courses
</th>
<th></th>
</tr>
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.LastName)
</td>
<td>
#Html.DisplayFor(modelItem => item.FirstMidName)
</td>
<td>
#Html.DisplayFor(modelItem => item.EnrollmentDate)
</td>
<td>
#Html.DisplayFor(modelItem => item.DateOfBirth)
</td>
<td>
</td>
<td>
#Html.ActionLink("Edit", "Edit", new { id=item.ID }) |
#Html.ActionLink("Details", "Details", new { id=item.ID }) |
#Html.ActionLink("Delete", "Delete", new { id=item.ID })
</td>
</tr>
}
</table>
<br />
Page #(Model.PageCount < Model.PageNumber ? 0 : Model.PageNumber) of #Model.PageCount
#Html.PagedListPager(Model, page => Url.Action("Index",
new { page, sortOrder = ViewBag.CurrentSort, currentFilter = ViewBag.CurrentFilter }))
Please tell me somebody the easiest way to do it!
Without knowing the model for the Enrollment class, assuming it has a Course property which returns a Course object, you can just do this in your view (where item is the Student object in your foreach loop):
#string.Join(",", item.Enrollments.Select(e => e.Course.Title))