EF SaveChanges()在localhost上非常快,但在远程数据库上非常慢

时间:2016-11-07 22:08:35

标签: c# entity-framework

我正在尝试使用实体框架插入大量100个实体(及其子元素)。将批量插入本地主机上的数据库大约需要3秒,而尝试在同一内部网上的远程数据库上插入相同批量则需要300秒。

远程数据库与执行SaveChanges(localhost)的计算机之间的连接速度不慢。

我猜它可能与防火墙或远程SQL服务器上的配置有关。我怎么能进一步调查这个问题?

这是代码:

    public void SaveProcessedProperties(object properties)
    {
        var propertiesToPersist = properties as List<Ejendom>;

        using (var db = new TinglysUdv_UdpakEntities())
        {
            db.Configuration.AutoDetectChangesEnabled = false;
            db.Configuration.LazyLoadingEnabled = false;

            try
            {
                foreach (var property in propertiesToPersist)
                {
                    #region Haeftelse
                    if (property.Haeftelse.Any())
                    {
                        foreach (var liability in property.Haeftelse)
                        {
                            if (liability.AttachDocument)
                            {
                                var existingDoc = db.Dokument.Find(liability.DokumentIdentifikator, liability.DokumentRevisionNummer);

                                if (existingDoc == null)
                                {
                                    Dokument dbDoc;
                                    if (DocumentExists(db, liability.DokumentIdentifikator, liability.DokumentRevisionNummer, out dbDoc))
                                    {
                                        existingDoc = dbDoc;
                                    }
                                }

                                if (existingDoc != null)
                                {
                                    liability.Dokument = existingDoc;
                                }
                                else
                                {
                                    var dok = new Dokument()
                                    {
                                        DokumentIdentifikator = liability.DokumentIdentifikator,
                                        DokumentRevisionNummer = liability.DokumentRevisionNummer
                                    };
                                    db.Dokument.Attach(dok);
                                    liability.Dokument = dok;
                                }
                            }
                        }
                    }
                    #endregion

                    #region Adkomst
                    if (property.Adkomst.Any())
                    {
                        foreach (var claim in property.Adkomst)
                        {
                            if (claim.AttachDocument)
                            {
                                var existingDoc = db.Dokument.Find(claim.DokumentIdentifikator, claim.DokumentRevisionNummer);

                                if (existingDoc == null)
                                {
                                    Dokument dbDoc;
                                    if (DocumentExists(db, claim.DokumentIdentifikator, claim.DokumentRevisionNummer, out dbDoc))
                                    {
                                        existingDoc = dbDoc;
                                    }
                                }

                                if (existingDoc != null)
                                {
                                    claim.Dokument = existingDoc;
                                }
                                else
                                {
                                    var dok = new Dokument()
                                    {
                                        DokumentIdentifikator = claim.DokumentIdentifikator,
                                        DokumentRevisionNummer = claim.DokumentRevisionNummer
                                    };
                                    db.Dokument.Attach(dok);
                                    claim.Dokument = dok;
                                }
                            }
                        }
                    }
                    #endregion

                    #region Servitut
                    if (property.Servitut.Any())
                    {
                        foreach (var easement in property.Servitut)
                        {
                            if (easement.AttachDocument)
                            {
                                var existingDoc = db.Dokument.Find(easement.DokumentIdentifikator, easement.DokumentRevisionNummer);

                                if (existingDoc == null)
                                {
                                    Dokument dbDoc;
                                    if (DocumentExists(db, easement.DokumentIdentifikator, easement.DokumentRevisionNummer, out dbDoc))
                                    {
                                        existingDoc = dbDoc;
                                    }
                                }

                                if (existingDoc != null)
                                {
                                    easement.Dokument = existingDoc;
                                }
                                else
                                {
                                    var dok = new Dokument()
                                    {
                                        DokumentIdentifikator = easement.DokumentIdentifikator,
                                        DokumentRevisionNummer = easement.DokumentRevisionNummer
                                    };
                                    db.Dokument.Attach(dok);
                                    easement.Dokument = dok;
                                }
                            }
                        }
                    }
                    #endregion

                    db.Ejendom.Add(property);
                }

                db.SaveChanges();
            }
            catch (Exception ex)
            {
                throw;
            }
        }
    }

private bool DocumentExists(TinglysUdv_UdpakEntities db, Guid documentIdentifier, int documentRevision, out Dokument dbDocument)
{
    var documentExists = false;

    dbDocument = db.Dokument.FirstOrDefault(x => x.DokumentIdentifikator == documentIdentifier && x.DokumentRevisionNummer == documentRevision);

    if (dbDocument != null)
    {
        documentExists = true;
    }

    return documentExists;
}

获得了Entity Framework Profiler的跟踪版本并对代码进行了分析。结果如下:

enter image description here

对于特定的上下文,我收到了以下警告:

EF正在进行的N + 1查询示例:

INSERT [dbo].[Respekt]
       ([RettighedIdentifikator],
        [RespektServitutDato],
        [Resp_Haeft_Fremtid_id])
VALUES ('1425fd57-042a-4649-a344-0f386b59e400' /* @0 - [RettighedIdentifikator] */,
        NULL,
        NULL)

--//////////////////////////////////////////////////

SELECT [Respekt_id]
FROM   [dbo].[Respekt]
WHERE  @@ROWCOUNT > 0
       AND [Respekt_id] = scope_identity()

除了减少我想保存的实体数量之外,我能做些什么吗?

2 个答案:

答案 0 :(得分:0)

为了接近问题所在,我将运行EF从SQL Management Studio生成的SQL。之后,如果需要,您可以运行SQL事件探查器。

您的代码可能会很慢,因为:

  1. 一个很慢的大SQL
  2. EF生成许多单独的SQL语句,每个语句都有点慢。 &lt; - 那是我的猜测
  3. 顺便说一句。在Intranet上ping 29ms非常慢。在ping主要搜索服务提供商时,我得到的不到,而在ping www.stackoverflow.com

    时为5ms

答案 1 :(得分:0)

您的代码通过该行在所有循环中进行了大量数据库调用:

var existingDoc = db.Dokument.Find(liability.DokumentIdentifikator, liability.DokumentRevisionNummer); 在foreach循环中发布,您需要最小化这些调用。

你可以通过在一次调用中检索所有对象而不是在循环中使用它来做到这一点

ex:而不是

foreach (var liability in property.Haeftelse)
                        {
                            if (liability.AttachDocument)
                            {
                                var existingDoc = db.Dokument.Find(liability.DokumentIdentifikator, liability.DokumentRevisionNummer);
}

你可以:

var allLiabilities = db.Dokument.Where(a=> property.Haeftelse.Contains(a)) //you can adjust the condition exactly, ex: check on if AttachDocument = true

您应该将此应用于所有其他循环

这样,每个循环都会有一个查询。

对于SaveChanges,我相信它会为每条记录发出单独的插入或更新声明,检查this question的答案,了解如何批量更新

另一种方法是创建存储过程,将记录列表作为用户定义的表类型传递,并对SQL执行所有这些检查