基于 EF Core 的领域驱动持久化实践
EF Core 在关系型数据库中的应用
在使用 SQL Server、Oracle 或 PostgreSQL 等关系型数据库时,推荐采用 Entity Framework Core 构建持久化层。该框架支持 LINQ 查询,提供强类型领域模型对象,并简化数据库操作流程。
EF Core 作为 .NET 生态的轻量级数据访问层,可在 Windows 和 Linux 平台运行。相比传统 EF,它具有更小的内存占用和显著的性能提升,是 .NET 应用程序的首选持久化方案。
DDD 视角下的实体设计
遵循领域驱动设计(DDD)原则,EF Core 允许使用 POCO 领域实体(代码优先模式)。这种设计使领域模型与持久化逻辑解耦,符合持久化无关(persistence-ignorant)和基础设施无关(infrastructure-ignorant)的设计准则。
在 DDD 实践中,应将业务规则封装在实体内部,而非通过公开集合操作。EF Core 1.1+ 提供了直接使用字段(而非属性)的能力,使集合访问可通过只读接口控制:
public class PurchaseOrder : BaseEntity
{
private readonly List<OrderDetail> _orderDetails;
public IReadOnlyCollection<OrderDetail> Details => _orderDetails;
protected PurchaseOrder() { }
public PurchaseOrder(int customerId, DateTime purchaseDate)
{
// 初始化业务逻辑
}
public void AddOrderDetail(int productId, string productName, decimal price)
{
// 业务验证逻辑
_orderDetails.Add(new OrderDetail(productId, productName, price));
}
}
通过配置导航属性访问模式,EF Core 能直接操作实体字段而非属性:
public class PurchaseOrderConfiguration : IEntityTypeConfiguration<PurchaseOrder>
{
public void Configure(EntityTypeBuilder<PurchaseOrder> builder)
{
builder.ToTable("purchase_orders");
var navigation = builder.Metadata.FindNavigation(nameof(PurchaseOrder.Details));
navigation.SetPropertyAccessMode(PropertyAccessMode.Field);
}
}
自定义仓储实现
仓储模式将持久化逻辑封装在独立类中,通过工作单元(DbContext)协调数据操作:
public class CustomerRepository : ICustomerRepository
{
private readonly OrderContext _context;
public IUnitOfWork UnitOfWork => _context;
public CustomerRepository(OrderContext context)
{
_context = context;
}
public Customer GetByGuid(string guid)
{
return _context.Customers
.Include(c => c.PaymentMethods)
.FirstOrDefault(c => c.IdentityGuid == guid);
}
}
仓储与 DbContext 的对比
直接使用 DbContext 可实现最简代码,但缺乏解耦。自定义仓储通过抽象层支持单元测试,无需依赖真实数据库:

EF Core 映射配置
使用 Fluent API 替代数据注解,保持领域模型纯净:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.ApplyConfiguration(new PurchaseOrderConfiguration());
}
public class PurchaseOrderConfiguration : IEntityTypeConfiguration<PurchaseOrder>
{
public void Configure(EntityTypeBuilder<PurchaseOrder> builder)
{
builder.HasKey(p => p.Id);
builder.Property(p => p.Id).UseHiLo("order_seq");
builder.OwnsOne(p => p.ShippingAddress, a =>
{
a.WithOwner();
});
builder.Property("_purchaseDate").UsePropertyAccessMode(PropertyAccessMode.Field);
}
}
Hi/Lo 主键生成策略
Hi/Lo 算法在提交前生成唯一标识,避免频繁数据库交互:
- 保持工作单元模式完整性
- 批量获取主键减少数据库往返
- 生成可读标识(非 GUID)
查询规约模式实现
规约模式封装查询条件与关联加载逻辑:
public interface IQuerySpecification<T>
{
Expression<Func<T, bool>> Criteria { get; }
List<Expression<Func<T, object>>> Includes { get; }
}
public class OrderWithDetailsSpecification : BaseSpecification<PurchaseOrder>
{
public OrderWithDetailsSpecification(int orderId)
: base(o => o.Id == orderId)
{
AddInclude(o => o.Details);
}
}
public class EfRepository<T> : IRepository<T>
{
public IEnumerable<T> List(IQuerySpecification<T> spec)
{
var query = spec.Includes.Aggregate(_context.Set<T>().AsQueryable(),
(q, include) => q.Include(include));
return query.Where(spec.Criteria).ToList();
}
}