Skip to content

使用 IBaseRepository.Update 批量更新由 IncludeMany 加载的子项时出现异常 #2197

@shine1258

Description

@shine1258

问题描述及重现代码:

使用 IBaseRepository.Update 批量更新由 IncludeMany 加载的子项时出现异常

using FreeSql;
using FreeSql.DataAnnotations;

var freeSql = new FreeSqlBuilder()
    .UseConnectionString(DataType.Sqlite, "Data Source=:memory:")
    .UseMonitorCommand(cmd => Console.WriteLine(cmd.CommandText))
    .Build();

await freeSql.Ado.ExecuteNonQueryAsync(
    """
    CREATE TABLE Foo (
        Id   INTEGER PRIMARY KEY AUTOINCREMENT,
        Name TEXT NOT NULL
    );

    CREATE TABLE Bar (
        Id    INTEGER PRIMARY KEY AUTOINCREMENT,
        FooId INTEGER NOT NULL,
        Value TEXT,

        FOREIGN KEY (FooId) REFERENCES Foo(Id)
            ON DELETE CASCADE
            ON UPDATE CASCADE
    );

    INSERT INTO Foo (Name) VALUES ('Foo A');
    INSERT INTO Foo (Name) VALUES ('Foo B');
    INSERT INTO Bar (FooId, Value) VALUES (1, 'Bar 1');
    INSERT INTO Bar (FooId, Value) VALUES (1, 'Bar 2');
    INSERT INTO Bar (FooId, Value) VALUES (2, 'Bar 3');
    """
);

var fooRepo = freeSql.GetRepository<Foo>();
var barRepo = freeSql.GetRepository<Bar>();

// IncludeMany 一对多并批量更新子项导致 System.Exception: FreeSql: Not updatable,
// data not tracked, should be queried first or Attach 异常
var foos = await fooRepo.Select.IncludeMany(f => f.Bars).ToListAsync();
var bars = foos[0].Bars;
bars.ForEach(b => b.FooId = 2);
await barRepo.UpdateAsync(bars); // <-- Exception here!

// 循环逐一更新子项无问题
// var foos = await fooRepo.Select.IncludeMany(f => f.Bars).ToListAsync();
// var bars = foos[0].Bars;
// bars.ForEach(b => b.FooId = 2);
// foreach (var bar in bars)
//     await barRepo.UpdateAsync(bar);

// 手动加载子项再批量更新也无问题
// var foos = await fooRepo.Select.ToListAsync();
// foreach (var foo in foos)
//     foo.Bars = await barRepo.Where(b => b.FooId == foo.Id).ToListAsync();
//
// var bars = foos[0].Bars;
// bars.ForEach(b => b.FooId = 2);
// await barRepo.UpdateAsync(bars);

public class Foo
{
    [Column(IsPrimary = true, IsIdentity = true)]
    public int Id { get; set; }

    public string Name { get; set; } = string.Empty;

    [Navigate(nameof(Bar.FooId))]
    public List<Bar> Bars { get; set; } = [];
}

public class Bar
{
    [Column(IsPrimary = true, IsIdentity = true)]
    public int Id { get; set; }

    [Navigate(nameof(FooId))]
    public Foo? Foo { get; set; }

    public int FooId { get; set; }

    public string Value { get; set; } = string.Empty;
}

异常信息

Unhandled exception. System.Exception: FreeSql: Not updatable, data not tracked, should be queried first or Attach:(1, 2, Bar 1)
   at FreeSql.DbSet`1.CanUpdate(TEntity data, Boolean isThrow)
   at FreeSql.DbSet`1.CanUpdate(IEnumerable`1 data, Boolean isThrow)
   at FreeSql.DbSet`1.UpdateRangePriv(IEnumerable`1 data, Boolean isCheck)
   at FreeSql.DbSet`1.UpdateRange(IEnumerable`1 data)
   at FreeSql.BaseRepository`1.UpdateAsync(IEnumerable`1 entitys, CancellationToken cancellationToken)
   at Program.<Main>$(String[] args) in D:\Repositories\FreeSqlIssues\Program.cs:line 44
   at Program.<Main>(String[] args)

查看异常提示需要先 Attach, 在 Update 前调用 barRepo.Attach(bars) 能解决这个问题

数据库版本

    <PackageReference Include="Microsoft.Data.Sqlite" Version="10.0.2" />

安装的Nuget包

    <PackageReference Include="FreeSql.Provider.SqliteCore" Version="3.5.305" />
    <PackageReference Include="FreeSql.Repository" Version="3.5.305" />

.net framework/. net core? 及具体版本

<TargetFramework>net10.0</TargetFramework>

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions