Reputation: 1449
In a view i need a view model which contains two models. I dont know how to retrieve data in the repository.
There is a Products model which has a foreign key to another model. Here is my PRODUCTS model:
public class Products
{
public int ProductId {get; set;}
public string Productname {get; set;}
public int ProductTypeID { get; set; }
[ForeignKey("ProductTypeID")]
public virtual ProductTypes ProductTypes { get; set; }
}
And this is my PRODUCTTYPE model:
public class ProductTypes
{
public int ProductTypeId {get; set;}
public int ProductTypeName {get; set;}
}
Now I want to use them in a View, so I made a viewmodel
public class ProductsViewModel
{
public Products Products { get; set; }
public IEnumerable<ProductTypes> ProductTypes { get; set; }
}
Here is my problem. I don't know how to retrieve data from a viewmodel in a repository like this:
public async Task<IEnumerable< XXX >> GetAllProducts()
{
return await _RepositoryContext.Set<xxx>.ToListAsync();
}
Finally this is my Controller:
public async Task<IActionResult> index()
{
return View(await ProductRepository.GetAllProducts());
}
Upvotes: 1
Views: 547
Reputation: 387547
It depends on what you actually want to do within your view: A view model is supposed to contain exactly the data necessary to display the view. Usually, you would not want to include more data there. So using database models inside of a view model is not the best choice; it would be better to design actual view models that match the stuff you display, and then you decide how to properly get that data.
From how your view model looks, I could assume two use cases:
Option 2 is really simple and requires you to only query each entity type once:
var viewModel = new ProductsViewModel
{
Products = await db.Products.ToListAsync(),
ProductTypes = await db.ProductTypes.ToListAsync(),
};
Option 1 can be solved naively by including the product types through the navigation property in the Product
entity:
var products = await db.Products.Include(p => p.ProductType).ToListAsync();
var viewModel = new ProductsViewModel
{
Products = products,
ProductTypes = products.Select(p => p.ProductType).Distinct().ToList(),
};
This however has the downside that with few distinct product types you will be loading each product type multiple times. Because your product type has only an id and a name, this is not that problematic but for more complex types it can be.
A different approach would be to query the product type ids first from the list of products, and then loading the product types afterwards:
var products = await db.Products.Include(p => p.ProductType).ToListAsync();
var productTypeIds = product.Select(p => p.ProductTypeId).Distinct().ToList();
var viewModel = new ProductsViewModel
{
Products = products,
ProductTypes = await db.ProductTypes.Select(t => productTypeIds.Contains(t.Id)).ToListAsync(),
};
Two notes:
DbContext
here. If you want to abstract that into a repository, you can do so.Products
but only contains information about a single product. Similarly, the view model contains a property Products
of that exact same type. That does not make a lot of sense to me, so I just assumed that the entity is called Product
and represents a single item, while the ProductsViewModel
has Products
, a list of Product
.Upvotes: 0
Reputation: 695
In your repository you use include expression like this
public async Task<IEnumerable< Products >> GetAllProducts()
{
var products = _RepositoryContext.Set<Products> ();
return await products.include(x =>x.ProductType).ToListAsync();
}
But I think you need to modify your models. you are returning domain classes in controller as view Model which is not a good idea, you should have separate model classes and should use Automapper to map domain to model classes so that on client side you send only model classes which may have many extra columns than domain classes . Here is how your model classes should look like
public class Products
{
public int ProductId {get; set;}
public string Productname {get; set;}
public int ProductTypeID { get; set; }
[ForeignKey("ProductTypeID")]
public virtual ProductTypes ProductTypes { get; set; }
}
public class ProductTypes
{
public int ProductTypeId {get; set;}
public int ProductTypeName {get; set;}
}
Now models should be like this
public class ProductsModel
{
public int ProductId {get; set;}
public string Productname {get; set;}
public int ProductTypeID { get; set; }
public virtual ProductTypesModel ProductTypes { get; set; }
public string ProductTypeName {get; set;}
}
public class ProductTypesModel
{
public int ProductTypeId {get; set;}
public int ProductTypeName {get; set;}
}
Now repository method should look like this
public async Task<IEnumerable< Products >> GetAllProducts()
{
var products = _RepositoryContext.Set<Products> ();
return await products.include(x =>x.ProductType).ToListAsync();
}
Finally this would be your controller Controller,
public async Task<IActionResult> index()
{
var productList= await ProductRepository.GetAllProducts()
var ProductModels = Mapper.Map<IEnumerable<Products>, IEnumerable<ProductsModel>>( productList)
return View(ProductModels);
}
To Know how to setup AutoMapper please refer to my this post, How to setup Automapper in ASP.NET Core
Upvotes: 1
Reputation: 3810
It all depends on your architecture. However, Repository might not be the best place to do the mapping between models and viewmodels because once the ORM is changed the repository will possibly be modified too while this mapping could be somewhere to avoid unnecessary changes. I personally prefer to do the mapping either in BLL or in viewmodel itself without using AutoMapper. Here is one possible way of mapping in viewmodels:
public class ProductsDto
{
public int ProductId { get; set; }
public string Productname { get; set; }
public virtual ProductTypeDto ProductTypes { get; set; }
public void SetDto(Products obj)
{
if (obj == null)
return;
this.ProductId = obj.ProductId;
this.Productname = obj.Productname;
if (obj.ProductTypes != null)
{
this.ProductTypes = new ProductTypeDto();
this.ProductTypes.SetDto(obj.City);
}
}
}
public class ProductTypeDto
{
public int ProductTypeId { get; set; }
public int ProductTypeName { get; set; }
public void SetDto(ProductType obj)
{
if (obj == null)
return;
this.ProductTypeId = obj.ProductTypeId;
this.ProductTypeName = obj.ProductTypeName;
}
}
Using viewmodels:
public ProductsDto GetProductsDto(Products obj)
{
var dto = new ProductsDto();
dto.SetDto(obj);
return dto;
}
----------------------------------------------------------
var products = _RepositoryContext.Products.ToList();
var productsDto = products?.Select(GetProductsDto);
Upvotes: 0