AutoMapper not able to convert from entity model to viewmodel - c#

I just followed this guide to install and set up AutoMapper, and I think I got it correctly set up. But when I try to map from IEnumerable<ProductCategory> to IEnumerable<ViewModelProductCategory>, like this:
var categories = _context.ProductCategories.Include(e => e.Children).ToList();
var topLevelCategories = categories.Where(e => e.ParentId == null);
var VMCategory = _mapper
.Map<IEnumerable<ProductCategory>,
IEnumerable<ViewModelProductCategory>>(topLevelCategories);
return View(VMCategory);
(Thank you to #Anton Gorbunov, for pointing out that I had my objects swapped!)
... my view complains about receiving the wrong model. I'm pretty sure it's because of the children categories not being mapped to a sub-list, so in the view, item.Children is not properly set up as a viewmodel.
How can I map a list of lists?
This is my Index-view:
#model IEnumerable<MyStore.Models.ViewModels.ViewModelProductCategory>
<ul>
#Html.Partial("_CategoryRecursive", Model)
</ul>
... and this is _CategoryRecursive.cshtml:
#model IEnumerable<MyStore.Models.ViewModels.ViewModelProductCategory>
<ul style="list-style:none;padding-left:0px;">
#if (Model != null)
{
foreach (var item in Model)
{
<li style="margin:8px 0px 0px 0px;">
<ul>
#Html.Partial("_CategoryRecursive.cshtml", item.Children)
</ul>
</li>
}
}
</ul>
Mapping profile:
public class MappingProfile : Profile
{
public MappingProfile()
{
CreateMap<ProductCategory, ViewModelProductCategory>();
CreateMap<ViewModelProductCategory, ProductCategory>();
CreateMap<Product, ViewModelProduct>();
CreateMap<ViewModelProduct, Product>();
}
}
Entity model:
public class ProductCategory
{
public int Id { get; set; }
public int SortOrder { get; set; }
public string Title { get; set; }
[ForeignKey(nameof(ParentCategory))]
public int? ParentId { get; set; }
public ProductCategory ParentCategory { get; set; } //nav.prop to parent
public ICollection<ProductCategory> Children { get; set; } //nav. prop to children
public List<ProductInCategory> ProductInCategory { get; set; }
}
Viewmodel:
public class ViewModelProductCategory
{
public int Id { get; set; }
public int? ParentId { get; set; }
public string Title { get; set; }
public int SortOrder { get; set; }
public string ProductCountInfo
{
get
{
return Products != null && Products.Any() ? Products.Count().ToString() : "0";
}
}
public ProductCategory ParentCategory { get; set; } // Nav.prop. to parent
public IEnumerable<ProductCategory> Children { get; set; } // Nav.prop. to children
public List<ViewModelProduct> Products { get; set; } // Products in this category
public List<ViewModelProduct> OrphanProducts { get; set; } // Products with no references in ProductInCategory
}

Problem here:
var VMCategory = _mapper.Map<IEnumerable<ViewModelProductCategory>, IEnumerable<ProductCategory>>(topLevelCategories);
you should change code to
var VMCategory = _mapper.Map<IEnumerable<ProductCategory>, IEnumerable<ViewModelProductCategory>>(topLevelCategories);
Description:
TDestination = Map<TSource,TDestination>(TSource sourceObject), but you swapped the types TSource and TDestination inside brackets <,>

Related

How to show dynamic category in razor page?

i have create dynamic category with subcategory ...
i can't show on my razor page i used foreach but foreach for 2 step category or 3 step my category have unlimited step
plz help me
public class vmCategoryForSearch
{
public int CategoryId { get; set; }
public string titleEn { get; set; }
public bool isDelete { get; set; }
public List<vmCategoryForSearch> Children { get; set; }
}
how to call this code
public List<vmCategoryForSearch> GetAllCategoryForSearchClients()
{
return catsearchList.Where(mm => mm.ParentId == 0).Select(m => new vmCategoryForSearch
{
titleEn = m.titleEn,
titleFa = m.titleFa,
Children = GetAllCategoryForSearchClients(m.SubId)
}).ToList();
}
private List<vmCategoryForSearch> GetAllCategoryForSearchClients(int id)
{
return catsearchList.Where(sm => sm.ParentId == id).Select(vm => new vmCategoryForSearch
{
titleEn = vm.titleEn,
titleFa = vm.titleFa,
Children = GetAllCategoryForSearchClients(vm.SubId)
}).ToList();
}
my razor page
Here is a simple worked example , you could refer to :
Category model
public class Category
{
public int Id { get; set; }
public string Name { get; set; }
public int ParentId { get; set; }
}
vmCategoryForSearch
public class vmCategoryForSearch
{
public int CategoryId { get; set; }
public string Name { get; set; }
public List<vmCategoryForSearch> Children { get; set; }
}
PageModel
private readonly RazorPages2_1Project.Data.RazorPagesDbContext _context;
private readonly List<Category> list;
public CreateModel(RazorPages2_1Project.Data.RazorPagesDbContext context)
{
_context = context;
list = _context.Category.ToList();
}
public IActionResult OnGet()
{
CategoryList = GetAllCategoryForSearchClients();
return Page();
}
[BindProperty]
public List<vmCategoryForSearch> CategoryList { get; set; }
public List<vmCategoryForSearch> GetAllCategoryForSearchClients()
{
var result= list.Where(mm => mm.ParentId == 0).Select(m => new vmCategoryForSearch
{
Name = m.Name,
Children = GetAllCategoryForSearchClients(m.Id)
}).ToList();
return result;
}
private List<vmCategoryForSearch> GetAllCategoryForSearchClients(int id)
{
var result1 = list.Where(sm => sm.ParentId == id).Select(vm => new vmCategoryForSearch
{
Name = vm.Name,
Children = GetAllCategoryForSearchClients(vm.Id)
}).ToList();
return result1;
}
}
Razor Pages
<div class="c-box_item">
#foreach(var item in Model.CategoryList)
{
<ul >
<li>
<span> #item.Name</span>
<div>
#foreach(var subitem in item.Children)
{
<ul>
<li>
<span> #subitem.Name</span>
<div>
#if (subitem.Children.Count > 0)
{
#foreach (var subitems in subitem.Children)
{}
}
</div>
</li>
</ul>
}
</div>
</li>
</ul>
}
</div>

grouping results in a view ASP.NET

I'm using ASP.NET Core 2.2. I have 2 models and a viewmodel which injects data to a view. I want to order results based on their productType. Let's make it clear
This is Product model
public class Product
{
public int ProductID { get; set; }
public int ProductName { get; set; }
public string ProductImage { get; set; }
public int ProductTypeID { get; set; }
[ForeignKey("ProductTypeID")]
public virtual ProductType ProductType{ get; set; }
}
This is ProductType model
public class ProductType
{
public int ProductTypeID { get; set; }
public int ProductTypeName { get; set; }
public string ProductTypeImage { get; set; }
public string ProductTypeDescription { get; set;
}
And finally this is DishesViewModel
public class DishesVM
{
public IEnumerable<ProductType> ProductType { get; set; }
public IEnumerable<Product> Product { get; set; }
}
In MyController I get data from DB then with automapper, map them to DishViewModel
public class HomeController : Controller
{
public IActionResult Dishes()
{
var productTypes= _context.ProductType.OrderBy(p =>p.ProductTypeID).ToList();
var products= _context.Products.OrderBy(p => p.ProductID).ToList();
var DishesVM = new DishesVM();
DishesVM.ProductType = _mapper.Map<IEnumerable<ProductType>>(productTypes);
DishesVM.Product = _mapper.Map<IEnumerable<Product>>(products);
}
}
Now in Dishes View I can have nested foreach
#model DishesViewModel
<div>
foreach(var pt in Model.ProductType)
{
<h1>pt.ProductTypeName</h1>
foreach(var p in Model.Product)
{
p.ProductName
}
}
</div>
This works fine but the only problem it has, is it returns all products. but I want each Product Category has its Products In front of its header. This is visual representation of what I want and what I have now.
This is what I want
But this is what I have
You have to filter your products by product type in each iteration. At the moment you just display all products for each product type:
<div>
foreach(var type in Model.ProductType)
{
//products which belong to the particular type
var productsForType = Model.Product.Where(x => x.ProductTypeID == type.ProductTypeID);
<h1>pt.ProductTypeName</h1>
foreach(var product in productsForType)
{
product.ProductName
}
}
</div>
This will give you a jump on the linq statement:
var list = productTypes.Where(x => x.ProductTypeID == 1).Select(x => new Product()
{
ProductImage = x.ProductTypeImage,
}).ToList();
You will need to decide on what to put into the where clause, I'm using ProductTypeID

Why can't I get this model to map to this viewmodel?

I have this entity model for a recursively structured category tree:
public class ProductCategory
{
public int Id { get; set; }
public int? ParentId { get; set; }
public string Title { get; set; }
public int SortOrder { get; set; }
public ProductCategory ParentCategory { get; set; } //nav.prop to parent
public ICollection<ProductCategory> Children { get; set; } = new List<ProductCategory>();
public ICollection<ProductInCategory> ProductInCategory { get; set; }
public ICollection<FrontPageProduct> FrontPageProduct { get; set; } // Nav.prop. to front page product
// Recursive sorting:
public void RecursiveOrder()
{
Children = Children.OrderBy(o => o.SortOrder).ToList();
Children.ToList().ForEach(r => r.RecursiveOrder());
}
}
... and this, supposedly, matching ViewModel:
public class ViewModelProductCategory
{
public int Id { get; set; }
public int? ParentId { get; set; }
public string Title { get; set; }
public int SortOrder { get; set; }
public bool Checked { get; set; } // Used for assigning a product to multiple categories in Product/Edit
// Nav.props:
public ViewModelProductCategory ParentCategory { get; set; } // Nav.prop. to parent
public ICollection<ViewModelProductCategory> Children { get; set; } // Nav.prop. to children
public IEnumerable<ViewModelProduct> Products { get; set; } // Products in this category
public IEnumerable<ViewModelFrontPageProduct> FrontPageProducts { get; set; }
public string ProductCountInfo { get { return Products?.Count().ToString() ?? "0"; } }
}
When I try to populate the viewmodel, like this:
List<ProductCategory> DbM =
await _context.ProductCategories
.Include(c => c.Children)
.Where(x => x.ParentId == null)
.OrderBy(o => o.SortOrder)
.ToListAsync();
foreach (var item in DbM)
{
VMSelectCategories.Add(
new ViewModelProductCategory{
Id = item.Id,
Children = item.Children,
Title = item.Title
});
}
VisualStudio screams at me that it can't implicitly convert ProductCategory to ViewModelCategory. This happens at Children = item.Children.
Why isn't it working? Can't I have additional properties in the viewmodel that I use unrelated to the original entity model? Like Checked and ProductCountInfo?
In this line:
Children = item.Children,
Children is the ViewModelProductCategory.Children property, which is type ICollection<ViewModelProductCategory>, while item.Children is the ProductCategory.Children property, which is type ICollection<ProductCategory>. They are different types and neither inherits or implements the other so why would you expect to be able to assign an object of one type to a property of the other type? Would you expect this to work:
var list1 = new List<int> {1, 2, 3};
List<string> list2 = list1;
Of course not (I hope) because assigning a List<int> object to a List<string> variable would be silly. What you're trying to do is exactly the same. You need to provide some way to convert from one type to the other and then implement that in your code. An option for that might be like this:
Children = item.Children.Select(pc => MapToViewModel(pc)).ToList(),
where MapToViewModel is a method that you write to create a ViewModelProductCategory and populate its properties from a ProductCategory parameter.
You might also look at using something like AutoMapper.

MVC get child count in Index view

I used scaffolding to create the Index, Details, Create, Edit and Delete views and the controller. I have two view models (Parent / Child) relation. In my Index view I want to display the list of Teams as well as some information on the players (Parent / child). For example I want to display in the Index view the teams with the players count per team and last players that was modified. I am not sure where to begin.
Example:
(Team) Red -- (Last Modified) 01/02/2015 -- (Number Players) 10 and so on.
Team ViewModel
public class TeamVM
{
public int ID { get; set; }
public string Name { get; set; }
public DateTime? LastActivity { get; set; }
public string NumberPlayers { get; set; }
public IList<PLayerVM> PlayerVM { get; set; }
}
Player ViewModel
public class PlayerVM
{
public int ID { get; set; }
public int TeamID { get; set; }
public string PlayerInfo { get; set; }
public DateTime? CreateDate { get; set; }
}
Other ViewModel
public class TeamViewModel
{
public List<Team> Teams{ get; set; }
}
Controller
public ActionResult Index()
{
TeamViewModelviewModel = new TeamViewModel();
viewModel.Teams= db.Teams.ToList();
return View(viewModel);
}
db.Products.ToList()?? I assume that is where you mean db.Teams.ToList()?
You are using viewmodels, so you should map the db data to your viewmodels first:
public ActionResult Index()
{
var teams = db
.Teams
.Include("Players") // Assuming your Team entity has a collection of Players
.SelectMany(t => new TeamVM {
ID = t.ID,
// etc..
})
.ToList();
return View(new TeamViewModel { Teams = teams });
}
model:
public class TeamVM
{
public int ID { get; set; }
public string Name { get; set; }
public DateTime? LastActivity { get; set; }
public IList<PLayerVM> PlayerVM { get; set; }
public int NumberPlayers {
get { return PlayerVM.Count(); }
}
}
Then in your view:
#model MyProject.Models.TeamViewModel
<table>
#foreach(var team in Model.Teams.ToList()) {
<tr>
<td>#team.Name</td> // Name
<td>#team.NumberPlayers</td> // Playercount
<td>#team.PlayerVM.Max(p => p.LastActivity).LastActivity</td> // Last edited
</tr>
}
</table>

MVC 5 Dynamic Navigation Bar 2 Tier

I currently have a navigation bar which is dynamically created from a MS SQL Database, but I would like to add a second sub layer. Below is what I have so far for the current working single level navigation bar.
Navigation
public partial class Navigation
{
public int Id { get; set; }
public string Title { get; set; }
public Nullable<int> Position { get; set; }
public bool Main { get; set; }
public string Action { get; set; }
public string Controller { get; set; }
}
HomeController
[ChildActionOnly]
public ActionResult Navigation()
{
var navigationModel = (from m in db.Navigations where (m.Main == true) orderby m.Position select m);
return View(navigationModel);
}
Navigation
#{ Layout = null; }
#model IEnumerable<WebApplication1.Models.Navigation>
<ul class="nav sf-menu clearfix">
#foreach (var item in Model)
{
#Html.MenuLink(item.Title, item.Action, item.Controller)
}
</ul>
The second tier needs to link to the Contents table, the relationship is db.Navigations.Id = db.Contents.NavigationId.
Content
public partial class Content
{
public int Id { get; set; }
public Nullable<int> NavigationId { get; set; }
public string Title { get; set; }
public string Content1 { get; set; }
public Nullable<int> Position { get; set; }
public string Image { get; set; }
public string Sub { get; set; }
public Nullable<bool> Active { get; set; }
public string Url { get; set; }
public string Summary { get; set; }
}
Could someone please provide a tutorial or example code of how I can bind child data to the parent dataset and then in the View, how to check if a parent holds child records and if so loop through them.
Any help would be much appreciated :-)
I added a new class called NavigationViewModel to declare the tables I wish to use and modified the HomeController to use NavigationViewModel for querying the respective tables.
NavigationViewModel
namespace WebApplication1.Models
{
public class NavigationViewModel
{
public List<Navigation> Navigation { get; set; }
public List<Content> Content { get; set; }
}
}
HomeController
[ChildActionOnly]
public ActionResult Navigation()
{
var navigationModel = new NavigationViewModel();
navigationModel.Navigation = (from m in db.Navigations where (m.Main == true) orderby m.Position select m).ToList();
navigationModel.Content = (from n in db.Contents where (n.Active == true) orderby n.Position select n).ToList();
return View(navigationModel);
}
Navigation
#model WebApplication1.Models.NavigationViewModel
<ul class="nav sf-menu clearfix">
#foreach (var navigation in Model.Navigation)
{
int records = Model.Content.Count(c => c.NavigationId == navigation.Id);
if (records > 0)
{
#Html.SubMenuLink(navigation.Title, navigation.Action, navigation.Controller)
#Html.Raw("<ul>")
foreach (var content in Model.Content.Where(c => c.NavigationId == navigation.Id))
{
if (string.IsNullOrEmpty(content.Url))
{
if (string.IsNullOrEmpty(content.Content1))
{
}
else
{
#Html.MenuLink(content.Title, "Home/Article/" + content.Id + "/" + ToFriendlyUrl(content.Title), "Home");
}
}
else
{
#Html.MenuLink(content.Title, content.Url, "Home");
}
}
#Html.Raw("</ul>")
#Html.Raw("</li>")
}
else
{
#Html.MenuLink(navigation.Title, navigation.Action, navigation.Controller)
}
}
</ul>
I'm not sure this is the most elegant or efficient way to achieve the end goal, any suggestions to make the code more efficient I am happy to listen.

Categories

Resources