Как получить связанные значения внешнего ключа при использовании универсального репозитория и шаблонов единиц работы с Entity Framework Core

Я новичок в Entity Framework Core 7 и ASP.NET Core 7 MVC. Я изучаю концепции, используя базу данных Northwind. Я использую общий репозиторий и шаблон UnitOfWork поверх EF Core для доступа к данным.

я создалProductsControllerи в представлении я хочу отобразить все строки продуктов в виде сетки вместе с соответствующим именем категории и именем поставщика, поскольку таблица имеетCategory IDиSupplier IDкак внешние ключи.

В чистом EF я мог бы использовать.Includeчтобы получить эти два значения. Однако он недоступен в универсальном шаблоне репозитория. Я делюсь диаграммой отношений этих трех таблиц здесь:

Код:

      // 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; }
}

вUnitOfWorkшаблон, эти три таблицы доступны по отдельности. По сути, я хочу знать, как получить имя категории и имя поставщика изProductsколлекция.

2 ответа

В шаблоне UnitOfWork эти три таблицы доступны по отдельности. По сути, я хочу знать, как получить имя категории и имя поставщика из коллекции продуктов.

Ну, поскольку у вас есть класс, и вместе с этим классом у вас есть класс навигации, например, какCategoriesиSuppliers, который имеет отношение внешней таблицы с классом 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; }
    }

Как загрузить связанные объекты из таблицы навигации в шаблон единицы работы:

В вашем сценарии мы можем расширить нашиGenericRepositoryВ репозиторий продуктов мы можем использовать.Includeобъект, который помогает запрашивать данные из связанного свойства таблицы, что было бы более удобным и эффективным. В вашем сценарии, когда мы будем запрашиватьProducttable в то же время мы хотели бы получить данные, относящиеся к другой таблице, а также правильно, поэтому посмотрите ниже:

Репозиторий UnitOfWork для продукта:

      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>();
            }
        }
    }

Юнитофворк дбконтекст:

      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");
           
        }

    }

Примечание. Убедитесь, чтоNorthwindбаза данных, свойство и ваш класс следуют одному и тому же случаю.

Контроллер UnitOfWork:

      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);
        }
    }

Вид:

      @model IEnumerable<YourProjectName.Models.Product>


<table class="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>

Выход:

Вы можете использовать метод LINQ Join, чтобы соединить три таблицы, а затем спроецировать результат в объект модели со свойствами для имени категории и имени поставщика.

      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);

В представлении вы можете получить доступ к свойствам CategoryName и SupplierName объекта модели. Надеюсь это поможет.

Другие вопросы по тегам