Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/309.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/asp.net-core/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/python-3.x/19.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 映射实体的EF Core写入操作存在问题 上下文_C#_Asp.net Core_Entity Framework Core_Domain Driven Design - Fatal编程技术网

C# 映射实体的EF Core写入操作存在问题 上下文

C# 映射实体的EF Core写入操作存在问题 上下文,c#,asp.net-core,entity-framework-core,domain-driven-design,C#,Asp.net Core,Entity Framework Core,Domain Driven Design,我正在DDD架构中尝试我自己的想法。与我所看到的其他项目的关键区别在于,我没有将域模型用作数据实体,而是使用我称之为存储的单独模型,这些模型与域模型映射并表示数据库的状态 如果您不熟悉这个想法,那么可以将核心业务逻辑与应用程序的其他元素(如数据库)完全解耦。为了实现这一点,我定义了域模型,其中包含业务逻辑和验证,然后是实体模型,它们表示与域模型相同的状态(业务和验证逻辑剥离),但也表示特定于EF的关系属性 问题 EF操作用于更简单的操作。假设我们有一个竞赛,它可以包含几个试验 伪代码中的示例:

我正在DDD架构中尝试我自己的想法。与我所看到的其他项目的关键区别在于,我没有将域模型用作数据实体,而是使用我称之为存储的单独模型,这些模型与域模型映射并表示数据库的状态

如果您不熟悉这个想法,那么可以将核心业务逻辑与应用程序的其他元素(如数据库)完全解耦。为了实现这一点,我定义了域模型,其中包含业务逻辑和验证,然后是实体模型,它们表示与域模型相同的状态(业务和验证逻辑剥离),但也表示特定于EF的关系属性

问题 EF操作用于更简单的操作。假设我们有一个竞赛,它可以包含几个试验

伪代码中的示例:

contest = new Contest
contest.Add(new Trial(1))
contest.Add(new Trial(2))

data.Save(contest) // performs mapping to ContestEntity and calls dbContext.Add
// So far so good

contestWithTrials = data.Get() // contest comes with 2 Included Trials
contestWithTrials.Add(new Trial(3))
data.Save(contestWithTrials) // performs mapping, calls dbContext.Update and tries to save but fails.

错误是:

无法跟踪实体类型为“试用”的实例,因为已跟踪键值为“{Id:1}”的另一个实例

试图更新或删除存储中不存在的实体

由于某种原因,映射混淆了EF,它试图重新创建已经存在的试用版,但我不明白为什么-我可以看到在调用
SaveChanges
之前,实体在
DbSet.Local
中被正确添加,但它仍然抛出


我已经建立了一个PoC分支。这是一个控制台应用程序,根据Progrman的建议,具有最少的可复制示例。由于安装需要几个软件包,我认为最好是在repo中,而不是在单个文件中进行设置。

最好将包含业务逻辑的域模型类与基础架构依赖项分离,以数据库为例。但是,当您使用EF-Core时,您可以完全忽略实体模型,因为EF-Core的设计方式已经允许您分离域和数据库关注点

让我们看一个来自微软公司的例子。 域模型类Order(排序上下文的聚合根)包含域逻辑,其结构使业务不变量能够以最佳方式得到遵守

当您查看Order类时,您会发现它没有数据库或其他基础结构依赖项。域模型类也位于

解决方案的一部分

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; }

    private readonly List<OrderItem> _orderItems = new List<OrderItem>();

    public IReadOnlyCollection<OrderItem> OrderItems => _orderItems.AsReadOnly();

    public decimal Total()
    {
        var total = 0m;
        foreach (var item in _orderItems)
        {
            total += item.UnitPrice * item.Units;
        }
        return total;
    }
}
公共类顺序:BaseEntity,IAggregateRoot
{
私人命令()
{
//EF要求
}
公共订单(字符串buyerId、地址shipToAddress、列表项)
{
防止无效空(buyerId,name of(buyerId));
防范.Null(shipToAddress,nameof(shipToAddress));
防范.Null(items,nameof(items));
BuyerId=BuyerId;
ShipToAddress=ShipToAddress;
_orderItems=项目;
}
公共字符串BuyerId{get;private set;}
public DateTimeOffset OrderDate{get;private set;}=DateTimeOffset.Now;
公共地址ShipToAddress{get;private set;}
私有只读列表_orderItems=new List();
public IReadOnlyCollection OrderItems=>\u OrderItems.AsReadOnly();
公共十进制总数()
{
var总计=0米;
foreach(orderItems中的变量项)
{
合计+=项目单价*项目单位;
}
返回总数;
}
}
为了将业务模型映射到数据库以持久化数据,只需定义如下所示的相应配置类即可使用EF Core的内置功能。为了将it与业务层分开,it还位于项目的(或数据层)中

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();
        });
    }
}
公共类OrderConfiguration:IEntityTypeConfiguration
{
公共void配置(EntityTypeBuilder)
{
var navigation=builder.Metadata.FindNavigation(nameof(Order.OrderItems));
navigation.SetPropertyAccessMode(PropertyAccessMode.Field);
builder.OwnsOne(o=>o.ShipToAddress,a=>
{
a、 WithOwner();
a、 属性(a=>a.ZipCode)
.HasMaxLength(18)
.IsRequired();
a、 物业(a=>a.Street)
.HasMaxLength(180)
.IsRequired();
a、 属性(a=>a.State)
.HasMaxLength(60);
a、 地产(a=>a.Country)
.HasMaxLength(90)
.IsRequired();
a、 地产(a=>a.City)
.HasMaxLength(100)
.IsRequired();
});
}
}
EF-Core唯一需要的是Order-domain模型类中的私有无参数构造函数,从我的观点来看,这是一个可以接受的折衷方案,考虑到您可以节省编写数据库映射类的工作量

如果我受到其他不提供此类功能的框架的限制,我通常也会采取与您现在类似的方式,但如果手头有EF Core的功能,我建议重新考虑您的方法,尝试一下EF Core配置功能


我知道这不是您面临的技术问题的确切答案,但我想向您展示一种替代方法。

您的问题是,当您从数据库加载实体时,EF Core会在其更改跟踪器中开始跟踪它们,以便在调用SaveChanges()时识别您对加载的实体所做的更改。只要修改EF加载的实际对象,此行为就可以正常工作

您正在做的是:加载DatabaseTrial(假设它具有id 1),然后将其映射到DomainTrial,可能对其进行修改,然后将其映射到DatabaseTrial的新实例,该实例也具有id 1,并将其添加到上下文中。这会混淆EF,因为它现在有两个不同的对象(通过引用),它们都具有id 1。这是不允许的,因为ID必须是唯一的(如果EF没有抛出此EXEP