Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/263.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如何提高实体框架代码的性能?_C#_Sql Server_Entity Framework - Fatal编程技术网

C# 如何提高实体框架代码的性能?

C# 如何提高实体框架代码的性能?,c#,sql-server,entity-framework,C#,Sql Server,Entity Framework,在这个项目中,我需要根据时间调用一个外部API。因此,对于一天,我可能需要调用API 24次,每次调用一小时。API结果是一个包含6个字段的XML文件。我需要将这些数据插入表中。平均每小时大约有20000行数据 该表有以下6列: col1, col2, col3, col4, col5, col6 当所有6个列相同时,我们认为行是相同的,并且不应该插入重复。 我正在使用C#和实体框架: foreach (XmlNode node in nodes) { try {

在这个项目中,我需要根据时间调用一个外部API。因此,对于一天,我可能需要调用API 24次,每次调用一小时。API结果是一个包含6个字段的XML文件。我需要将这些数据插入表中。平均每小时大约有20000行数据

该表有以下6列:

col1, col2, col3, col4, col5, col6

当所有6个列相同时,我们认为行是相同的,并且不应该插入重复。

我正在使用C#和实体框架:

foreach (XmlNode node in nodes)
{
    try
    {
        count++;

        CallData data = new CallData();
        ...
        // get all data and set in 'data'

        // check whether in database already                        
        var q = ctx.CallDatas.Where(x => x.col1 == data.col1
                    && x.col2 == data.col2
                    && x.col3 == data.col3
                    && x.col4 == data.col4
                    && x.col5 == data.col5
                    && x.col6 == data.col6
                ).Any();
        if (q)
        {
            // exists in database, skip
            // log info
        }
        else
        {
            string key = $"{data.col1}|{data.col2}|{data.col3}|{data.col4}|{data.col5}|{data.col6}";
            // check whether in current chunk already
            if (dic.ContainsKey(key))
            {
                // in current chunk, skip
                // log info
            }
            else
            {
                // insert
                ctx.CallDatas.Add(data);

                // update dic
                dic.Add(key, true);
            }
        }
    }
    catch (Exception ex)
    {
        // log error
    }
}
Logger.InfoFormat("Saving changes ...");
if (ctx.ChangeTracker.HasChanges())
{
    await ctx.SaveChangesAsync();
}
Logger.InfoFormat("Saving changes ... Done.");
代码运行良好。但是,我们需要使用此代码来运行过去几个月。问题是:代码运行缓慢,因为对于每一行,它都需要检查它是否已经存在

有什么建议可以提高绩效吗


谢谢

在创建上下文或生命周期时,您不会显示代码。我倾向于把你的索引指给你看。如果这些不是主键,那么您可能会看到性能问题。如果您正在进行全表扫描,它将逐渐变慢。尽管如此,有两种不同的方法来处理

EF原生方式:您可以在每个交互上显式地创建一个新连接(避免对所有条目进行更改跟踪,从而减少渐进式的减速)。此外,您的保存是异步的,但您的*Any语句是同步的。如果当前线程正在等待,那么使用异步也可能有助于减轻当前线程的压力

// Start your context scope closer to the data call, as if the look is long 
// running you could be building up tracked changes in the cache, this prevents 
// that situation.
using (YourEntity ctx = new YourEntity())
{
    CallData data = new CallData();
    if (await ctx.CallDatas.Where(x => x.col1 == data.col1
        && x.col2 == data.col2
        && x.col3 == data.col3
        && x.col4 == data.col4
        && x.col5 == data.col5
        && x.col6 == data.col6
        ).AnyAsync()
        )
    { 
        // exists in database, skip
        // log info
    }
    else
    {
        string key = $"{data.col1}|{data.col2}|{data.col3}|{data.col4}|{data.col5}|{data.col6}";
        // check whether in current chunk already
        if (dic.ContainsKey(key))
        {
            // in current chunk, skip
            // log info
        }
        else
        {
            // insert
            ctx.CallDatas.Add(data);
            await ctx.SaveChangesAsync();
            // update dic
            dic.Add(key, true);
        }
    }
}
可选方法:通过存储过程使用批量操作插入数据。20k行很简单,您仍然可以使用实体框架来实现这一点。看

我已经创建了我自己的版本(针对我的特定需求进行了定制),并且发现它运行良好,可以更好地控制批量插入

我用这种思想一次插入10万条记录。我在存储过程中有我的逻辑,用于检查重复项,这给了我更好的控制,并将在线调用减少到0读和1写。假设您的存储过程已优化,则执行此操作只需一两秒钟

不同的方法:

保存所有重复的行-应该非常有效


当您使用表中的数据时,对于所有字段使用不同的.< /p> 。对于原始的、大容量的操作,我会考虑避免EF实体和上下文跟踪,并且仅通过上下文执行SQL:

var sql = $"IF NOT EXISTS(SELECT 1 FROM CallDates WHERE Col1={data.Col1} AND Col2={data.Col2} AND Col3={data.Col3} AND Col4={data.Col4} AND Col5={data.Col5} AND Col6={data.Col6}) INSERT INTO CallDates(Col1,Col2,Col3,Col4,Col5,Col6) VALUES ({data.Col1},{data.Col2},{data.Col3},{data.Col4},{data.Col5},{data.Col6})";
context.Database.ExeculeSqlCommand(sql);

这不需要额外的检查和日志记录,只需要使用重复检测有效地生成SQL

您是否尝试过运行等效的SQL,看看这是否是问题所在?查看是否可以为查询中使用的字段编制索引。是的,这6列有索引。首先,您需要知道问题出在哪里。运行SQL server profiler,看看查询和插入实际需要多长时间。
string key=$“{data.col1}{data.col2}{data.col3}{data.col4}{data.col5}{data.col6}”//检查当前区块中是否已经存在(dic.ContainsKey(key))
此逻辑应该在数据库调用之前。如果有两种方法跳过(昂贵的数据库查找方式,或者廉价的字典查找方式),首先进行廉价的检查。也可以考虑将<代码>批量插入< /代码>放入一个分级表中,然后从该表复制到主表中。大容量插入比逐行插入快得多,它甚至不必是存储过程;您可以在EF中执行任意SQL。请参阅了解,但我还没有看到原始SQL场景中表值参数的工作模型。我也不喜欢代码中的硬编码SQL(所以我倾向于只看存储过程——所以它可能存在)。