将项目从dotnet core 2.0迁移到3.1,并在Entity Framework上遇到了一些问题。

huangapple go评论70阅读模式
英文:

Migrating project from dotnet core 2.0 to 3.1 and having some problems on Entity Framework

问题

我正在使用DDD构建项目。因此,更新/添加/删除子实体的唯一方式是通过其父实体,当我使用dotnet core 2.0时,这不是问题,但现在我正在将项目迁移到dotnet core 3.1时,我遇到以下错误:

预期数据库操作将影响1行,但实际上影响了0行。数据可能已在加载实体后被修改或删除。请参阅 http://go.microsoft.com/fwlink/?LinkId=527962 以了解有关理解和处理乐观并发异常的信息。

我有一个如下所示的Client类:

public class Client
{
    // DDD Patterns comment 使用私有集合字段,更适合DDD聚合的封装性,因此Receptions不能直接从“聚合根外部”添加到集合,而只能通过方法ClientAggregateRoot.AddReception()添加,该方法包括行为。
    private readonly List<Reception> _receptions;

    public Client(Guid id, string name, string url, string domainEmail)
    {
        Id = id;
        Name = name;
        Url = url;
        DomainEmail = domainEmail;
        _receptions = new List<Reception>();
    }

    protected Client()
    {
        _receptions = new List<Reception>();
    }

    public string Name { get; private set; }

    public string Url { get; private set; }

    public string DomainEmail { get; private set; }

    public bool IsDeleted { get; private set; }

    public DateTime CreatedAt { get; private set; }

    public DateTime ModifiedAt { get; private set; }

    public IReadOnlyCollection<Reception> Receptions => _receptions;        
}

我有我的Reception类:

public class Reception
{
    public Reception(Guid id, string name, string address, string noteToGuest, string noteToReceptionist,
        string timeZone, Guid clientId)
    {
        Id = id;
        Name = name;
        Address = address;
        ClientId = clientId;
    }

    private Reception()
    {
    }

    public Guid ClientId { get; private set; }

    public string Name { get; private set; }

    public string Address { get; private set; }        
}

这是Client配置文件:

public class ClientEntityTypeConfiguration
    : IEntityTypeConfiguration<Client>
{
    public void Configure(EntityTypeBuilder<Client> builder)
    {
        builder.ToTable("Clients", BeWelcomeContext.CLIENT_SCHEMA);
        builder.HasKey(c => c.Id);            

        var navigation = builder.Metadata.FindNavigation(nameof(Client.Receptions));

        // DDD Patterns comment: Set as field (New since EF 1.1) to access the Receptions
        // collection property through its fields
        navigation.SetPropertyAccessMode(PropertyAccessMode.Field);
    }
}

以及Reception配置文件:

public class ReceptionEntityTypeConfiguration
    : IEntityTypeConfiguration<Reception>
{
    public void Configure(EntityTypeBuilder<Reception> builder)
    {
        builder.ToTable("Receptions", BeWelcomeContext.CLIENT_SCHEMA);
        builder.HasKey(r => r.Id);

        builder.HasOne<Client>()
            .WithMany(r => r.Receptions)
            .IsRequired()
            .HasForeignKey(r => r.ClientId);
    }
}

这在项目迁移之前一直完美运行,我不知道发生了什么,是否需要更改一些配置。

英文:

I'm bulding a project using DDD. So the only way to update/add/delete a child entity is through its parent, this was not a problem when I was using dotnet core 2.0 but now that i'm migrating the project to dotnet core 3.1 I'm getting the following error:

> Database operation expected to affect 1 row(s) but actually affected 0 row(s). Data may have been modified or deleted since entities were loaded. See http://go.microsoft.com/fwlink/?LinkId=527962 for information on understanding and handling optimistic concurrency exceptions.

I have a Client class like that:

public class Client
{
    /// &lt;summary&gt;
    /// DDD Patterns comment Using a private collection field, better for DDD Aggregate&#39;s
    /// encapsulation so Receptions cannot be added from &quot;outside the AggregateRoot&quot; directly to
    /// the collection, but only through the method ClientAggrergateRoot.AddReception() which
    /// includes behaviour.
    /// &lt;/summary&gt;
    private readonly List&lt;Reception&gt; _receptions;

    public Client(Guid id, string name, string url, string domainEmail)
    {
        Id = id;
        Name = name;
        Url = url;
        DomainEmail = domainEmail;
        _receptions = new List&lt;Reception&gt;();
    }

    protected Client()
    {
        _receptions = new List&lt;Reception&gt;();
    }

    public string Name { get; private set; }

    public string Url { get; private set; }

    public string DomainEmail { get; private set; }

    public bool IsDeleted { get; private set; }

    public DateTime CreatedAt { get; private set; }

    public DateTime ModifiedAt { get; private set; }

    public IReadOnlyCollection&lt;Reception&gt; Receptions =&gt; _receptions;        
}

I have my Reception class:

public class Reception
{
    public Reception(Guid id, string name, string address, string noteToGuest, string noteToReceptionst,
        string timeZone, Guid clientId)
    {
        Id = id;
        Name = name;
        Address = address;
        ClientId = clientId;
    }

    private Reception()
    {
    }

    public Guid ClientId { get; private set; }

    public string Name { get; private set; }

    public string Address { get; private set; }        
}

And this is the Client configuration file:

public class ClientEntityTypeConfiguration
    : IEntityTypeConfiguration&lt;Client&gt;
{
    public void Configure(EntityTypeBuilder&lt;Client&gt; builder)
    {
        builder.ToTable(&quot;Clients&quot;, BeWelcomeContext.CLIENT_SCHEMA);
        builder.HasKey(c =&gt; c.Id);            

        var navigation = builder.Metadata.FindNavigation(nameof(Client.Receptions));

        // DDD Patterns comment: Set as field (New since EF 1.1) to acces the Receptions
        // collection property through its fields
        navigation.SetPropertyAccessMode(PropertyAccessMode.Field);
    }
}

And the reception configuration file:

public class ReceptionEntityTypeConfiguration
    : IEntityTypeConfiguration&lt;Reception&gt;
{
    public void Configure(EntityTypeBuilder&lt;Reception&gt; builder)
    {
        builder.ToTable(&quot;Receptions&quot;, BeWelcomeContext.CLIENT_SCHEMA);
        builder.HasKey(r =&gt; r.Id);

        builder.HasOne&lt;Client&gt;()
            .WithMany(r =&gt; r.Receptions)
            .IsRequired()
            .HasForeignKey(r =&gt; r.ClientId);
    }
}

This have worked perfectly until I the project migration, I don't know what is happening, if I have to change some configuration.

答案1

得分: 2

以下是翻译好的内容:

这是dot net 3.0中的一个重大变化。

如果您使用Guid作为键并预先生成它们(我也在做相同的DDD工作),那么Ef现在将其视为已修改的对象而不是新对象。

解决方案是关闭模型的键生成。

上面的链接有详细的解释。
但总结一下,您需要在DbContext的OnModelCreating方法中进行此更改:

modelBuilder
    .Entity<Client>()
    .Property(e => e.Id)
    .ValueGeneratedNever();

如果模型从具有Id属性的某个类派生,那么更好的解决方法可能是使用以下方式:

[DatabaseGenerated(DatabaseGeneratedOption.None)]
public string Id { get; set; }

将此设置在父类上将应用于所有类。

英文:

https://learn.microsoft.com/en-us/ef/core/what-is-new/ef-core-3.0/breaking-changes#detectchanges-honors-store-generated-key-values

This is a breaking change in dot net 3.0.

If you are using Guid key and pregenerating them (I was doing same DDD stuff), then Ef treats it now as modified object rather than new object.

Solution is to turn off Key generating for the model.

The above link has detailed explanation.
But to summarise you need to do this change in the DbContext OnModelCreating

modelBuilder
.Entity&lt;Client&gt;()
.Property(e =&gt; e.Id)
.ValueGeneratedNever();

In case models are deriving from some class like Entity that has Id property, then a better workaround maybe to use,

 [DatabaseGenerated(DatabaseGeneratedOption.None)]
 public string Id { get; set; }

Setting this on parent will apply it all the classes.

答案2

得分: 0

数据库操作预期影响1行记录,但实际影响0行记录。数据可能在加载实体后已被修改或删除。请查看 http://go.microsoft.com/fwlink/?LinkId=527962 以了解有关理解和处理乐观并发异常的信息。

似乎您的数据库阻止了数据的修改。查看您的日志,复制由 EF Core 创建的 SQL 查询,并尝试在您的数据库管理系统中运行它,也许您将获得更详细的错误信息。通常情况下,有一个触发器会阻止执行操作。

英文:

> Database operation expected to affect 1 row(s) but actually affected 0 row(s). Data may have been modified or deleted since entities were loaded. See http://go.microsoft.com/fwlink/?LinkId=527962 for information on understanding and handling optimistic concurrency exceptions.

It seems like your DB prevent to modify the data. Take a look at your logs, copy the SQL query created by EF Core and try to run it in your DBMS, maybe you will get more detailed error. It is usual that there is a trigger that prevents to do the operation.

huangapple
  • 本文由 发表于 2020年1月6日 22:06:01
  • 转载请务必保留本文链接:https://go.coder-hub.com/59613544.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定