I am new to Entity Framework Core 7 and ASP.NET Core 7 MVC. I am learning the concepts using the Northwind database. I am using a generic repository and UnitOfWork pattern on top of EF Core for data access.
I have created a ProductsController
and in the view, I want to display all rows of the products in a grid view along with the corresponding category name and supplier name as the Products
table has Category ID
and Supplier ID
as foreign keys.
In the pure EF, I could have used .Include
to get these two values. However, it's not available in the generic repository pattern. I'm sharing a relationship diagram of these three tables here:
Code:
// GET: ProductController
public ActionResult Index()
{
var products = _unitOfWork.Product.GetAll();
return View(products);
}
// Product model
public partial class Product
{
public int ProductId { get; set; }
[DisplayName("Product Name")]
public string ProductName { get; set; } = null!;
public int? SupplierId { get; set; }
public int? CategoryId { get; set; }
[DisplayName("Quantity Per Unit")]
public string? QuantityPerUnit { get; set; }
[DataType(DataType.Currency)]
public decimal? UnitPrice { get; set; }
public short? UnitsInStock { get; set; }
public short? UnitsOnOrder { get; set; }
public short? ReorderLevel { get; set; }
public bool Discontinued { get; set; }
public virtual Category? Category { get; set; }
public virtual ICollection<OrderDetail> OrderDetails { get;
} = new List<OrderDetail> ();
public virtual Supplier? Supplier { get; set; }
}
In the UnitOfWork
pattern, these three tables are available individually. Essentially I want to know how to get the Category Name and Supplier Name from the Products
collection.
CodePudding user response:
In the UnitOfWork pattern, these three tables are available individually. Essentially I want to know how to get the Category Name and Supplier Name from the Products collection.
Well, as you have Product
class and along with this class you have navigation class those are for instance as Categories
and Suppliers
, which has foreign table relationship with Product
class
Navigation class
public class Categories
{
[Key]
public int CategoryID { get; set; }
public string? CategoryName { get; set; }
}
public class Suppliers
{
[Key]
public int SupplierID { get; set; }
public string? CompanyName { get; set; }
public string? ContactName { get; set; }
}
How To Load Related Entities From Navigation Table In Unit Of Work Pattern:
In your scenario, we can extent our GenericRepository
To Product Repository then we can use .Include
object which help to query data from related table property which would be more convenient and efficient. In your scenario, When we will query upon Product
table at the same time we would like to fetch data related to other table as well right, so have a look below:
UnitOfWork Repository For Product:
public class ProductRepository : GenericRepository<Product>, IProductReposiotry
{
public ProductRepository(ApplicationDbContext context, ILogger logger) : base(context, logger) { }
public override async Task<IEnumerable<Product>> All()
{
try
{
var products = context.Products.Include(cat => cat.Category).Include(sup=>sup.Supplier).ToList();
return await dbSet.ToListAsync();
}
catch (Exception ex)
{
_logger.LogError(ex, "{Repo} All function error", typeof(ProductRepository));
return new List<Product>();
}
}
}
UnitOfWork DbContext:
public class ApplicationDbContext : DbContext
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
public DbSet<Product> Products { get; set; }
public DbSet<Categories> Categories { get; set; }
public DbSet<Suppliers> Suppliers { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Product>().ToTable("Products");
modelBuilder.Entity<Categories>().ToTable("Categories");
modelBuilder.Entity<Suppliers>().ToTable("Suppliers");
}
}
Note: Please be sure about the Northwind
database,property and your class follow same case.
UnitOfWork Controller:
public class UnitOfWorkProductController : Controller
{
private readonly ILogger<UnitOfWorkProductViewController> _logger;
private readonly IUnitOfWork _unitOfWork;
public UnitOfWorkProductViewController(
ILogger<UnitOfWorkProductViewController> logger,
IUnitOfWork unitOfWork)
{
_logger = logger;
_unitOfWork = unitOfWork;
}
public async Task<IActionResult> Index()
{
var products = await _unitOfWork.Products.All();
return View(products);
}
}
View:
@model IEnumerable<YourProjectName.Models.Product>
<table >
<thead>
<tr>
<th>ProductId
<th>ProductName
<th>Supplier Name
<th>Category Name
<th>UnitPrice
</tr>
</thead>
<tbody>
@foreach (var item in Model)
{
<tr>
<td>@item.ProductId</td>
<td>@item.ProductName</td>
<td>@item.Supplier?.CompanyName</td>
<td>@item.Category?.CategoryName</td>
<td>@item.UnitPrice</td>
</tr>
}
</tbody>
</table>
CodePudding user response:
You can use the LINQ Join method to join the three tables and then project the result into a model object with properties for the Category Name and Supplier Name.
var result = from p in _unitOfWork.Product.GetAll()
join c in _unitOfWork.Category.GetAll() on p.CategoryId equals c.CategoryId
join s in _unitOfWork.Supplier.GetAll() on p.SupplierId equals s.SupplierId
select new {
p.ProductId,
p.ProductName,
c.CategoryName,
s.CompanyName
};
return View(result);
In the view, you can access the CategoryName and SupplierName properties of the model object. Hope this helps.