如果核心,为什么它生成此查询而不是简单的插入? (保存带有byte []的对象图)和相关的性能问题

问题描述 投票:0回答:1

我正在将数据从遗留数据库导入到新项目中,我对EF Core生成的一些SQL感到有点困惑(我在输出视图中查看它,因为它的性能与我预期的相比非常差)。

我的目标是SQL Server,我正在创建具有Image(poco)类型子属性的对象,这里是一个如何在具有这种属性的对象上使用它的示例:

MyObject.Thumbnail = new Image()
                {
                    Name = Thumbnail.GetValue("Name"),
                    OriginalData = ImageData,
                    Data = ImageData,
                    Width = bmp.Width,
                    Height = bmp.Height,
                    Format = ImageFormat.Jpg
                };

我对生成的SQL感到困惑(说实话我不理解它,它是否声明临时表然后合并?为什么?)以及它的性能(用2秒图像插入10个元素需要17秒每个属性,我在运行查询的.net核心进程在同一台机器上本地运行SQL Server,因此没有网络延迟和内部带宽和字节数组,开始时相当小,大多数在300kb范围内) 。

预期的性能将删除一到两个零。

Microsoft.EntityFrameworkCore.Database.Command:Information: Executed DbCommand (17,211ms) [Parameters=[@p0='?' (Size = -1) (DbType = Binary), @p1='?' (Size = 4000), @p2='?' (DbType = Int32), @p3='?' (DbType = Int32), @p4='?' (Size = 4000), @p5='?' (Size = -1) (DbType = Binary), @p6='?' (DbType = Int32), @p7='?' (Size = -1) (DbType = Binary), @p8='?' (Size = 4000), @p9='?' (DbType = Int32), @p10='?' (DbType = Int32), @p11='?' (Size = 4000), @p12='?' (Size = -1) (DbType = Binary), @p13='?' (DbType = Int32), @p14='?' (Size = -1) (DbType = Binary), @p15='?' (Size = 4000), @p16='?' (DbType = Int32), @p17='?' (DbType = Int32), @p18='?' (Size = 4000), @p19='?' (Size = -1) (DbType = Binary), @p20='?' (DbType = Int32), @p21='?' (Size = 8000) (DbType = Binary), @p22='?' (Size = 4000), @p23='?' (DbType = Int32), @p24='?' (DbType = Int32), @p25='?' (Size = 4000), @p26='?' (Size = 8000) (DbType = Binary), @p27='?' (DbType = Int32), @p28='?' (Size = -1) (DbType = Binary), @p29='?' (Size = 4000), @p30='?' (DbType = Int32), @p31='?' (DbType = Int32), @p32='?' (Size = 4000), @p33='?' (Size = -1) (DbType = Binary), @p34='?' (DbType = Int32)], CommandType='Text', CommandTimeout='30']
SET NOCOUNT ON;
DECLARE @inserted0 TABLE ([Id] int, [_Position] [int]);
MERGE [Images] USING (
VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6, 0),
(@p7, @p8, @p9, @p10, @p11, @p12, @p13, 1),
(@p14, @p15, @p16, @p17, @p18, @p19, @p20, 2),
(@p21, @p22, @p23, @p24, @p25, @p26, @p27, 3),
(@p28, @p29, @p30, @p31, @p32, @p33, @p34, 4)) AS i ([Data], [Description], [Format], [Height], [Name], [OriginalData], [Width], _Position) ON 1=0
WHEN NOT MATCHED THEN
INSERT ([Data], [Description], [Format], [Height], [Name], [OriginalData], [Width])
VALUES (i.[Data], i.[Description], i.[Format], i.[Height], i.[Name], i.[OriginalData], i.[Width])
OUTPUT INSERTED.[Id], i._Position
INTO @inserted0;
c# sql-server entity-framework entity-framework-core
1个回答
3
投票

对于SQL Server SaveChanges默认批处理。对于这种情况(加载blob数据),批处理实际上是错误的,因为您不希望在客户端和服务器上绑定大的参数集,然后遍历加载。您需要单插入批处理(或者如果您的blob非常大,SqlClient Streaming,您需要将其下载到ADO.NET)。

您在指示DbContext使用SQL Server的同一行上配置它,例如:

optionsBuilder.UseSqlServer(constr, b => b.MaxBatchSize(1).UseRelationalNulls(true) );

禁用批处理,并选择不生成模拟C#null比较语义的查询。

如果需要有条件地禁用批处理,可以添加在OnConfiguring上读取的DbContext构造函数参数。例如

public class Db : DbContext
{
    bool disableBatching = false;

    public Db() : base()
    {

    }
    public Db(bool disableBatching)
    {
        this.disableBatching = true;
    }

如果你需要说服你的DI容器有时会在禁用批处理的情况下分发DbContexts,你可以为它创建一个新的子类型,因为大多数DI容器都可以处理类型注册。例如:

public class NoBatchingDb : Db
{
    public NoBatchingDb() : base(disableBatching: true) { }
}
© www.soinside.com 2019 - 2024. All rights reserved.