MST

星途 面试题库

面试题:C#的Entity Framework Core中处理复杂关联数据的优化策略

在C#使用Entity Framework Core时,有三个实体:`Customer`(客户)、`Order`(订单)和`Product`(产品),它们之间存在复杂的多对多关系。客户可以有多个订单,订单可以包含多种产品。现在要获取某个客户购买过的所有产品列表,并且确保在处理大数据量时性能良好,你如何设计数据访问层并优化查询?
25.8万 热度难度
编程语言C#

知识考点

AI 面试

面试题答案

一键面试
  1. 设计实体类
    • 定义Customer类,包含IdName等属性,以及一个ICollection<Order>导航属性来表示该客户的订单。
    public class Customer
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public ICollection<Order> Orders { get; set; }
    }
    
    • 定义Order类,包含IdOrderDate等属性,一个Customer导航属性表示下单的客户,以及一个ICollection<Product>导航属性表示订单中的产品。
    public class Order
    {
        public int Id { get; set; }
        public DateTime OrderDate { get; set; }
        public Customer Customer { get; set; }
        public ICollection<Product> Products { get; set; }
    }
    
    • 定义Product类,包含IdNamePrice等属性。
    public class Product
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public decimal Price { get; set; }
    }
    
  2. 配置多对多关系: 在DbContextOnModelCreating方法中配置多对多关系。
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Order>()
           .HasOne(o => o.Customer)
           .WithMany(c => c.Orders)
           .HasForeignKey(o => o.CustomerId);
    
        modelBuilder.Entity<Order>()
           .HasMany(o => o.Products)
           .WithMany()
           .UsingEntity(j => j.ToTable("OrderProduct"));
    }
    
  3. 数据访问层设计
    • 创建一个接口ICustomerProductRepository来定义获取客户购买产品列表的方法。
    public interface ICustomerProductRepository
    {
        Task<List<Product>> GetCustomerProductsAsync(int customerId);
    }
    
    • 实现该接口,在实现类CustomerProductRepository中使用DbContext进行查询。
    public class CustomerProductRepository : ICustomerProductRepository
    {
        private readonly YourDbContext _context;
    
        public CustomerProductRepository(YourDbContext context)
        {
            _context = context;
        }
    
        public async Task<List<Product>> GetCustomerProductsAsync(int customerId)
        {
            return await _context.Customers
               .Where(c => c.Id == customerId)
               .SelectMany(c => c.Orders.SelectMany(o => o.Products))
               .Distinct()
               .ToListAsync();
        }
    }
    
  4. 优化查询
    • 索引优化
      • Customer表的Id列创建索引,这在Where子句中使用,通常EF Core会自动为作为主键的Id列创建索引。
      • Order表的CustomerId列创建索引,因为在关联CustomerOrder表时会使用该列。
      • OrderProduct连接表的OrderIdProductId列创建索引,因为在多对多关系查询中会使用这两列。
    • 分页处理:如果数据量过大,考虑分页处理。例如,修改GetCustomerProductsAsync方法支持分页。
    public async Task<List<Product>> GetCustomerProductsAsync(int customerId, int pageNumber, int pageSize)
    {
        return await _context.Customers
           .Where(c => c.Id == customerId)
           .SelectMany(c => c.Orders.SelectMany(o => o.Products))
           .Distinct()
           .Skip((pageNumber - 1) * pageSize)
           .Take(pageSize)
           .ToListAsync();
    }
    
    • 缓存机制:可以使用缓存来减少数据库的查询压力。例如,使用MemoryCache或者分布式缓存(如Redis)。如果客户购买的产品列表不经常变化,可以将查询结果缓存起来。
    private readonly IMemoryCache _memoryCache;
    public CustomerProductRepository(YourDbContext context, IMemoryCache memoryCache)
    {
        _context = context;
        _memoryCache = memoryCache;
    }
    public async Task<List<Product>> GetCustomerProductsAsync(int customerId)
    {
        if (_memoryCache.TryGetValue($"Customer_{customerId}_Products", out List<Product> products))
        {
            return products;
        }
    
        products = await _context.Customers
           .Where(c => c.Id == customerId)
           .SelectMany(c => c.Orders.SelectMany(o => o.Products))
           .Distinct()
           .ToListAsync();
    
        _memoryCache.Set($"Customer_{customerId}_Products", products, TimeSpan.FromMinutes(10));
        return products;
    }