哪个适合性能使用Entity Framework Core还是 ADO.NET?

Which is suitable for performance use Entity Framework Core or ADO.NET?

提问人:ahmed abdelaziz 提问时间:10/3/2023 最后编辑:marc_sahmed abdelaziz 更新时间:10/3/2023 访问量:74

问:

我在 Blazor 应用程序服务器端工作,设计 Web API 以与 Razor 页面交互。

我的问题是哪个适合我的方案 - 使用 ADO.NET 还是 Entity Framework Core?

我尝试如下:

[HttpGet]
[Route("GetAllServersDetails")]
public IActionResult GetAllServersDetails()
{
    string query = "";

    query = "select s.ServerID, s.Server_Name as ServerName,isnull(s.ServerIp,'') as ServerIp,s.ServerTypeId,isnull(st.ServerType,'') as ServerType,s.OsTypeId,isnull(os.DetailsName,'')  as OsType,s.HostedZoneId,isnull(hostedzone.DetailsName,'') as HostedZone,s.ServerityId,isnull(serverity.DetailsName,'') as Serverity,s.HostedTypeId,isnull(hostedType.DetailsName,'') as HostedType,isnull(s.ServerRoleId,0) as ServerRoleId,isnull(serverrole.DetailsName,'') as ServerRole,isnull(s.DRRequired,0) as DRRequiredID,isnull(drrequired.DetailsName,'') as DRRequired,isnull(s.Remarks,'') as Remarks,isnull(s.OwnerFileNo,'') as ownerfilenumber, s.IsActive from [dbo].[ServerNames] s with(nolock)
inner join [dbo].[ServerTypes] st with(nolock) on st.ServerTypeId=s.ServerTypeId
left join  Details os with(nolock) on os.ID=s.OsTypeId and os.HeaderId=12
left join  Details hostedzone with(nolock) on hostedzone.ID=s.HostedZoneId and hostedzone.HeaderId=13
left join  Details serverity with(nolock) on serverity.ID=s.ServerityId and serverity.HeaderId=14
left join  Details hostedType with(nolock) on hostedType.ID=s.HostedTypeId and hostedType.HeaderId=15
left join  Details serverrole with(nolock) on serverrole.ID=s.ServerRoleId and serverrole.HeaderId=16
left join  Details drrequired with(nolock) on drrequired.ID=s.DRRequired and drrequired.HeaderId=17";

    try
    {
        ICollection<object> ApplicationsDataValues = new List<object>();

        using (var command = _context.Database.GetDbConnection().CreateCommand())
        {
            command.CommandText = query;
            command.CommandType = CommandType.Text;

            _context.Database.OpenConnection();

            using (var reader = command.ExecuteReader())
            {
                while (reader.Read())
                {
                    ApplicationsDataValues.Add(new
                            {
                                ServerID = reader.GetFieldValue<Int32>(0),
                                ServerName = reader.GetFieldValue<string>(1).ToString(),
                                ServerIp = reader.GetFieldValue<string>(2).ToString(),
                                ServerTypeId = reader.GetFieldValue<Int32>(3),
                                
                                ServerType = reader.GetFieldValue<string>(4).ToString(),
                                OsTypeId = reader.GetFieldValue<Int32>(5),
                                
                                OsType = reader.GetFieldValue<string>(6).ToString(),
                                HostedZoneId = reader.GetFieldValue<Int32>(7),
                                
                                HostedZone = reader.GetFieldValue<string>(8).ToString(),

                                ServerityId = reader.GetFieldValue<Int32>(9),
                                Serverity = reader.GetFieldValue<string>(10).ToString(),

                                HostedTypeId = reader.GetFieldValue<Int32>(11),
                                HostedType = reader.GetFieldValue<string>(12).ToString(),

                                ServerRoleId = reader.GetFieldValue<Int32>(13),
                                ServerRole = reader.GetFieldValue<string>(14).ToString(),

                                DRRequiredID = reader.GetFieldValue<string>(15).ToString(),
                                DRRequired = reader.GetFieldValue<string>(16).ToString(),
                                
                                Remarks = reader.GetFieldValue<string>(17).ToString(),
                                ownerfilenumber = reader.GetFieldValue<string>(18).ToString(),
                                
                                IsActive = reader.GetFieldValue<bool>(19)//.ToString()
                    });
                }
            }
        }

        return StatusCode(200, ApplicationsDataValues); // Get all users   
    }
    catch (Exception e)
    {
        return StatusCode(500, e);
    }
}

那么,在 ADO.NET 中使用数据读取器是最佳选择,还是使用 Entity Framework Core?

一些开发人员告诉我为什么不使用 ADO.NET - 这是旧技术,这不好,因为它。

打开和关闭与每个加载页的连接,Entity Framework Core 不会这样做。

另外,ADO.NET 很容易从 Entity Framework Core 进行黑客攻击吗?这是正确的吗?

使用 Entity Framework Core 会更好吗?我依赖于代码优先技术

上述语句的结果是最多 10000 行。

asp.net-core entity-framework-core ado.net

评论

1赞 marc_s 10/3/2023
ADO.NET 是成熟的技术——但不是过时的“旧”——一点也不。ADO.NET 通常在运行时速度更快,但需要您进行更多编码 - 您需要能够编写高效且安全的 SQL 语句才能运行。EF Core 对开发人员来说要方便得多 - 需要考虑更少的细节,处理大量需要为 ADO.NET 编写的“粘合代码” - 因此从程序员的工作效率来看,它的效率更高 - 但它是 ADO.NET 之上的一层,因此通常速度较慢

答:

0赞 Neil W 10/3/2023 #1

无论如何,EF 在引擎盖下使用 ADO.Net。

但是,如果指定 SQL 语句并直接使用 ADO.Net 执行,则不会涉及 ORM(如 EF)的开销。

使用 EF 时,它需要解释 LINQ 查询并转换为 SQL。此外,默认情况下,EF 将跟踪实体,这涉及为返回的数据设置代理。当使用 EF 检索不会编辑的数据时,可以使用 AsNoTracking() 轻松覆盖此值。

但是,在将 LINQ 转换为 SQL 所花费的时间方面,需要权衡的是 EF 确实使编码变得更加容易。最好的方法是直接使用 ADO.Net 并使用 EF 对结果进行基准测试,然后确定性能的权衡是否值得代码的简单性。

因此,您的问题的答案是“这取决于您想要优先考虑的内容、性能或编码简单性”。

您还可以将 Dapper 视为一个轻量级 ORM,它在某种程度上简化了对 SQL 查询返回的数据的处理。

0赞 Steve Py 10/3/2023 #2

使用 ORM 的参数通常涉及多个用例。例如,如果您只想从数据库中读取数据块,那么像 EF 这样的 ORM 几乎可以肯定是矫枉过正。EF 提供的是一个数据层,用于读取数据、跟踪更改以及将数据转换为最适合代码需求的对象模型。其优点包括最大限度地减少需要使用的代码/SQL 数量,以及提供一些保护措施,例如参数化以防止 SQL 注入之类的事情,您需要确保自己使用 ADO 保持一致。

使用像 EF 这样的 ORM 的缺点是,虽然它非常强大,并且可以产生与你自己编写的查询一样高效的查询,但这需要时间来了解它的功能。如果使用不当,如果您开始被延迟加载绊倒,甚至滥用预先加载而不是投影进行读取操作,可能会导致一些绝对糟糕的性能问题。它也可能看起来很混乱,如果你忽略了导航属性等内容,它就会像 ADO/SQL 一样涉及大量的手动编码。

因此,在正确完成 EF 的情况下,您的上述代码会是什么样子:

[HttpGet]
[Route("GetAllServersDetails")]
public IActionResult GetAllServersDetails()
{
    ICollection<object> ApplicationsDataValues = new List<object>();
    using var context = new AppDbContext(); // or better, dependency inject the DbContext in.

    try
    {
        var applicationDataValues = context.ServerNames
            .Select(x => new 
            {
                x.ServerId, 
                x.ServerName, // Entity can define ServerName with a DB Column name of Server_Name
                ServerIp = x.ServerIp ?? "",
                SeverType = x.ServerType.ServerType ?? "",
                OsType = x.OsType.DetailsName ?? "", 
                HostedZone =  x.HostedZone.DetailsName ?? "", 
                Severity = x.Serverity.DetailsName ?? "",  
                HostedType = x.HostedType.DetailsName ?? "",
                ServerRole = x.ServerRole.DetailsName ?? "",
                DRRequired = x.DRRequired.DetailsName ?? "",
                Remarks = x.Remarks ?? "",
                OwnerFileNumber = x.OwnerFileNo ?? "", 
                x.IsActive 
            }).ToList();

        return StatusCode(200, applicationsDataValues); // Get all users   
    }
    catch (Exception e)
    {
        return StatusCode(500, e);
    }
         
}

这假定数据库已正确规范化,并且实体已使用关系的导航属性进行设置。从原始 SQL 中,尚不清楚 HeaderId=# 在每个连接中的意义是什么。通常,在多对一中,FK 足以将行相互关联。

上面的查询使用投影来告知 EF 你希望使用实体定义来生成 SQL 语句,以便仅检索请求的数据。这与人们首次了解 EF 时的默认 go-to 行为不同,后者正在加载和跟踪实体本身的实例。使用投影,您无需担心延迟加载和急切加载成本等问题,它是跨相关实体进行只读操作的高性能选项。