Ho cercato molto sul mio problema di prestazioni e ho provato un sacco di cose diverse, ma non riesco a farlo funzionare abbastanza velocemente. Ecco il mio problema per la sua forma più semplice:CodePrimo caricamento 1 genitore collegato a 25.000 bambini è lento
Sto usando l'entità framework 5 e voglio essere in grado di caricare le istanze secondarie di un genitore quando l'utente seleziona quel genitore, quindi non devo tirare l'intero Banca dati. Tuttavia ho avuto problemi di prestazioni con il caricamento pigro dei bambini. Penso che il problema sia il cablaggio delle proprietà di navigazione tra il genitore ed i bambini. Sto anche pensando che dev'essere qualcosa che ho sbagliato perché credo che sia un caso semplice.
Così ho installato un programma per testare un singolo carico pigro per isolare il problema.
Ecco il test:
ho creato una classe genitore e un bambino POCO POCO Class. Il genitore ha n Bambini e Bambino ha 1 genitore. C'è solo un genitore nel database di SQL Server e 25.000 bambini per quel genitore single. Ho provato diversi metodi per caricare questi dati. Ogni volta che carico i figli e il genitore nello stesso DbContext, ci vuole molto tempo. Ma se li carico in diversi DbContexts, si carica molto velocemente. Tuttavia, voglio che tali istanze siano nello stesso DbContext.
Qui è la mia messa a punto di test e tutto il necessario per replicare:
pocos:
public class Parent
{
public int ParentId { get; set; }
public string Name { get; set; }
public virtual List<Child> Childs { get; set; }
}
public class Child
{
public int ChildId { get; set; }
public int ParentId { get; set; }
public string Name { get; set; }
public virtual Parent Parent { get; set; }
}
DbContext:
public class Entities : DbContext
{
public DbSet<Parent> Parents { get; set; }
public DbSet<Child> Childs { get; set; }
}
TSQL script per creare il Datab ase e dei dati:
USE [master]
GO
IF EXISTS(SELECT name FROM sys.databases
WHERE name = 'PerformanceParentChild')
alter database [PerformanceParentChild] set single_user with rollback immediate
DROP DATABASE [PerformanceParentChild]
GO
CREATE DATABASE [PerformanceParentChild]
GO
USE [PerformanceParentChild]
GO
BEGIN TRAN T1;
SET NOCOUNT ON
CREATE TABLE [dbo].[Parents]
(
[ParentId] [int] CONSTRAINT PK_Parents PRIMARY KEY,
[Name] [nvarchar](200) NULL
)
GO
CREATE TABLE [dbo].[Children]
(
[ChildId] [int] CONSTRAINT PK_Children PRIMARY KEY,
[ParentId] [int] NOT NULL,
[Name] [nvarchar](200) NULL
)
GO
INSERT INTO Parents (ParentId, Name)
VALUES (1, 'Parent')
DECLARE @nbChildren int;
DECLARE @childId int;
SET @nbChildren = 25000;
SET @childId = 0;
WHILE @childId < @nbChildren
BEGIN
SET @childId = @childId + 1;
INSERT INTO [dbo].[Children] (ChildId, ParentId, Name)
VALUES (@childId, 1, 'Child #' + convert(nvarchar(5), @childId))
END
CREATE NONCLUSTERED INDEX [IX_ParentId] ON [dbo].[Children]
(
[ParentId] ASC
)
GO
ALTER TABLE [dbo].[Children] ADD CONSTRAINT [FK_Children.Parents_ParentId] FOREIGN KEY([ParentId])
REFERENCES [dbo].[Parents] ([ParentId])
GO
COMMIT TRAN T1;
App.config che contiene la stringa di connessione:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<connectionStrings>
<add
name="Entities"
providerName="System.Data.SqlClient"
connectionString="Server=localhost;Database=PerformanceParentChild;Trusted_Connection=true;"/>
</connectionStrings>
</configuration>
class Test console:
class Program
{
static void Main(string[] args)
{
List<Parent> parents;
List<Child> children;
Entities entities;
DateTime before;
TimeSpan childrenLoadElapsed;
TimeSpan parentLoadElapsed;
using (entities = new Entities())
{
before = DateTime.Now;
parents = entities.Parents.ToList();
parentLoadElapsed = DateTime.Now - before;
System.Diagnostics.Debug.WriteLine("Load only the parent from DbSet:" + parentLoadElapsed.TotalSeconds + " seconds");
}
using (entities = new Entities())
{
before = DateTime.Now;
children = entities.Childs.ToList();
childrenLoadElapsed = DateTime.Now - before;
System.Diagnostics.Debug.WriteLine("Load only the children from DbSet:" + childrenLoadElapsed.TotalSeconds + " seconds");
}
using (entities = new Entities())
{
before = DateTime.Now;
parents = entities.Parents.ToList();
parentLoadElapsed = DateTime.Now - before;
before = DateTime.Now;
children = entities.Childs.ToList();
childrenLoadElapsed = DateTime.Now - before;
System.Diagnostics.Debug.WriteLine("Load the parent from DbSet:" + parentLoadElapsed.TotalSeconds + " seconds" +
", then load the children from DbSet:" + childrenLoadElapsed.TotalSeconds + " seconds");
}
using (entities = new Entities())
{
before = DateTime.Now;
children = entities.Childs.ToList();
childrenLoadElapsed = DateTime.Now - before;
before = DateTime.Now;
parents = entities.Parents.ToList();
parentLoadElapsed = DateTime.Now - before;
System.Diagnostics.Debug.WriteLine("Load the children from DbSet:" + childrenLoadElapsed.TotalSeconds + " seconds" +
", then load the parent from DbSet:" + parentLoadElapsed.TotalSeconds + " seconds");
}
using (entities = new Entities())
{
before = DateTime.Now;
parents = entities.Parents.ToList();
parentLoadElapsed = DateTime.Now - before;
before = DateTime.Now;
children = parents[0].Childs;
childrenLoadElapsed = DateTime.Now - before;
System.Diagnostics.Debug.WriteLine("Load the parent from DbSet:" + parentLoadElapsed.TotalSeconds + " seconds" +
", then load the children from Parent's lazy loaded navigation property:" + childrenLoadElapsed.TotalSeconds + " seconds");
}
using (entities = new Entities())
{
before = DateTime.Now;
parents = entities.Parents.Include(p => p.Childs).ToList();
parentLoadElapsed = DateTime.Now - before;
System.Diagnostics.Debug.WriteLine("Load the parent from DbSet and children from include:" + parentLoadElapsed.TotalSeconds + " seconds");
}
using (entities = new Entities())
{
entities.Configuration.ProxyCreationEnabled = false;
entities.Configuration.AutoDetectChangesEnabled = false;
entities.Configuration.LazyLoadingEnabled = false;
entities.Configuration.ValidateOnSaveEnabled = false;
before = DateTime.Now;
parents = entities.Parents.Include(p => p.Childs).ToList();
parentLoadElapsed = DateTime.Now - before;
System.Diagnostics.Debug.WriteLine("Load the parent from DbSet and children from include:" + parentLoadElapsed.TotalSeconds + " seconds with everything turned off");
}
}
}
Ecco i risultati di tali test di:
Caricare solo il genitore da DbSet: 0,972 secondi
Carica solo i bambini da DbSet: 0,714 sec ondi
carico del genitore da DbSet: 0,001 secondi, quindi caricare i bambini da DbSet: 8,6026 secondi
Caricare i bambini da DbSet: 0,6864 secondi, quindi caricare il genitore da DbSet: 7,5816159 secondi
carico del genitore da DbSet: 0 secondi, quindi caricare i bambini dalla proprietà di navigazione caricato pigro del genitore: secondi 8,5644549
carico del genitore da DbSet e bambini da includono: secondi 8,6428788
Loa d il genitore da DbSet e bambini da includono: secondi 9,1416586 con tutto spenti
Analisi
Ogni volta che il genitore ed i bambini sono nella stessa DbContext, ci vuole molto tempo (9 secondi) collegare tutto. Ho anche provato a disattivare tutto, dalla creazione del proxy al caricamento lento, ma senza alcun risultato. Qualcuno può aiutarmi ?
+1: Ottima domanda e analisi! – Slauma