我在保存订单时遇到问题,在负责此任务的服务中,它调用了await _unitOfWork.Repository().AddAsync(order)方法。
然后在数据库中开始保存过程,但保存时返回错误。 奇怪的是,我从来没有将deliveryMethod保存在数据库中,我只保存订单,相同的方法适用于其他实体,但在我保存订单时它返回错误。
我不知道我哪里出了问题,如果有人能给我一些建议,告诉我如何纠正这个问题。
订单服务
using Domain.Entities;
using Domain.Entities.OrderAggregate;
using Domain.Interfaces;
using Domain.Specifications;
namespace Infrastructure.Services;
public class OrderService : IOrderService
{
private readonly ICartRepository _cartRepo;
private readonly IUnitOfWork _unitOfWork;
private readonly IStockMovimentService _movimentService;
public OrderService(ICartRepository cartRepo, IUnitOfWork unitOfWork, IStockMovimentService movimentService)
{
_cartRepo = cartRepo;
_unitOfWork = unitOfWork;
_movimentService = movimentService;
}
public async Task<Order> CreateOrderAsync(string buyerEmail,
int deliveryMethodId, string cartId, Address ShippingAddress)
{
var cart = await _cartRepo.GetCartAsync(cartId);
var items = new List<OrderItem>();
foreach(var item in cart.Items)
{
var productItem = await _unitOfWork.Repository<Product>().GetByIdAsync(item.Id);
var itemOrdered = new ProductItemOrdered(productItem.Id,
productItem.Name, productItem.PictureUrl);
var orderItem = new OrderItem(itemOrdered, productItem.Price, item.Quantity, item.Size);
items.Add(orderItem);
await _movimentService.OutgoingStockMovimentService(productItem, item.Size, item.Quantity);
}
var dm = await _unitOfWork.Repository<DeliveryMethod>().GetByIdAsync(deliveryMethodId);
var subtotal = items.Sum(item => item.Price * item.Quantity);
var spec = new OrderByPaymentIntentIdSpecification(cart.PaymentIntentId);
var order = await _unitOfWork.Repository<Order>().GetEntityWithSpecification(spec);
if(order is not null)
{
order.ShipToAddress = ShippingAddress;
order.DeliveryMethod = dm;
order.Subtotal = subtotal;
await _unitOfWork.Repository<Order>().UpdateAsync(order);
}
else
{
order = new Order(items,buyerEmail,ShippingAddress,dm,subtotal,cart.PaymentIntentId);
await _unitOfWork.Repository<Order>().AddAsync(order);
}
var result = await _unitOfWork.Complete();
if (result <= 0) return null;
return order;
}
订单实体:
using Domain.Enums;
namespace Domain.Entities.OrderAggregate;
public class Order : BaseEntity
{
public string BuyerEmail { get; set; }
public DateTime OrderDate { get; set; } = DateTime.UtcNow;
public Address ShipToAddress { get; set; }
public int DeliveryMethodId { get; set; }
public DeliveryMethod DeliveryMethod { get; set; }
public IReadOnlyList<OrderItem> OrderItems { get; set; }
public decimal Subtotal { get; set; }
public OrderStatus Status { get; set; } = OrderStatus.Pending;
public string PaymentIntentId { get; set; }
public Order()
{
}
public Order(IReadOnlyList<OrderItem> orderItems, string buyerEmail, Address shipToAddress,
DeliveryMethod deliveryMethod, decimal subtotal, string paymentIntentId)
{
BuyerEmail = buyerEmail;
ShipToAddress = shipToAddress;
DeliveryMethod = deliveryMethod;
OrderItems = orderItems;
Subtotal = subtotal;
PaymentIntentId = paymentIntentId;
}
public decimal GetTotal()
{
return Subtotal + DeliveryMethod.Price;
}
}
通用存储库和工作单元
using Microsoft.EntityFrameworkCore;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace Infrastructure.Data
{
public class GenericRepository<T> : IGenericRepository<T> where T : BaseEntity
{
private readonly IDbContextFactory<StoreDbContext> _contextFactory;
public GenericRepository(IDbContextFactory<StoreDbContext> contextFactory)
{
_contextFactory = contextFactory;
}
public async Task AddAsync(T entity)
{
using var context = _contextFactory.CreateDbContext();
await context.Set<T>().AddAsync(entity);
await context.SaveChangesAsync();
}
....
using Domain.Entities;
using Domain.Interfaces;
using Microsoft.EntityFrameworkCore;
using System.Collections;
namespace Infrastructure.Data
{
public class UnitOfWork : IUnitOfWork
{
private readonly IDbContextFactory<StoreDbContext> _contextFactory;
private Hashtable _repositories;
public UnitOfWork(IDbContextFactory<StoreDbContext> contextFactory)
{
_contextFactory = contextFactory;
}
public void Dispose()
{
// No need to dispose DbContext as it's handled by the factory
}
public IGenericRepository<TEntity> Repository<TEntity>() where TEntity : BaseEntity
{
if (_repositories is null) _repositories = new Hashtable();
var type = typeof(TEntity).Name;
if (!_repositories.ContainsKey(type))
{
var repositoryType = typeof(GenericRepository<>);
var repositoryInstance = Activator.CreateInstance(
repositoryType.MakeGenericType(typeof(TEntity)), _contextFactory);
_repositories.Add(type, repositoryInstance);
}
return (IGenericRepository<TEntity>)_repositories[type];
}
public async Task<int> Complete()
{
using var context = _contextFactory.CreateDbContext();
return await context.SaveChangesAsync();
}
}
}
代码返回以下错误:
Microsoft.EntityFrameworkCore.Update[10000]
An exception occurred in the database while saving changes for context type 'Infrastructure.Data.StoreDbContext'.
Microsoft.EntityFrameworkCore.DbUpdateException: An error occurred while saving the entity changes. See the inner exception for details.
---> Npgsql.PostgresException (0x80004005): 23505: duplicate key value violates unique constraint "PK_DeliveryMethods"
DETAIL: Detail redacted as it may contain sensitive data. Specify 'Include Error Detail' in the connection string to include this information.
at Npgsql.Internal.NpgsqlConnector.ReadMessageLong(Boolean async, DataRowLoadingMode dataRowLoadingMode, Boolean readingNotifications, Boolean isReadingPrependedMessage)
at System.Runtime.CompilerServices.PoolingAsyncValueTaskMethodBuilder`1.StateMachineBox`1.System.Threading.Tasks.Sources.IValueTaskSource<TResult>.GetResult(Int16 token)
at Npgsql.NpgsqlDataReader.NextResult(Boolean async, Boolean isConsuming, CancellationToken cancellationToken)
at Npgsql.NpgsqlDataReader.NextResult(Boolean async, Boolean isConsuming, CancellationToken cancellationToken)
at Npgsql.EntityFrameworkCore.PostgreSQL.Update.Internal.NpgsqlModificationCommandBatch.Consume(RelationalDataReader reader, Boolean async, CancellationToken cancellationToken)
Exception data:
Severity: ERROR
SqlState: 23505
MessageText: duplicate key value violates unique constraint "PK_DeliveryMethods"
Detail: Detail redacted as it may contain sensitive data. Specify 'Include Error Detail' in the connection string to include this information.
SchemaName: public
TableName: DeliveryMethods
ConstraintName: PK_DeliveryMethods
File: nbtinsert.c
Line: 671
Routine: _bt_check_unique
--- End of inner exception stack trace ---
at Npgsql.EntityFrameworkCore.PostgreSQL.Update.Internal.NpgsqlModificationCommandBatch.Consume(RelationalDataReader reader, Boolean async, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Update.ReaderModificationCommandBatch.ExecuteAsync(IRelationalConnection connection, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Update.ReaderModificationCommandBatch.ExecuteAsync(IRelationalConnection connection, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Update.Internal.BatchExecutor.ExecuteAsync(IEnumerable`1 commandBatches, IRelationalConnection connection, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Update.Internal.BatchExecutor.ExecuteAsync(IEnumerable`1 commandBatches, IRelationalConnection connection, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Update.Internal.BatchExecutor.ExecuteAsync(IEnumerable`1 commandBatches, IRelationalConnection connection, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChangesAsync(IList`1 entriesToSave, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChangesAsync(StateManager stateManager, Boolean acceptAllChangesOnSuccess, CancellationToken cancellationToken)
at Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal.NpgsqlExecutionStrategy.ExecuteAsync[TState,TResult](TState state, Func`4 operation, Func`4 verifySucceeded, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.DbContext.SaveChangesAsync(Boolean acceptAllChangesOnSuccess, CancellationToken cancellationToken)
Microsoft.EntityFrameworkCore.DbUpdateException: An error occurred while saving the entity changes. See the inner exception for details.
---> Npgsql.PostgresException (0x80004005): 23505: duplicate key value violates unique constraint "PK_DeliveryMethods"
DETAIL: Detail redacted as it may contain sensitive data. Specify 'Include Error Detail' in the connection string to include this information.
at Npgsql.Internal.NpgsqlConnector.ReadMessageLong(Boolean async, DataRowLoadingMode dataRowLoadingMode, Boolean readingNotifications, Boolean isReadingPrependedMessage)
at System.Runtime.CompilerServices.PoolingAsyncValueTaskMethodBuilder`1.StateMachineBox`1.System.Threading.Tasks.Sources.IValueTaskSource<TResult>.GetResult(Int16 token)
at Npgsql.NpgsqlDataReader.NextResult(Boolean async, Boolean isConsuming, CancellationToken cancellationToken)
at Npgsql.NpgsqlDataReader.NextResult(Boolean async, Boolean isConsuming, CancellationToken cancellationToken)
at Npgsql.EntityFrameworkCore.PostgreSQL.Update.Internal.NpgsqlModificationCommandBatch.Consume(RelationalDataReader reader, Boolean async, CancellationToken cancellationToken)
Exception data:
Severity: ERROR
SqlState: 23505
MessageText: duplicate key value violates unique constraint "PK_DeliveryMethods"
Detail: Detail redacted as it may contain sensitive data. Specify 'Include Error Detail' in the connection string to include this information.
SchemaName: public
TableName: DeliveryMethods
ConstraintName: PK_DeliveryMethods
File: nbtinsert.c
Line: 671
Routine: _bt_check_unique
--- End of inner exception stack trace ---
at Npgsql.EntityFrameworkCore.PostgreSQL.Update.Internal.NpgsqlModificationCommandBatch.Consume(RelationalDataReader reader, Boolean async, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Update.ReaderModificationCommandBatch.ExecuteAsync(IRelationalConnection connection, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Update.ReaderModificationCommandBatch.ExecuteAsync(IRelationalConnection connection, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Update.Internal.BatchExecutor.ExecuteAsync(IEnumerable`1 commandBatches, IRelationalConnection connection, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Update.Internal.BatchExecutor.ExecuteAsync(IEnumerable`1 commandBatches, IRelationalConnection connection, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Update.Internal.BatchExecutor.ExecuteAsync(IEnumerable`1 commandBatches, IRelationalConnection connection, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChangesAsync(IList`1 entriesToSave, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChangesAsync(StateManager stateManager, Boolean acceptAllChangesOnSuccess, CancellationToken cancellationToken)
at Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal.NpgsqlExecutionStrategy.ExecuteAsync[TState,TResult](TState state, Func`4 operation, Func`4 verifySucceeded, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.DbContext.SaveChangesAsync(Boolean acceptAllChangesOnSuccess, CancellationToken cancellationToken)
An error occurred while saving the entity changes. See the inner exception for details.
首先,我认为这可能是数据库中订单表中的内容,但它是空的。
-- 调试:
-- 订单表:
-- StoreContext
using System.Reflection;
using Domain.Entities;
using Domain.Entities.OrderAggregate;
using Microsoft.EntityFrameworkCore;
namespace Infrastructure.Data;
public class StoreDbContext : DbContext
{
public StoreDbContext(DbContextOptions<StoreDbContext> options) : base(options)
{
}
public DbSet<Product> Products { get; set; }
public DbSet<ProductType> ProductTypes { get; set; }
public DbSet<ProductBrand> ProductBrands { get; set; }
public DbSet<ProductUnit> ProductUnits { get; set; }
public DbSet<Order> Orders { get; set; }
public DbSet<OrderItem> OrderItem { get; set; }
public DbSet<DeliveryMethod> DeliveryMethods { get; set; }
public DbSet<ProductMovimentHistory> ProductMovimentHistory { get; set; }
public DbSet<ProductSize> ProductSizes { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
foreach (var relationship in modelBuilder.Model.GetEntityTypes().SelectMany(e => e.GetForeignKeys()))
{
relationship.DeleteBehavior = DeleteBehavior.Restrict;
}
modelBuilder.ApplyConfigurationsFromAssembly(Assembly.GetExecutingAssembly());
}
}
我尝试调试它,但我找不到我做错的地方。 如果有人能帮我找到那就太好了。
我发现了这个问题,它发生是因为我正在映射实体本身,因此实体框架知道它需要作为新实体插入,所以我更改了保存实体 Id 的构造函数,以便实体框架理解它是参考而不是插入。
变更后订单实体如何:
using Domain.Enums;
namespace Domain.Entities.OrderAggregate;
public class Order : BaseEntity
{
public string BuyerEmail { get; set; }
public DateTime OrderDate { get; set; } = DateTime.UtcNow;
public Address ShipToAddress { get; set; }
public int DeliveryMethodId { get; set; }
public DeliveryMethod DeliveryMethod { get; set; }
public IReadOnlyList<OrderItem> OrderItems { get; set; }
public decimal Subtotal { get; set; }
public OrderStatus Status { get; set; } = OrderStatus.Pending;
public string PaymentIntentId { get; set; }
public Order()
{
}
public Order(IReadOnlyList<OrderItem> orderItems, string buyerEmail, Address shipToAddress,
DeliveryMethod deliveryMethod, decimal subtotal, string paymentIntentId)
{
BuyerEmail = buyerEmail;
ShipToAddress = shipToAddress;
DeliveryMethodId = deliveryMethod.Id;
OrderItems = orderItems;
Subtotal = subtotal;
PaymentIntentId = paymentIntentId;
}
public decimal GetTotal()
{
return Subtotal + DeliveryMethod.Price;
}
}