NomenNescio
NomenNescio

Reputation: 3030

read implicit return type in Razor MVC View

I'm kind of new to razor MVC, and I'm wondering how can I read the values I return in the view?

My code is like this:

    public ActionResult Subject(int Category)
    {
        var db = new KnowledgeDBEntities();
        var category = db.categories.Single(c => c.category_id == Category).name;
        var items = from i in db.category_items
                    where i.category_id == Category
                    select new { ID = i.category_id, Name = i.name };
        var entries = from e in db.item_entry
                      where items.Any(item => item.ID == e.category_item_id)
                      select new { ID = e.category_item_id, e.title };
        db.Dispose();


        var model = new { Name = category, Items = items, Entries = entries };
        return View(model);
    }

Basically, I return an anonymous type, what code do I have to write to read the values of the anonymous type in my view?

And if this is not possible, what would be the appropriate alternative?

Upvotes: 1

Views: 464

Answers (1)

Darin Dimitrov
Darin Dimitrov

Reputation: 1039278

Basically, I return an anonymous type

Nope. Ain't gonna work. Anonymous types are emitted as internal by the compiler and since ASP.NET compiles your views into separate assemblies at runtime they cannot access those anonymous types which live in the assembly that has defined them.

In a properly designed ASP.NET MVC application you work with view models. So you start by defining some:

public class MyViewModel
{
    public string CategoryName { get; set; }
    public IEnumerable<ItemViewModel> Items { get; set; }
    public IEnumerable<EntryViewModel> Entries { get; set; }
}

public class ItemViewModel
{
    public int ID { get; set; }
    public string Name { get; set; }
}

public class EntryViewModel
{
    public int ID { get; set; }
    public string Title { get; set; }
}

and then you adapt your controller action to pass this view model to the view:

public ActionResult Subject(int Category)
{
    using (var db = new KnowledgeDBEntities())
    {

        var category = db.categories.Single(c => c.category_id == Category).name;
        var items = 
            from i in db.category_items
            where i.category_id == Category
            select new ItemViewModel 
            { 
                ID = i.category_id, 
                Name = i.name 
            };

        var entries = 
            from e in db.item_entry
            where items.Any(item => item.ID == e.category_item_id)
            select new EntryViewModel 
            { 
                ID = e.category_item_id, 
                Title = e.title 
            };

        var model = new MyViewModel 
        { 
            CategoryName = category, 
            Items = items.ToList(),      // be eager
            Entries = entries.ToList()   // be eager
        };
        return View(model);
    }
}

and finally you strongly type your view to the view model you have defined:

@model MyViewModel

@Model.Name

<h2>Items:</h2>
@foreach (var item in Model.Items)
{
    <div>@item.Name</div>
}

<h2>Entries:</h2>
@foreach (var entry in Model.Entries)
{
    <div>@entry.Title</div>
}

By the way to ease the mapping between your domain models and view models I would recommend you checking out AutoMapper.

Oh, and since writing foreach loops in a view is kinda ugly and not reusable I would recommend you using display/editor templates which would basically make you view look like this:

@model MyViewModel

@Model.Name

<h2>Items:</h2>
@Html.DisplayFor(x => x.Items)

<h2>Entries:</h2>
@Html.DisplayFor(x => x.Entries)

and then you would define the respective display templates which will be automatically rendered for each element of the respective collections:

~/Views/Shared/DisplayTemplates/ItemViewModel:

@model ItemViewModel
<div>@item.Name</div>

and ~/Views/Shared/DisplayTemplates/EntryViewModel:

@model EntryViewModel
<div>@item.Title</div>

Upvotes: 2

Related Questions