- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
偶然发现了一段相对常用的代码,起初看起来效率很低。 (我知道有时优化可能是邪恶的,但我想知道)
介绍部分 - 相当简单的 SP 执行 + 读取返回的数据:
try
{
await connection.OpenAsync();
using (var command = connection.CreateCommand())
{
command.CommandText = sql.ToString();
command.Parameters.AddRange(sqlParameters.ToArray());
var reader = await command.ExecuteReaderAsync();
if (reader.HasRows)
{
while (await reader.ReadAsync())
{
var item = await GetProjectElement(reader);
list.Add(item);
}
}
reader.Dispose();
}
}
finally
{
connection.Close();
}
我担心的是功能
await GetProjectElement(reader)
private async Task<Project> GetProjectElement(DbDataReader reader)
{
var item = new Project
{
Id = await reader.GetFieldValueAsync<int>(1),
ParentId = await reader.IsDBNullAsync(2) ? default(int?) : await reader.GetFieldValueAsync<int>(2),
Name = await reader.IsDBNullAsync(3) ? default(string) : await reader.GetFieldValueAsync<string>(3),
Description = await reader.IsDBNullAsync(4) ? default(string) : await reader.GetFieldValueAsync<string>(4),
Address = await reader.IsDBNullAsync(5) ? default(string) : await reader.GetFieldValueAsync<string>(5),
City = await reader.IsDBNullAsync(6) ? default(string) : await reader.GetFieldValueAsync<string>(6),
PostalCode = await reader.IsDBNullAsync(7) ? default(string) : await reader.GetFieldValueAsync<string>(7),
Type = (ProjectTypeEnum)(await reader.GetFieldValueAsync<byte>(8)),
StartDate = await reader.IsDBNullAsync(9) ? default(DateTime?) : await reader.GetFieldValueAsync<DateTime>(9),
EstimatedEndDate = await reader.IsDBNullAsync(10) ? default(DateTime?) : await reader.GetFieldValueAsync<DateTime>(10),
ActualEndDate = await reader.IsDBNullAsync(11) ? default(DateTime?) : await reader.GetFieldValueAsync<DateTime>(11),
WebsiteUrl = await reader.IsDBNullAsync(12) ? default(string) : await reader.GetFieldValueAsync<string>(12),
Email = await reader.IsDBNullAsync(13) ? default(string) : await reader.GetFieldValueAsync<string>(13),
PhoneNumber = await reader.IsDBNullAsync(14) ? default(string) : await reader.GetFieldValueAsync<string>(14),
MobilePhoneNumber = await reader.IsDBNullAsync(15) ? default(string) : await reader.GetFieldValueAsync<string>(15),
Key = await reader.IsDBNullAsync(16) ? default(Guid?) : await reader.GetFieldValueAsync<Guid>(16),
OrganizationElementId = await reader.GetFieldValueAsync<int>(17),
CompanyOrganizationElementId = await reader.IsDBNullAsync(18) ? default(int?) : await reader.GetFieldValueAsync<int>(18),
IsArchived = await reader.GetFieldValueAsync<bool>(20),
IsDeleted = await reader.GetFieldValueAsync<bool>(21),
CreatedOn = await reader.GetFieldValueAsync<DateTime>(22),
CreatedBy = await reader.GetFieldValueAsync<string>(23),
ModifiedOn = await reader.IsDBNullAsync(24) ? default(DateTime?) : await reader.GetFieldValueAsync<DateTime>(24),
ModifiedBy = await reader.IsDBNullAsync(25) ? default(string) : await reader.GetFieldValueAsync<string>(25)
};
return item;
}
如您所见,有很多等待调用被编译器转换为状态机,不是吗?
您可以找到编译器生成代码的简化版本 here .大量 GOTO,这意味着上下文会一遍又一遍地切换。
由于 SP 是在没有指定 CommandBehavior 的情况下执行的 - 数据将处于非顺序模式。 (可能的原因是对于 Project
link 的这种情况,表格行的字节数不应该很大)
我的问题是:
1) 这是在没有明显原因的情况下滥用 async/await,因为行数据已经缓冲在内存中,对吗?
2) 是 Task<Project>
在这种情况下是纯开销吗?
3) 与没有 await
的方法相比,这种方法的性能实际上会更差吗?正在
最后的想法:如果我做对了,我们会想对内容可能超过合理长度的大表行使用 CommandBehavior.SequentialAccess,从而使我们想要异步读取它? (比如存储 varbinary(max) 或 blob)
最佳答案
正如其他人所指出的,GOTO 不会导致上下文切换,而且速度非常快。
1) is this abusing of the async/await without an obvious reason, because the row data is already buffered in memory, right?
ADO.NET 允许实现者在实现基类型的确切方式上有很大的余地。也许该行在内存中,也许不在。
2) is Task a pure overhead in this scenario?
是的,如果操作实际上是同步的。这是您的 ADO.NET 提供程序的实现细节。
注意状态机和await
这里几乎不增加任何开销;有一个async fast path如果可能,代码只是保持同步执行。
3) would this approach actually have a worse performance compared to one without awaiting
可能不会。首先,性能影响不会受到调用每个方法并继续同步执行所完成的少量 CPU 工作的驱动。您看到的任何性能影响都是由于额外的 Task<T>
实例扔到 Gen0 堆上,必须进行垃圾回收。这就是现在有 ValueTask<T>
的原因.
但即使是这种性能影响也很可能在对数据库服务器的网络 I/O 调用旁边不明显。也就是说,如果您想了解微观性能损失,Zen of Async是经典。
关于C# 异步/等待读取 DbDataReader 的效率(或滥用),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54217004/
有谁知道 DbDataReaders 是如何工作的。我们可以以SqlDataReader为例。 当您执行以下操作时 cmd.CommandText = "SELECT * FROM Customers
有什么巧妙的方法可以简化这种下流话吗?它有效,但不可读... int? myVal = r.IsDBNull(r.GetOrdinal("colName")) ? (int?)null : r.Get
根据 MSDN , System.Data.SqlClient支持从 DataTable 填充表值参数, DbDataReader或 IEnumerable对象。 我编写了以下代码,使用 IEnume
这个问题是我的 previous 的延续一。无需过多介绍,我将使用 2 个相关的一对多表填充数据集。所以,我现在的问题是 - 为什么这段代码运行良好 public DataAgencyR_DataSe
如何让dr.Read();在满足条件的情况下重新从头开始阅读? 类似于: SqlDataReader dr = command.ExecuteReader(); for(int i=0; dr.Rea
在下面的代码中,command是一个已经设置好的DbCommand: using( var dataReader = command.ExecuteReader() /*The actual exec
考虑下面的 FetchData 方法。它旨在避免每次您想从数据库中获取一些数据时重复数据库查询代码。 List myData = new List { myData.Add(new
我在许多示例中看到了 IDataReader 而不是 DbDataReader 的用法(以及其他 ADO.NET 接口(interface)而不是其相应类的用法)。 我知道 IDataReader
我有很多使用原始 ADO.NET(DbConnection、DbDataReader 等)的现有代码。我想过渡到对新代码使用 LINQ to SQL,但现在将现有代码和新代码都放在一组统一的 Repo
我试图让 Entity Framework 返回自定义查询的多个结果,然后将其映射回 Entity Framework 代码优先模型。是否可以使用Entity Framework的内部映射引擎? 我可
如何转换 List personList至 DBDataReader ? 在我下面给出的代码中,我试图批量插入 personList .我有大约 50 万条记录,方法 WriteToServer需要一
我目前正在实现一个可以从不同数据库读取数据的小软件。以下是代码: interface Fetch { Dictionary GetData(); } abstract class Conn :
偶然发现了一段相对常用的代码,起初看起来效率很低。 (我知道有时优化可能是邪恶的,但我想知道) 介绍部分 - 相当简单的 SP 执行 + 读取返回的数据: try { await connec
我有一个简单的应用程序,需要执行某些查询来获取数据库架构信息。我写了一个简单的方法来执行查询并返回一个阅读器,就像这样 - public static DbDataReader ExecuteQuer
我有以下函数,它允许我传入一个对象并用返回数据(如果有)填充该对象。 我修改了该函数,使其可以异步调用。 public static async Task MySQLReturnReader(stri
我的属性绑定(bind)数据访问类有一个小问题(更像是一个烦恼)。问题是当类中相应属性的读取器中不存在列时映射失败。 代码 这是映射器类: // Map our datareader object t
我在 MS reference 查看 DbDataReader(也称为 DbCommand)的代码并且无法弄清楚 ReadAsync() 方法中的异步是什么。 virtual public T
您好,我正在尝试从一个方法中返回 DataReader,但它返回一个关闭的 DbDataReader 对象。解决这个问题的任何想法。我愿意接受任何改进代码的建议。 谢谢 更新我不想让数据库连接保持打开
我通过以下方式调用 EF Core 2.0 中的存储过程。 private async Task> InvokeStoredProcedureAsync(string entityName) {
我是一名优秀的程序员,十分优秀!