FreeSql icon indicating copy to clipboard operation
FreeSql copied to clipboard

添加ToAsyncEnumerable返回异步流功能

Open ROMYIM opened this issue 1 year ago • 5 comments

Feature 特性

public class FreeSqlAsyncEnumerable<T>(DbDataReader reader) : IAsyncEnumerable<T>
{
    public IAsyncEnumerator<T> GetAsyncEnumerator(CancellationToken cancellationToken = new CancellationToken())
    {
        return new FreeSqlAsyncEnumerator<T>(reader, cancellationToken);
    }
}

public class FreeSqlAsyncEnumerator<T>(DbDataReader reader, CancellationToken cancellationToken) : IAsyncEnumerator<T>
{
    public T Current => (T) Utils.ExecuteArrayRowReadClassOrTuple(flag, typeof(T), indexes, reader, 0, _util).Value;

    public ValueTask<bool> MoveNextAsync()
    {
        return new ValueTask<bool>(reader.ReadAsync(cancellationToken));
    }

    public async ValueTask DisposeAsync()
    {
        await reader.CloseAsync();
        await reader.DisposeAsync();
    }
}

简要描述原因

针对大数据导出和后续处理非常有用。ToChunk和ToChunkAsync只支持传递委托进去,使得代码耦合。而且没有数据返回。 返回IAsyncEnumberable接口,可以把查询和后续数据处理分离出来,代码不耦合。而且还可以返回数据。其次避免ToListAsync中List扩容的数据复制带来的开销。

使用场景

freeSql.Select<T>.ToAsyncEnumerable(cancellationToken);
freeSql.Select<T>.ToAsyncEnumerable(t => new T2()
{
    Id = t.Id
}, cancellationToken);
freeSql.Select<T1, T2>.ToAsyncEnumerable((t1, t2) => new T3()
{
    Id = t1.Id,
    Name = t2.Name
}, cancellationToken);

ROMYIM avatar Feb 27 '25 02:02 ROMYIM

之前是考虑到使用者,返回的对象不一定 Disponse,然后把锅甩到 orm 身上。

2881099 avatar Feb 27 '25 03:02 2881099

之前是考虑到使用者,返回的对象不一定 Disponse,然后把锅甩到 orm 身上。

可以改造一下MoveNextAsync方法。当返回false的时候立马Dispose掉。这样的话在流读取完后就立即释放掉。 因为EfCore本来就有提供这个接口方法,所以我希望FreeSql也能实现。

public class FreeSqlAsyncEnumerator<T>(DbDataReader reader, CancellationToken cancellationToken) : IAsyncEnumerator<T>
{
    public T Current => (T) Utils.ExecuteArrayRowReadClassOrTuple(flag, typeof(T), indexes, reader, 0, _util).Value;

    public async ValueTask<bool> MoveNextAsync()
    {
        var result = await reader.ReadAsync(cancellationToken);
        if (!result)  await DiposeAsync();
        return result;
    }

    public async ValueTask DisposeAsync()
    {
        await reader.CloseAsync();
        await reader.DisposeAsync();
    }
}

ROMYIM avatar Feb 27 '25 07:02 ROMYIM

@2881099 这功能可以实现吗

ROMYIM avatar Mar 11 '25 01:03 ROMYIM

暂时没时间,上次尝试过一次,改动较大

2881099 avatar Mar 11 '25 04:03 2881099

大佬有空试下。

await foreach (var items in fsql.Select<User1>().ToChunkAsyncEnumerable(10))
{
    foreach (var item in items)
        Console.WriteLine(item.Nickname);
}

2881099 avatar Apr 24 '25 11:04 2881099