使用Entity Framework Core实现餐厅订单入库的困惑与疑问
餐厅点餐系统订单入库实现方案
先修正实体类的问题
你的实体类存在几个会导致入库失败的问题,先调整:
- Order类:
CustomerID属性类型错误,应该是Guid(对应Customer表的主键),而非Customer类型。如果需要导航到Customer实体,可以额外添加导航属性:
public class Order { public Guid ID { get; set; } public Guid CustomerID { get; set; } // 修正为Guid类型 public Customer Customer { get; set; } // 可选:添加导航属性 public int Quantity { get; set; } public DateTime OrderedDateOn { get; set; } public string OrderDateFor { get; set; } public decimal TotalAmount { get; set; } public string Status { get; set; } public string? DetailRequest { get; set; } public string? DetailReply { get; set; } public int ItemCount { get; set; } // 可选:添加订单明细的导航属性,用于EF自动关联 public ICollection<OrderDetails> OrderDetails { get; set; } = new List<OrderDetails>(); }
- OrderDetails类:
OrderID应该是Guid类型的外键,同时添加Order导航属性;Item属性不应该是List<Item>,因为一条订单明细对应单个商品(记录该商品的购买数量),所以改为ItemID外键+Item导航属性:
public class OrderDetails { public Guid ID { get; set; } public Guid OrderID { get; set; } // 修正为Guid外键 public Order Order { get; set; } // 添加导航属性 public Guid ItemID { get; set; } // 商品ID外键 public Item Item { get; set; } // 商品导航属性 public int AmountOfItem { get; set; } // 该商品的购买数量 }
订单入库的两种实现方式
方式一:利用EF导航属性自动批量保存
如果配置了Order和OrderDetails的导航属性,EF可以自动处理关联关系,一次性完成订单和明细的入库,无需分步操作:
// 1. 构建完整的订单及明细对象 var customerId = Guid.Parse("顾客的ID"); // 替换为实际顾客ID var itemId1 = Guid.Parse("商品1的ID"); var itemId2 = Guid.Parse("商品2的ID"); var newOrder = new Order { ID = Guid.NewGuid(), // 或设置为自动生成,见后文说明 CustomerID = customerId, OrderedDateOn = DateTime.Now, OrderDateFor = "2024-05-20", // 用餐日期 TotalAmount = 125.50m, // 计算好的总金额 Status = "待确认", DetailRequest = "少辣,不要香菜", ItemCount = 2, // 商品种类数 OrderDetails = new List<OrderDetails> { new OrderDetails { ID = Guid.NewGuid(), ItemID = itemId1, AmountOfItem = 1 }, new OrderDetails { ID = Guid.NewGuid(), ItemID = itemId2, AmountOfItem = 2 } } }; // 2. 批量保存到数据库 using (var context = new RestaurantDbContext()) { context.Orders.Add(newOrder); await context.SaveChangesAsync(); }
EF会自动先插入Order记录,再将Order的ID赋值给每个OrderDetails的OrderID外键,最后插入明细记录,全程无需手动关联。
方式二:手动分步保存(先存订单,再存明细)
如果需要更精细的控制,或者不想依赖导航属性,可以分步操作:
// 步骤1:保存主订单,获取订单ID var newOrder = new Order { ID = Guid.NewGuid(), CustomerID = customerId, OrderedDateOn = DateTime.Now, OrderDateFor = "2024-05-20", TotalAmount = 125.50m, Status = "待确认", DetailRequest = "少辣,不要香菜", ItemCount = 2 }; using (var context = new RestaurantDbContext()) { context.Orders.Add(newOrder); await context.SaveChangesAsync(); // 此时newOrder的ID已被数据库确认(如果是自动生成的也会自动赋值) } // 步骤2:构建并保存订单明细,关联已保存的订单ID var orderDetails = new List<OrderDetails> { new OrderDetails { ID = Guid.NewGuid(), OrderID = newOrder.ID, // 关联刚保存的订单ID ItemID = itemId1, AmountOfItem = 1 }, new OrderDetails { ID = Guid.NewGuid(), OrderID = newOrder.ID, ItemID = itemId2, AmountOfItem = 2 } }; using (var context = new RestaurantDbContext()) { context.OrderDetails.AddRange(orderDetails); await context.SaveChangesAsync(); }
关键注意事项
- 主键生成:如果不想手动生成Guid,可以给ID属性添加
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]特性,让数据库自动生成主键值。 - 关系配置:需要在DbContext的
OnModelCreating方法中配置实体间的关联关系,确保EF能正确识别外键:
protected override void OnModelCreating(ModelBuilder modelBuilder) { // 订单与明细:一对多 modelBuilder.Entity<Order>() .HasMany(o => o.OrderDetails) .WithOne(od => od.Order) .HasForeignKey(od => od.OrderID) .OnDelete(DeleteBehavior.Cascade); // 删除订单时自动删除明细 // 明细与商品:多对一 modelBuilder.Entity<OrderDetails>() .HasOne(od => od.Item) .WithMany() .HasForeignKey(od => od.ItemID); // 订单与顾客:多对一 modelBuilder.Entity<Order>() .HasOne(o => o.Customer) .WithMany() .HasForeignKey(o => o.CustomerID); }
- 事务保障:为了避免订单保存成功但明细保存失败导致数据不一致,建议使用事务包裹整个入库操作:
using (var context = new RestaurantDbContext()) using (var transaction = context.Database.BeginTransaction()) { try { context.Orders.Add(newOrder); await context.SaveChangesAsync(); context.OrderDetails.AddRange(orderDetails); await context.SaveChangesAsync(); await transaction.CommitAsync(); } catch (Exception ex) { await transaction.RollbackAsync(); // 处理异常,比如日志记录、返回错误信息 throw; } }
内容的提问来源于stack exchange,提问作者Danny
相关产品推荐
相关产品推荐

