2012-09-02 7 views
7

Konsekwentnie uzyskiwanie wyników pokazano na poniższym wykresie. Zastanawiam się, czy jest coś, co mogę zrobić, aby zwiększyć przewidywalność. Nie używam SqlBulkCopy, ponieważ muszę wykorzystać możliwości sprawdzania poprawności EFV5.Dlaczego EF5 wytwarza te skoki w czasach SaveChanges()?

Byłoby wspaniale, gdyby ktoś mógł zweryfikować/obalić moje wyniki. Moim celem jest pozbycie się obu rodzajów kolców. Dostarczam kod źródłowy poniżej, aby był szybki dla Ciebie. Wszystko czego potrzebujesz to projekt biblioteki klas w VS, z odniesieniami do EFv5 i NUnit, oba dostępne przez NuGet. Po prostu wklej ten kod w Class1, zmodyfikuj ciąg połączenia i uruchom go. Możesz użyć skryptu sql poniżej, aby odtworzyć tabelę.

Używam .Net 4.5, EF 5, NUnit 2.6.1, uruchamiam kod w trybie Release, bez dołączonego debuggera. Baza danych to SqlServer 2008 R2. Uruchomę test z NUnit.exe w trybie 64-bitowym, który pokazuje "Net 4.0" jako wersję szkieletową.

EFv5 simple entity insert. 1000 batches of 100 entities

Oś X oznacza numer serii (1000 partie łącznie), a oś Y oznacza milisekund. Pierwsza partia zajmuje około 30 sekund, co jest oczekiwane, ponieważ dbContext jest "zimny". Każda partia zapisuje 100 elementów.

Należy pamiętać, że to pytanie szuka trochę informacji brakuje w this answer, to jestfluktuacji EF w zapisie.

Oto kod używam:

tabela:

CREATE TABLE [dbo].[Entity1](
    [Id] [int] IDENTITY(1,1) NOT NULL, 
    [IntField] [int] NOT NULL, 
    [StrField] [nvarchar](50) NOT NULL, 
    [DateField] [datetimeoffset](7) NOT NULL, 
CONSTRAINT [PK_Entity1] PRIMARY KEY CLUSTERED 
(
    [Id] ASC 
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, 
    ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] 
) ON [PRIMARY] 

Klasy:

using System; 
using System.Collections.Generic; 
using System.Data.Entity; 
using System.Diagnostics; 
using NUnit.Framework; 

namespace ClassLibrary1 
{ 
    public class Entity1 
    { 
     public int Id { get; protected set; } 
     public int IntField { get; set; } 
     public string StrField { get; set; } 
     public DateTimeOffset DateField { get; set; } 
    } 

    public class MyContext : DbContext 
    { 
     public MyContext(string connStr) : base(connStr) { } 
     public virtual DbSet<Entity1> Entities { get { return base.Set<Entity1>(); } } 
    } 

    [TestFixture] 
    public class Class1 
    { 
     [Test] 
     public void EfPerf() 
     { 
      var entities = buildEntities(100000); 
      int batchSize = 100; 
      var batchTimes = new List<Stopwatch>(); 

      for (int i = 0; i < entities.Length; i += batchSize) 
      { 
       var sw = Stopwatch.StartNew(); 
       using (var ctx = buildCtx()) 
       { 
        for (int j = i; j < i + batchSize; j++) 
         ctx.Entities.Add(entities[j]); 
        ctx.SaveChanges(); 
       } 
       sw.Stop(); 
       batchTimes.Add(sw); 
      } 

      batchTimes.ForEach(sw => Console.Out.WriteLine("Elapsed ms: " + 
       sw.ElapsedMilliseconds)); 
     } 

     private MyContext buildCtx() 
     { 
      var cs = "Data Source=your db server;" + 
        "Initial Catalog=your db;" + 
        "Persist Security Info=True;" + 
        "User ID=your user;" + 
        "Password=your pwd"; 
      var ctx = new MyContext(cs); 
      //ctx.Configuration.ProxyCreationEnabled = false; 
      return ctx; 
     } 

     private Entity1[] buildEntities(int count) 
     { 
      var entities = new Entity1[count]; 
      for (int i = 0; i < count; i++) 
       entities[i] = new Entity1 { IntField = i, StrField = "str" + i, 
        DateField = DateTimeOffset.UtcNow }; 
      return entities; 
     } 
    } 
} 
+0

Czy możesz wyolbrzymić problem, zwiększając rozmiar partii? To może pozwolić ci włamać się do debuggera. Możesz także uruchomić timer po każdym wywołaniu wsadowym Debugger.Break po 8s do wsadu. – usr

+0

Uruchom także program SQL Profiler, aby sprawdzić, czy skoki występują na poziomie SQL. Spójrz na kolumnę duration. – usr

+1

Celowo nie dołączam debuggera, więc mogę się upewnić, że to nie jest czynnik. Ponadto uruchomiłem Sql Profiler, a wszystkie wstawki trwają "0", a tylko jeden na kilkudziesięciu zajmuje więcej niż "0" procesora (i tylko około "15" w tym przypadku). – esegura

Odpowiedz

1

Mam wrażenie, że są uruchomione do problemu z deadlocking w twój DB. EF umieszcza wszystkie transakcje saveChanges() w transakcji. Domyślnie transakcje w SQL Serverze działają jako Read Committed, co jest bardzo restrykcyjne. Być może możesz spróbować zmienić poziom izolacji w następujący sposób:

using (var scope = new TransactionScope(TransactionScopeOption.Required, new 
2: TransactionOptions { IsolationLevel= IsolationLevel.Snapshot })) 
3: { 
4: // do something with EF here 
5: scope.Complete(); 
6: } 
Powiązane problemy