I am trying to list single product details in a view. The product specification changes dynamically because specifications are added row-wise in table, which means we can add huge number of specifications for each product (as done in ecommerce sites). Right now I am able to meet the requirement using ViewBag
, but I am deciding to use ViewModel
as a better practice.
Model class:
// Product:
public partial class ProductTable
{
public ProductTable()
{
this.SpecificationsTable = new HashSet<SpecificationsTable>();
}
public int ProductID { get; set; }
public string Title { get; set; }
public string SmallDescription { get; set; }
public string FullDescription { get; set; }
public virtual ICollection<SpecificationsTable> SpecificationsTable { get; set; }
}
//Specifications:
public partial class SpecificationsTable
{
public int SpecificationsID { get; set; }
public string SpecificationName { get; set; }
public string SpecificationValue { get; set; }
public Nullable<int> ProductID { get; set; }
public virtual ProductTable ProductTable { get; set; }
}
ViewModel:
public class DetailsViewModel
{
public int ProductID { get; set; }
public string Title { get; set; }
public string SmallDescription { get; set; }
public string FullDescription { get; set; }
public string SpecificationName { get; set; }
public string SpecificationValue { get; set; }
}
ActionMethod
public ActionResult ProductDetails(int id)
{
var details = (from c in dbo.ProductTable
join s in dbo.SpecificationsTable
on c.ProductID equals s.ProductID
where c.ProductID == id
select new DetailViewModel
{
Title = c.Title,
SmallDescription = c.SmallDescription,
FullDescription = c.FullDescription
}).ToList();
// To remove repeated product title , small and full description
var distinctItems = details.GroupBy(x => x.ProductID).Select(y => y.First());
// To show product title, small and full description for this product
ViewBag.ProductDetails = distinctItems;
var specifications = (from c in dbo.ProductTable
join s in dbo.SpecificationsTable
on c.ProductID equals s.ProductID
where c.ProductID == id
select new DetailViewModel
{
SpecificationName = s.SpecificationName,
SpecificationValue = s.SpecificationValue
}).ToList();
// To show list of specifications for this product
ViewBag.Specifcations = specifications;
return View();
}
expected output in view:
Details:
Title: New Samsung offer
SmallDescription : Something small
FullDescription : Something full
Specifcations:
Mobile Name :Samsung
Model : 2015
Price : 70 $
Color: White
I am using database first method and I am trying to learn how we can use view model here.
You current view model does not reflect what you want to display in the view, which is multiple specification for each product, so you need a collection property. Change your view models to
public class SpecificationVM
{
public string Name { get; set; }
public string Value { get; set; }
}
public class ProductVM
{
public string Title { get; set; }
public string SmallDescription { get; set; }
public string FullDescription { get; set; }
IEnumerable<SpecificationVM> Specifications { get; set; }
}
Then in the controller, populate you view model using
public ActionResult ProductDetails(int id)
{
var product = db.ProductTable.Where(p => p.ProductID == id).FirstOrDefault();
// note you may need to add .Include("SpecificationsTable") in the above
if (product == null)
{
return new HttpNotFoundResult();
}
ProductVM model = new ProductVM()
{
Title = product.Title,
SmallDescription = product.SmallDescription,
FullDescription = product.FullDescription,
Specifications = product.SpecificationsTable.Select(s => new SpecificationVM()
{
Name = s.SpecificationName,
Value = s.SpecificationValue
})
};
return View(model);
}
Then in the view
@model yourAssembly.ProductVM
<h2>Details</h2>
@Html.DisplayNameFor(m => m.Title)
@Html.DisplayFor(m => m.Title)
.... // ditto for SmallDescription and FullDescription
<h2>Specifications</h2>
@foreach(var item in Model.Specifications)
{
@Html.DisplayFor(m => item.Name)
@Html.DisplayFor(m => item.Value)
}