清洁架构 EF Core DbContext 项目
Clean Arcitecture EF Core DbContext project
正在寻找有关 Clean Architecture 方法和 EF Core 的一些一般指导。
我有一个 Web 应用程序 (UI) 项目、Domain/Business 项目和我的 Data/Infrastructure 项目用于所有 crud 操作。
引入和创建 DbContext 时,上下文本身应该存储在数据项目中,而实体 (table 类) 应该存储在 Domain/Business 项目中,因为它们有没有直接的 crud 选项。
或者 DbContext 和相应的实体 (tables) 应该一起在 Data 项目中。
感谢任何帮助。
您应该根据业务需求设计您的域模型类,并以最有效的方式实现业务域不变量。
这意味着 不要对您的域模型实体造成任何基础架构问题的负担。如果将这些 类 放入域项目中,将 DbContext 和 EFCore 配置 类 之类的东西放入数据层,则可以更容易地遵守此原则。
您可以查看参考资料 Microsoft reference project,其中采用了这种方法。
放置在核心(或业务领域)项目中的order entity(这里也是一个聚合根)除了无参数私有构造函数之外没有任何基础设施问题EFCore 要求 - 可接受的权衡。
using Ardalis.GuardClauses;
using Microsoft.eShopWeb.ApplicationCore.Interfaces;
using System;
using System.Collections.Generic;
namespace Microsoft.eShopWeb.ApplicationCore.Entities.OrderAggregate
{
public class Order : BaseEntity, IAggregateRoot
{
private Order()
{
// required by EF
}
public Order(string buyerId, Address shipToAddress, List<OrderItem> items)
{
Guard.Against.NullOrEmpty(buyerId, nameof(buyerId));
Guard.Against.Null(shipToAddress, nameof(shipToAddress));
Guard.Against.Null(items, nameof(items));
BuyerId = buyerId;
ShipToAddress = shipToAddress;
_orderItems = items;
}
public string BuyerId { get; private set; }
public DateTimeOffset OrderDate { get; private set; } = DateTimeOffset.Now;
public Address ShipToAddress { get; private set; }
// DDD Patterns comment
// Using a private collection field, better for DDD Aggregate's encapsulation
// so OrderItems cannot be added from "outside the AggregateRoot" directly to the collection,
// but only through the method Order.AddOrderItem() which includes behavior.
private readonly List<OrderItem> _orderItems = new List<OrderItem>();
// Using List<>.AsReadOnly()
// This will create a read only wrapper around the private list so is protected against "external updates".
// It's much cheaper than .ToList() because it will not have to copy all items in a new collection. (Just one heap alloc for the wrapper instance)
//https://msdn.microsoft.com/en-us/library/e78dcd75(v=vs.110).aspx
public IReadOnlyCollection<OrderItem> OrderItems => _orderItems.AsReadOnly();
public decimal Total()
{
var total = 0m;
foreach (var item in _orderItems)
{
total += item.UnitPrice * item.Units;
}
return total;
}
}
}
order entity database configuration 被放置在 infrastructure/data 项目中,以检测域和数据库层之间的映射,使域项目独立于此类基础设施问题。
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using Microsoft.eShopWeb.ApplicationCore.Entities.OrderAggregate;
namespace Microsoft.eShopWeb.Infrastructure.Data.Config
{
public class OrderConfiguration : IEntityTypeConfiguration<Order>
{
public void Configure(EntityTypeBuilder<Order> builder)
{
var navigation = builder.Metadata.FindNavigation(nameof(Order.OrderItems));
navigation.SetPropertyAccessMode(PropertyAccessMode.Field);
builder.OwnsOne(o => o.ShipToAddress, a =>
{
a.WithOwner();
a.Property(a => a.ZipCode)
.HasMaxLength(18)
.IsRequired();
a.Property(a => a.Street)
.HasMaxLength(180)
.IsRequired();
a.Property(a => a.State)
.HasMaxLength(60);
a.Property(a => a.Country)
.HasMaxLength(90)
.IsRequired();
a.Property(a => a.City)
.HasMaxLength(100)
.IsRequired();
});
}
}
}
有了这个,您就可以清楚地分离关注点,可以专注于业务层中的业务逻辑,获得更好的逻辑可测试性,并且您仍然能够利用 EFCore 的优势。
正在寻找有关 Clean Architecture 方法和 EF Core 的一些一般指导。
我有一个 Web 应用程序 (UI) 项目、Domain/Business 项目和我的 Data/Infrastructure 项目用于所有 crud 操作。
引入和创建 DbContext 时,上下文本身应该存储在数据项目中,而实体 (table 类) 应该存储在 Domain/Business 项目中,因为它们有没有直接的 crud 选项。
或者 DbContext 和相应的实体 (tables) 应该一起在 Data 项目中。
感谢任何帮助。
您应该根据业务需求设计您的域模型类,并以最有效的方式实现业务域不变量。
这意味着 不要对您的域模型实体造成任何基础架构问题的负担。如果将这些 类 放入域项目中,将 DbContext 和 EFCore 配置 类 之类的东西放入数据层,则可以更容易地遵守此原则。
您可以查看参考资料 Microsoft reference project,其中采用了这种方法。
放置在核心(或业务领域)项目中的order entity(这里也是一个聚合根)除了无参数私有构造函数之外没有任何基础设施问题EFCore 要求 - 可接受的权衡。
using Ardalis.GuardClauses;
using Microsoft.eShopWeb.ApplicationCore.Interfaces;
using System;
using System.Collections.Generic;
namespace Microsoft.eShopWeb.ApplicationCore.Entities.OrderAggregate
{
public class Order : BaseEntity, IAggregateRoot
{
private Order()
{
// required by EF
}
public Order(string buyerId, Address shipToAddress, List<OrderItem> items)
{
Guard.Against.NullOrEmpty(buyerId, nameof(buyerId));
Guard.Against.Null(shipToAddress, nameof(shipToAddress));
Guard.Against.Null(items, nameof(items));
BuyerId = buyerId;
ShipToAddress = shipToAddress;
_orderItems = items;
}
public string BuyerId { get; private set; }
public DateTimeOffset OrderDate { get; private set; } = DateTimeOffset.Now;
public Address ShipToAddress { get; private set; }
// DDD Patterns comment
// Using a private collection field, better for DDD Aggregate's encapsulation
// so OrderItems cannot be added from "outside the AggregateRoot" directly to the collection,
// but only through the method Order.AddOrderItem() which includes behavior.
private readonly List<OrderItem> _orderItems = new List<OrderItem>();
// Using List<>.AsReadOnly()
// This will create a read only wrapper around the private list so is protected against "external updates".
// It's much cheaper than .ToList() because it will not have to copy all items in a new collection. (Just one heap alloc for the wrapper instance)
//https://msdn.microsoft.com/en-us/library/e78dcd75(v=vs.110).aspx
public IReadOnlyCollection<OrderItem> OrderItems => _orderItems.AsReadOnly();
public decimal Total()
{
var total = 0m;
foreach (var item in _orderItems)
{
total += item.UnitPrice * item.Units;
}
return total;
}
}
}
order entity database configuration 被放置在 infrastructure/data 项目中,以检测域和数据库层之间的映射,使域项目独立于此类基础设施问题。
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using Microsoft.eShopWeb.ApplicationCore.Entities.OrderAggregate;
namespace Microsoft.eShopWeb.Infrastructure.Data.Config
{
public class OrderConfiguration : IEntityTypeConfiguration<Order>
{
public void Configure(EntityTypeBuilder<Order> builder)
{
var navigation = builder.Metadata.FindNavigation(nameof(Order.OrderItems));
navigation.SetPropertyAccessMode(PropertyAccessMode.Field);
builder.OwnsOne(o => o.ShipToAddress, a =>
{
a.WithOwner();
a.Property(a => a.ZipCode)
.HasMaxLength(18)
.IsRequired();
a.Property(a => a.Street)
.HasMaxLength(180)
.IsRequired();
a.Property(a => a.State)
.HasMaxLength(60);
a.Property(a => a.Country)
.HasMaxLength(90)
.IsRequired();
a.Property(a => a.City)
.HasMaxLength(100)
.IsRequired();
});
}
}
}
有了这个,您就可以清楚地分离关注点,可以专注于业务层中的业务逻辑,获得更好的逻辑可测试性,并且您仍然能够利用 EFCore 的优势。