ViewModel to handle multiple data models - c#

I am batting 1000 here with my questions. So i will try to be as descriptive as possible.
I have multiple views in a layout that come from different models.
When a record is selected from a list it opens this layout. At the top of the layout it displays the record information in a table format. This is a simple ID - /AuditSchedule/1122. This is currently the Body. This works.
In another area of the layout i have a list of action links (side menu) that are generated from a another table. The links, I think should be as follows but not sure /AuditSchedule/1122/1. This was accomplished by using the Global.asax with routes.
Naturally when you open this layout you should get all the above plus the first record of the next area of the layout which is the form. In this form i need it to show a question from a table of questions and have a set of check boxes created with, what i will call scores, to the right of the question. These scores are also in a table called scores. Everything that i have in this is pretty much in a data table so that everything can be edited and changed if needed.
When a user submits the form it will store in another table called MainAnswers the id's of the auditSchedule, mainQuestion, and a string of the score. This table is a blank table so it would insert a new record for every Main Question for that AuditSchedule.
So far i have had no luck with help on this. If anyone could point me to an example of this that they have seen. It would be great. I cannot be the only one that has tried to do this. However i am new to MVC C#. If this were Zend and PHP i would have no issues.
I have used the code first approach. All of my relationships are done. The issue lies in implementing the view and saving the information in the right tables.
Anyone that can help would be much appreciated. I am struggling here.
Updated 08/16/2012 3:12pm
Let me take baby steps first.
I want to be able to select a menu Item from the side and have a list of questions come up from that section. Here is my Code:
#{ Layout = null; }
#model QQAForm.ViewModels.AuditFormEdit
<table width="698" border="2" cellpadding="2">
<tr>
<td align="center"><b>Section</b><br />1.0</td>
<td>
<br />(Title of Section Goes Here - SubcategoryName - Located in Subcategory Model)<br />
<br />
(Child Questions Go here - QuestionText - Located in ChildQuestion Model)
</td>
<td>
(This should be the result of what is written in AuditFormEdit view model - it does not currently work - Nothing shows up)
#for (int index = 0; index < Model.ScoreCardCheckBoxHelperList.Count; index++)
{
#Html.CheckBoxFor(m => m.ScoreCardCheckBoxHelperList[index].Checked)
#Html.LabelFor(m => m.ScoreCardCheckBoxHelperList[index], Model.ScoreCardCheckBoxHelperList[index].ScoreName)
#Html.HiddenFor(m => m.ScoreCardCheckBoxHelperList[index].ScoreID)
#Html.HiddenFor(m => m.ScoreCardCheckBoxHelperList[index].ScoreName)
}
</td>
</tr>
</table>
Here is the View model i am working on:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using QQAForm.Models;
namespace QQAForm.ViewModels
{
public class AuditFormEdit
{
public List<SubcategoryHelper> SubcategoryHelperGet { get; set; }
public class SubcategoryHelper : Models.SubCategory
{
public SubcategoryHelper(Models.SubCategory subCat)
{
this.SubCategoryID = subCat.SubCategoryID;
this.SubcategoryName = subCat.SubcategoryName;
}
}
public Models.MainAnswer ScoreInstance { get; set; }
public List<ScoreCardCheckBoxHelper> ScoreCardCheckBoxHelperList { get; set; }
public void InitializeScoreCheckBoxHelperList(List<Models.Score> ScoreList)
{
if (this.ScoreCardCheckBoxHelperList == null)
this.ScoreCardCheckBoxHelperList = new List<ScoreCardCheckBoxHelper>();
if (ScoreList != null
&& this.ScoreInstance != null)
{
this.ScoreCardCheckBoxHelperList.Clear();
ScoreCardCheckBoxHelper scoreCardCheckBoxHelper;
string scoreTypes =
string.IsNullOrEmpty(this.ScoreInstance.Score) ?
string.Empty : this.ScoreInstance.Score;
foreach (Models.Score scoreType in ScoreList)
{
scoreCardCheckBoxHelper = new ScoreCardCheckBoxHelper(scoreType);
if (scoreTypes.Contains(scoreType.ScoreName))
scoreCardCheckBoxHelper.Checked = true;
this.ScoreCardCheckBoxHelperList.Add(scoreCardCheckBoxHelper);
}
}
}
public void PopulateCheckBoxsToScores()
{
this.ScoreInstance.Score = string.Empty;
var scoreType = this.ScoreCardCheckBoxHelperList.Where(x => x.Checked)
.Select<ScoreCardCheckBoxHelper, string>(x => x.ScoreName)
.AsEnumerable();
this.ScoreInstance.Score = string.Join(", ", scoreType);
}
public class ScoreCardCheckBoxHelper : Models.Score
{
public bool Checked { get; set; }
public ScoreCardCheckBoxHelper() : base() { }
public ScoreCardCheckBoxHelper(Models.Score score)
{
this.ScoreID = score.ScoreID;
this.ScoreName = score.ScoreName;
}
}
}
}
Here is the controller parts:
//get
public ActionResult _Forms(int section)
{
AuditFormEdit viewModel = new AuditFormEdit();
//viewModel.ScoreInstance = _db.MainAnswers.Single(r => r.MainAnswerID == id);
_db.SubCategories.Single(r => r.SubCategoryID == section);
viewModel.InitializeScoreCheckBoxHelperList(_db.Scores.ToList());
return View(viewModel);
}
//post
[HttpPost]
public ActionResult _Forms(AuditFormEdit viewModel)
{
if (ModelState.IsValid)
{
viewModel.PopulateCheckBoxsToScores();
_db.Entry(viewModel.ScoreInstance).State = System.Data.EntityState.Modified;
_db.SaveChanges();
return RedirectToAction("/");
}
else
{
return View(viewModel);
}
}
So if you look at the Layout, where it show the spot for _Forms the Section should change with the link /AuditSchedule/1132/1 - it does not. As well as my check boxes which currently do not show up either.

...my check boxes which currently do not show up either.
That's because the line which sets the viewModel.ScoreInstance in your controller's GET action is commented out:
//viewModel.ScoreInstance = _db.MainAnswers.Single(r => r.MainAnswerID == id);
Hence viewModel.ScoreInstance is null and in InitializeScoreCheckBoxHelperList you fill the ScoreCardCheckBoxHelperList only when viewModel.ScoreInstance is not null:
if (this.ScoreCardCheckBoxHelperList == null)
this.ScoreCardCheckBoxHelperList = new List<ScoreCardCheckBoxHelper>();
if (ScoreList != null
&& this.ScoreInstance != null)
{
//... add elements to ScoreCardCheckBoxHelperList
}
Empty ScoreCardCheckBoxHelperList = no checkboxes.

Related

Null Exception thrown in View

Using ASP.NET MVC, .NET Framework 4.5.2, Entity Data Model for SQL DB, Visual Studio 2017.
I have a class generated from the ADO.NET(EF Designer from Database) :
BookInfo.cs
namespace LibraryMS
{
using System;
using System.Collections.Generic;
public partial class BookInfo
{
public string BookID { get; set; }
public string Title { get; set; }
public string Author { get; set; }
public string Publisher { get; set; }
public string PublishDate { get; set; }
public string Edition { get; set; }
public virtual Inventory Inventory { get; set; }
}
}
The database is designed where the "BookID" in the BookInfo table has a foreign key "BookID" in the Inventory table.
In a view to update an inventory's properties referenced by "BookID", I then proceed to query the list and update the correct instance.
Screenshot of update inventory page:
When landing on page to enter info the [HttpGet] UpdateInventory() is called, when clicking "Create" button as seen above, the [HttpPost] UpdateInventory(...) is called.
Logic/Code in Controller:
[HttpGet]
public ActionResult UpdateInventory()
{
return View();
}
[HttpPost]
public async Task<ActionResult> UpdateInventory(string bookID, string ttlIn, string lowin, string outnow)
{
var bf = await SqlRestApiHelper.searchFromBooks(bookID);
bf.Inventory.TotalIn = Convert.ToInt16(ttlIn);
bf.Inventory.LowIn = Convert.ToInt16(lowin);
bf.Inventory.Out = Convert.ToInt16(outnow);
await SqlRestApiHelper.UpdateBookInfoInventory(bf.Inventory);
await SqlRestApiHelper.SaveChanges();
return View("All");
}
[HttpGet]
public async Task<ActionResult> All()
{
return View(await SqlRestApiHelper.getAllBooksInfo(0, 10));
}
SqlRestApiHelper.cs
namespace LibraryMS
{
public static class SqlRestApiHelper
{
private static libraryDBEntities entities = new libraryDBEntities();
public static async Task<LibraryMS.BookInfo> searchFromBooks(string id)
{
return entities.BookInfoes.ToList().Find(book => book.BookID == id);
}
public static async Task UpdateBookInfoInventory(LibraryMS.Inventory inv)
{
var newInv = inv;
var el = entities.BookInfoes.ToList().Find(x => x.Inventory.BookID == newInv.BookID);
if (el != null)
{
el.Inventory.TotalIn = newInv.TotalIn;
el.Inventory.LowIn = newInv.LowIn;
el.Inventory.Out = newInv.Out;
// the above updates the list item referenced
}
}
public static async Task SaveChanges()
{
await entities.SaveChangesAsync();
}
public static async Task<IPagedList<BookInfo>> getAllBooksInfo(int page, int itemsPerPage)
{
List<BookInfo> bookinfo = new List<BookInfo>();
bookinfo = (from o in entities.BookInfoes
orderby o.Title descending //use orderby, otherwise Skip will throw an error
select o)
.Skip(itemsPerPage * page).Take(itemsPerPage)
.ToList();
int totalCount = bookinfo.Count();//return the number of pages
IPagedList<BookInfo> pagebooks = new StaticPagedList<BookInfo>(bookinfo, page + 1,10,totalCount);
return pagebooks;//the query is now already executed, it is a subset of all the orders.
}
The Null Exception Thrown:
Code for all.cshtml view page:
#model PagedList.IPagedList<LibraryMS.BookInfo>
#using PagedList.Mvc;
#{
ViewBag.Title = "All";
}
<h2>all</h2>
<table class="table">
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.Title)
</td>
<td>
#Html.DisplayFor(modelItem => item.Author)
</td>
<td>
#Html.DisplayFor(modelItem => item.Publisher)
</td>
<td>
#Html.DisplayFor(modelItem => item.PublishDate)
</td>
<td>
#Html.DisplayFor(modelItem => item.Edition)
</td>
<td>
#Html.ActionLink("Details","Details",new { item.BookID})
</td>
</tr>
}
</table>
#Html.PagedListPager(Model, page => Url.Action("All","BookInfoController", new { page }))
your view is throwing error because you are returning the view without passing the model, you are using return View("All") without passing the model
the right way is by passing the model with the view, you can do it this way
View("ViewName", ModelData);
in your case
return View("All", await SqlRestApiHelper.getAllBooksInfo(0, 10));
for the saving part I am not sure why, but i can see few errors,
first what if book does not have inventory info ?
it will throw null error, so first check if null, create new inventory, if not update accordingly
here is how i would do it
public static async Task UpdateBookInfoInventory(Inventory inv)
{
var newInv = inv;
// get book info
var el = entities.BookInfoes.FirstOrDefault(x => x.BookID == inv.BookID);
if (el != null)
{
if(el.Inventory != null)
{
// update accordingly
el.Inventory.TotalIn = newInv.TotalIn;
el.Inventory.LowIn = newInv.LowIn;
el.Inventory.Out = newInv.Out;
// the above updates the list item referenced
}
else
{
/// add new if null
el.inventory = newInv;
}
await SqlRestApiHelper.SaveChanges();
}
As a first step , put the breakpoint in the getAllBooksInfo method and see whether the list count is coming or not in visual studio. This will help you a lot.
And As an alternative step, you can also solve this error by using the ToPagedList(pageIndex, pageSize); method, i have used it personally and it was worked well
ToPagedList example as per your code:
**public static async Task<IPagedList<BookInfo>> getAllBooksInfo(int page, int itemsPerPage)
{
List<BookInfo> bookinfo = new List<BookInfo>();
bookinfo = (from o in entities.BookInfoes
orderby o.Title descending //use orderby, otherwise Skip will throw an error
select o)
.Skip(itemsPerPage * page).Take(itemsPerPage)
.ToList();
int totalCount = bookinfo.Count();//return the number of pages
//changes made to the below line
IPagedList<BookInfo> pagebooks = bookinfo.ToPagedList(1, 10);
return pagebooks;//the query is now already executed, it is a subset of all the orders.
}**
official source:https://github.com/troygoode/PagedList
Note: Even with this approach, kindly check whether you are getting the data or not from the database by using the breakpoint in the changed line.
Hope this will surely solve your problem kindly let me know thoughts or feedbacks
Thanks
karthik

Calling httppost actionresult from inside a view using a button

I have a project to make an online shop between users (post a product, buy, etc.) using a database. In this project I have a view called "ShoppingCart":
#model IEnumerable<MyFirstProject.Models.Product>
#{
ViewBag.Title = "ShoppingCart";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>Your Shopping Cart</h2>
#if (Model == null)
{
<div style="float:left">Your cart is empty.</div>
<div>
Total payment: 0
</div>
}
else
{
decimal tPrice = 0;
<div>
<table style="float:left">
#foreach (var product in Model)
{
tPrice = tPrice + product.Price;
{ Html.RenderPartial("ProductLine", product);}
}
</table>
</div>
<div>
Total payment: #tPrice
</div>
}
It receives a list of products which the user decided to buy and displays them (not the important part). I need to add a button which will send the list to an action result in the "ShoppingController":
[HttpPost]
public ActionResult ShoppingCart(List<Product> bought)
{
if (ModelState.IsValid)
{
foreach (var listP in bought.ToList())
{
foreach (var databaseP in db.Products.ToList())
{
if (listP.ProductID == databaseP.ProductID)
{
databaseP.State = 1;
db.SaveChanges();
break;
}
}
}
return RedirectToAction("Index");
}
else
{
return View(bought);
}
}
"State" indicates if the product was bought or not (0=not bought, 1=bought), db is the database
If you wan't to post any data from a view to an action method, you should keep that data in form elements and keep that in a form. Since you want to post a collection of items, You may use Editor Templates.
Let's start by creating a view model.
public class ShoppingCartViewModel
{
public decimal TotalPrice { set; get; }
public List<Product> CartItems { set; get; }
}
public class Product
{
public int Id { set; get; }
public string Name { set; get; }
}
Now in your GET action, you will create an object of the ShoppingCartViewModel, load the CartItems property and send to the view.
public ActionResult Index()
{
var cart = new ShoppingCartViewModel
{
CartItems = new List<Product>
{
new Product { Id = 1, Name = "Iphone" },
new Product { Id = 3, Name = "MacBookPro" }
},
TotalPrice = 3234.95
};
return View(cart);
}
Now i will create an EditorTemplate. To do that, Go to your ~/Views/YourControllerName folder, and Create a directory called EditorTemplates and add a view with name Product.cshtml
The name of the file should match with the name of the type.
Open this new view and add the below code.
#model YourNamespace.Product
<div>
<h4>#Model.Name</h4>
#Html.HiddenFor(s=>s.Id)
</div>
You can keep the display however you want. But the important thing is, We need to keep a form field for the productId. We are keeping that in a hidden field here.
Now let's go back to our main view. We need to make this view strongly typed to our ShoppingCartViewModel. We will use the EditorFor html helper method in this view to call our editor template
#model ReplaceYourNamespaceHere.ShoppingCartViewModel
#using (Html.BeginForm())
{
#Html.EditorFor(x => x.CartItems)
<p>Total : #Model.TotalPrice</p>
<input type="submit" />
}
And in your HttpPost action method, We will have a paramer of type ShoppingCartViewModel. When the form is submitted, MVC Model binder will map the posted form values to an object of ShoppingCartViewModel.
[HttpPost]
public ActionResult Index(ShoppingCartViewModel model)
{
foreach (var item in model.CartItems)
{
var productId = item.Id;
// to do : Use productId and do something
}
return RedirectToAction("OrderSucessful");
}
You can iterate through the CartItems collection and get the Id of the Products and do whatever you want.
If you wan't to allow the user to edit the items (using a check box) in this page, Take a look at this answer. It is basically same, but you add a boolean property to Product class and use that for rendering a checkbox.

How to add #Html.ValidationMessageFor for each item in a collection?

How would you add #Html.ValidationMessageFor() for each item in a collection? Say,
public class FooVm
{
// some property
public ICollection<BarVm> Bars { get; set; }
}
public class BarVm
{
// some property
[Range(1, int.Max, ErrorMessage = "Must be greater than 1")
public float? Fox { get; set; }
}
Then in a view
#model namespace.here.FooVm
<div class="container"></div>
Populate
<script>
$(function() {
var i = 0;
var populate = function() {
var strBuilder = '<input type="text" name="Bars[i].Fox" />';
$(".container").append(strBuilder);
return false;
};
$(".trigger").click(populate);
});
</script>
It's all working. But how can I add the validation in every textbox? I'm using ASP.NET MVC 4 still practicing. I'm also utilizing unobtrusive validation for client validation. Any you-should-do-something-like-this suggestions or tips, sample code would be great. Thanks.
Actually, using Javascript to populate a View is not the way MVC should be used. Instead, you can render all textboxes like this:
First the code for the class:
public class FooVm
{
// some property
public List<BarVm> Bars { get; set; }
public FooVm()
{
// Make sure the collection exists to prevent NullReferenceException
this.Bars = new List<BarVm>();
}
}
public class BarVm
{
// some property
[Range( 1, Int32.MaxValue, ErrorMessage = "Must be greater than 1" )]
public float? Fox { get; set; }
}
Now the code for the View:
#model WebApplication2.Models.FooVm
<h2>Sample View</h2>
#using ( Html.BeginForm( "YourAction", "YourController" ) )
{
<div class="container">
#for ( int i = 0; i < Model.Bars.Count; i++ )
{
#Html.TextBoxFor( m => m.Bars[i].Fox )
#Html.ValidationMessageFor( m => m.Bars[i].Fox );
}
</div>
}
This will render the necessary tags - and of course the validationmessage-bits. However, it's also possible to combine all error messages in one place by using
#Html.ValidationSummary()
If you really want to display the stuff only after clicking a button, consider using a partial view and loading that one. That's a much better approach than trying to create all necessary tags and attributes for validation using javascript.
Regards,
Frank

Is there some proper way to add model error for IEnumerable model field to highlight specific input in view but not all of them?

Well I have some view model for, let's say, product:
public class ProductsAddModel
{
public IEnumerable<string> Names {get;set;}
}
I need to add several product names at a time but if there already exists product with one of the inputed names i want to add model error for Names and highlight the proper input in view. It basically looks like:
[HttpPost]
public ActionResult(ProductsAddModel model)
{
if(ModelState.IsValid)
{
var existedProducts = productRepository.AllProducts;
if(model.Names.Any(n => existedProducts.Select(p => p.Name).Contains(n)))
{
ModelState.AddModelError("Names", "Error");
return View(model);
}
}
}
But in this case all Names inputs will be highlighted in view. How can I highlight specific name input - input which causes error. I have several solutions such ass past error Name with view data to view add add error class to text input if it's value matches error name. Something like:
var match = existedProducts.Select(p => p.Name).FirstOrDefault(n => model.Names.Contains(n));
if(match != null)
{
ModelState.AddModelError("Names", "Error");
ViewBag.ErrorName = match.Name;
return View(model);
}
And in view:
for (int i; i<Model.NumberOfInputs; i++)
{
var value = Model.Names != null && Model.Names.Count() < index ? Model.Names.ToArray()[index] : string.Empty;
var errorClass = value.Equals(ViewBag.ErrorName) ? "error" : string.Empty;
<input type="text" name="Names" value="#value" class="#errorClass" />
}
... or create partial view for each input, save error name to temp data (not to view data to be able to sent this value through actions) with unique key, create render action for each input partial view, generate input id (or some custom data attribute) and temp data error key by the same pattern, check is there error for this input in temp data and add model error for "Names" if there is. Something like below:
var match = existedProducts.Select(p => p.Name).FirstOrDefault(n => model.Names.Contains(n));
if(match != null)
{
ModelState.AddModelError("Names", "Error");
var key = string.Format("Names-{0}", match.Name);
TempData[key] = "Error"; // or just true
return View(model);
}
View:
for (int i; i<Model.NumberOfInputs; i++)
{
var value = Model.Names != null && Model.Names.Count() < index ? Model.Names.ToArray()[index] : string.Empty;
Html.RenderAction("RenderInputAction", new {name = value});
}
And additional action I was talking about:
public PartialViewResult RenderInputAction(string name)
{
var key = string.Format("Names-{0}", name);
if(TempData[key])
{
ModelState.AddModelError("Names", "Error");
}
return PartialView("NameInput", name);
}
and partial view itself:
<input type="text" name="Names" value="#Model" />
As you can see these are robust and strange approaches. I just want to know is there some proper and simpler way in MVC framework to handle such complex models and their model state errors? Maybe some prefix for name attribute to catch specific input or something like that. I'm just not even sure what to google to find the answer. Thanks in advance.
Nice question! I wasn't sure.. so I tried it. Here's what I found.
My test had a model like this:
public class TestModel {
public IList<string> Items { get; set; }
public TestModel()
{
Items = new List<string>() { "Simon", "Whitehead" };
}
}
..a View like this:
#model MvcApplication1.Models.TestModel
#using (Html.BeginForm())
{
for (var i = 0; i < Model.Items.Count; i++)
{
<p>
#Html.TextBoxFor(x => Model.Items[i])
</p>
<p>#Html.ValidationMessageFor(x => Model.Items[i])</p>
}
<input type="submit" />
}
This renders the names Items[0] and Items[1]. Basically, you add a model error with the same names:
[HttpPost]
[ActionName("Index")]
public ActionResult Index_Post(TestModel model)
{
for (int i = 0; i < model.Items.Count; i++)
{
if (string.IsNullOrEmpty(model.Items[i]))
{
// empty name is Items[i]
ModelState.AddModelError(string.Format("Items[{0}]", i), "Required");
return View(model);
}
}
return null;
}
Works fine for me. Individual boxes are required when blank. The result is:

Sending Complex Data to ASP MVC view

I've been struggling to research an answer to this question as I cannot come up with the correct search terms.
Basically I have 2 IEnumerable<T>'s in my controller, below is the code for the attempt I made.
Controller:
IEnumerable<Room> allRooms = RoomHelper.FindAllRooms();
foreach (var room in allRooms)
{
IEnumerable<Bunk> associatedBunks = RoomHelper.FindAssociatedBunksByRoom(room);
if (associatedBunks.Count() > 0)
{
ViewData["Room_"+room.RoomId] = associatedBunks;
}
}
And I'm trying to send them to the view in a way that I can do two foreach loops that will cycle through one set of data (in this case the Room objects and will then using the Room.RoomId key cycle through another IEnumerable which contains the associated data.
My view looks like this but is showing parse errors:
#foreach (var room in ViewBag.Rooms)
{
<h2>#room.RoomName</h2>
#if (ViewData["Room_" + room.RoomId].Count() > 0)
{
<ol>
#foreach (var bunk in ViewData["Room_" + room.RoomId])
{
<li>#bunk.BunkId</li>
}
</ol>
}
}
The end result I'm looking for in the HTML is something like:
<h2>Room 1</h2>
<ol>
<li>Bunk 1</li>
<li>Bunk 2</li>
</ol>
<h2>Room 2</h2>
<ol>
<li>Bunk 3</li>
<li>Bunk 4</li>
</ol>
What is the best practice in ASP.NET MVC 4 with EF5 to achieve this kind of result when passing "multidimensional" (is this multidimensional?) data?
Don't rely on ViewData. Store the data that you want to pass on to your view in a proper ViewModel:
public class RoomViewModel
{
List<Room> Rooms { get; set;}
...
}
Store your data in one of those.
Your Controller method then returns an instance of it:
public RoomViewModel GetRooms(int someParameter)
{
RoomViewModel result = new RoomViewModel();
result.Rooms = RoomHelper.Something(someParameter);
...
return result;
}
Your View declares its model on top:
#model MyApplication.ViewModels.RoomViewModel
and hence you use it in your View.
<h2>#Model.Rooms.Count rooms found</h2>
etc.
Use a code block in your view instead of adding an '#' in front of each C# statement:
#{
foreach (var room in ViewBag.Rooms)
{
#Html.Raw("<h2>" + room.RoomName + "</h2>");
if (ViewData["Room_" + room.RoomId].Count() > 0)
{
#Html.Raw("<ol>");
foreach (var bunk in ViewData["Room_" + room.RoomId])
{
#Html.Raw("<li>" + bunk.BunkId + "</li>");
}
#Html.Raw("</ol>");
}
}
}
You should avoid the use of #HtmlRaw("") as far as possible as it has a XSS vulnerability. But this should put you on the right track.
As per description given by you it seems that Bunk is associated with rooms. If that's the case then Bunk may have some id for pointing to room it belongs. Now you can create a ViewModel like this
public class BunkViewModel:Bunk
{
public BunkViewModel()
{
Mapper.CreateMap<Bunk,BunkViewModel>();
}
//I'm assuming that you already have some id in bunk to point to room it belongs.
//But writing it here to make it clear
public int RoomId { get; set; }
public string RoomName { get; set; }
//Use AutoMapper to Map
public static BunkViewModel Map(Bunk source)
{
return Mapper.Map<Bunk,BunkViewModel>(source);
}
}
Now in your controller
public ActionResult ActionName()
{
var result = new List<BunkViewModel>();
var rooms = RoomHelper.FindAllRooms();
var bunks = BunkHelper.GetAllBunks();
foreach(var bunk in bunks)
{
var bunkViewModel = BunkViewModel.Map(bunk);
var room = rooms.FirstorDefault(r=>room.RoomId == bunk.RoomId);
bunkViewModel.RoomId = room.RoomId; //No need to set if you already have this id in bunk
bunkViewModel.RoomName = room.RoomName;
result.Add(bunkViewModel);
}
return View(result.);
}
Now in your view you can do like this
#model List<MyApplication.ViewModels.RoomViewModel>
#foreach(var bunk in Model)
{
//Print it here as you want..
}

Categories

Resources