Home > Software design >  How to get foreign key linked values while using generic repository and unit of work patterns with E
How to get foreign key linked values while using generic repository and unit of work patterns with E

Time:01-03

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:

enter image description 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>

Output: enter image description here

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.

  • Related