Как получить связанные значения внешнего ключа при использовании универсального репозитория и шаблонов единиц работы с 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
объект, который помогает запрашивать данные из связанного свойства таблицы, что было бы более удобным и эффективным. В вашем сценарии, когда мы будем запрашиватьProduct
table в то же время мы хотели бы получить данные, относящиеся к другой таблице, а также правильно, поэтому посмотрите ниже:
Репозиторий 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 объекта модели. Надеюсь это поможет.