Die Verwendung von LINQ .Wählen Sie (), zu werfen in die neue Art ist ZU langsam?
Aktuellen Projekt aus, brach sich den Kopf über dieses problem:
Client-Repository:
public class ClientRepository
{
//Members
private masterDataContext _db;
//Constructor
public ClientRepository()
{
_db = new masterDataContext();
}
public IEnumerable<ClientName> GetCorporateClientNames()
{
return _db.corporate_client_tbs.Select(o => new ClientName { id = o.id, name = o.company_name }).AsEnumerable();
}
public IEnumerable<ClientName> GetRetailClientNames()
{
return _db.retail_client_tbs.Select(o => new ClientName { id = o.id, name = o.name }).AsEnumerable();
}
//Define return type
public class ClientName
{
public int id { get; set; }
public string name { get; set; }
}
}
Nun im Controller habe ich Folgendes:
public ActionResult Index()
{
var _visits = _db.GetAllServiceVisits();
return View(_visits);
}
Dauert ungefähr 4 Sekunden zum laden der Ansicht mit den 200 ungerade Zeilen gegenwärtig.
Möchte ich hinzufügen, dass eine Eigenschaft "client" auf den Besuch Modell enthält den Namen des Kunden.
Der name des Kunden wird aus zwei verschiedenen Tabellen, die holt man sich aus einem der beiden arrays vom Typ "ClientName".
Ist dies die erste Methode, die verwendet LINQ :
public ActionResult Index()
{
private ClientRepository _cr = new ClientRepository();
var _retailclients = _cr.GetRetailClientNames().ToArray();
var _corporateclients = _cr.GetCorporateClientNames().ToArray();
var _visits = _db.GetAllServiceVisits();
var _temp = _visits.Select(o => new ServiceVisitViewModel
{
service_visit = o,
client = (o.client_type ? _corporateclients.Where(p => p.id == o.client_id).First().name : _retailclients.Where(p => p.id == o.client_id).First().name)
}).ToArray();
return View(_temp);
}
Dies ist der zweite Ansatz, die Verwendung plain 'ol C# :
public ActionResult Index()
{
private ClientRepository _cr = new ClientRepository();
var _retailclients = _cr.GetRetailClientNames().ToArray();
var _corporateclients = _cr.GetCorporateClientNames().ToArray();
var _visits = _db.GetAllServiceVisits();
List<ServiceVisitViewModel> _temp = new List<ServiceVisitViewModel>();
foreach (service_visit_tb v in _visits)
{
_temp.Add(new ServiceVisitViewModel { service_visit = v, client = (v.client_type ? _corporateclients.Where(p => p.id == v.client_id).First().name : _retailclients.Where(p => p.id == v.client_id).First().name) });
}
return View(_temp);
}
Der zweite Ansatz ist etwa 8 - 10 mal schneller basierend auf meine tests.
Der einzige Unterschied, den ich sehen kann ist die .Select-Anweisung.
Kann mir bitte jemand sagen ob ich etwas falsch gemacht haben, im ersten Ansatz oder in der alternative, warum der erste Ansatz ist die so !@#$ing langsam?!
Edit:
Die _db.GetAllServiceVisits () - definition ist wie folgt:
public IEnumerable<service_visit_tb> GetAllServiceVisits()
{
var _visits = _db.service_visit_tbs;
return _visits.AsEnumerable();
}
End Edit
Zweiter Edit:
Ich habe entfernt diese Zeile aus jeder Eintrag in der log:
-- Context: SqlProvider(Sql2008) Model: AttributedMetaModel Build: 4.0.30319.1
Rahmen Log ist wie folgt:
//This query is to fetch all the clients of Type One (corresponding to _cr.GetRetailClientNames() )
SELECT [t0].[id], [t0].[name]
FROM [genii].[retail_client_tb] AS [t0]
//This query is to fetch all the clients of Type Two (corresponding to _cr.GetCorporateClientNames() )
SELECT [t0].[id], [t0].[company_name] AS [name]
FROM [genii].[corporate_client_tb] AS [t0]
//This is the main query (loading roughly 250 records) which fetchs all Visits
SELECT [t0].[id], [t0].[client_type], [t0].[client_id], [t0].[machine_type], [t0].[machineID], [t0].[visit_type], [t0].[scheduledon], [t0].[arrivedon], [t0].[completedon], [t0].[reported_problem], [t0].[diagnosed_problem], [t0].[action_taken], [t0].[visit_status], [t0].[engineer_id], [t0].[reference_id], [t0].[addedby], [t0].[addedon], [t0].[modifiedby], [t0].[modifiedon]
FROM [genii].[service_visit_tb] AS [t0]
//These next queries are not being manually called by me, I assume they are being
//called when the Razor view is compiled since I am calling the name value of a linked table as such:
//@item.service_visit.engineer_tb.name
SELECT [t0].[id], [t0].[type]
FROM [genii].[visit_type_tb] AS [t0]
WHERE [t0].[id] = @p0
-- @p0: Input Int (Size = -1; Prec = 0; Scale = 0) [8]
SELECT [t0].[id], [t0].[status]
FROM [genii].[visit_status_tb] AS [t0]
WHERE [t0].[id] = @p0
-- @p0: Input Int (Size = -1; Prec = 0; Scale = 0) [1]
SELECT [t0].[id], [t0].[name]
FROM [genii].[engineer_tb] AS [t0]
WHERE [t0].[id] = @p0
-- @p0: Input Int (Size = -1; Prec = 0; Scale = 0) [3]
SELECT [t0].[id], [t0].[type]
FROM [genii].[visit_type_tb] AS [t0]
WHERE [t0].[id] = @p0
-- @p0: Input Int (Size = -1; Prec = 0; Scale = 0) [11]
SELECT [t0].[id], [t0].[name]
FROM [genii].[engineer_tb] AS [t0]
WHERE [t0].[id] = @p0
-- @p0: Input Int (Size = -1; Prec = 0; Scale = 0) [2]
SELECT [t0].[id], [t0].[type]
FROM [genii].[visit_type_tb] AS [t0]
WHERE [t0].[id] = @p0
-- @p0: Input Int (Size = -1; Prec = 0; Scale = 0) [7]
SELECT [t0].[id], [t0].[type]
FROM [genii].[visit_type_tb] AS [t0]
WHERE [t0].[id] = @p0
-- @p0: Input Int (Size = -1; Prec = 0; Scale = 0) [2]
SELECT [t0].[id], [t0].[type]
FROM [genii].[visit_type_tb] AS [t0]
WHERE [t0].[id] = @p0
-- @p0: Input Int (Size = -1; Prec = 0; Scale = 0) [6]
SELECT [t0].[id], [t0].[type]
FROM [genii].[visit_type_tb] AS [t0]
WHERE [t0].[id] = @p0
-- @p0: Input Int (Size = -1; Prec = 0; Scale = 0) [3]
SELECT [t0].[id], [t0].[name]
FROM [genii].[engineer_tb] AS [t0]
WHERE [t0].[id] = @p0
-- @p0: Input Int (Size = -1; Prec = 0; Scale = 0) [5]
SELECT [t0].[id], [t0].[name]
FROM [genii].[engineer_tb] AS [t0]
WHERE [t0].[id] = @p0
-- @p0: Input Int (Size = -1; Prec = 0; Scale = 0) [4]
SELECT [t0].[id], [t0].[status]
FROM [genii].[visit_status_tb] AS [t0]
WHERE [t0].[id] = @p0
-- @p0: Input Int (Size = -1; Prec = 0; Scale = 0) [8]
SELECT [t0].[id], [t0].[status]
FROM [genii].[visit_status_tb] AS [t0]
WHERE [t0].[id] = @p0
-- @p0: Input Int (Size = -1; Prec = 0; Scale = 0) [2]
Nachtrag zu der Frage: gibt es einen besseren Weg, um ziehen Sie diese Daten? Ich habe immer vermutet (nie überprüft), dass die foreign-key-basierte Daten zugreifen, die LINQ-Kontext mir gab, war so gut wie es geht, aber da diese zusätzlichen Abfragen, ich bin mir nicht mehr so sicher!
Veröffentlichen Geschwindigkeiten für den zweiten Teil der Ausführung später heute (eine lange Wochenende hier in Mumbai, aber wir arbeiten Recht durch)
End Edit
Dritte Bearbeiten
(Ich überlege, Antwort vom webserver zum client, da alle Berechnungen /abrufen /Bindung /etc etc sollte berücksichtigt werden.)
Methode Eins : 6.85 Sekunden (Aufruf aus 3 Tabellen, und führen Sie dann Gießen in den Blick-Modell mit C#)
public IEnumerable<service_visit_tb> GetAllServiceVisits()
{
var _visits = _db.service_visit_tbs;
_db.Log = new DebuggerWriter();
return _visits.AsEnumerable();
}
public ActionResult Index()
{
var _retailclients = _cr.GetRetailClientNames().ToArray();
var _corporateclients = _cr.GetCorporateClientNames().ToArray();
var _visits = _db.GetAllServiceVisits();
List<ServiceVisitViewModel> _temp = new List<ServiceVisitViewModel>();
foreach (service_visit_tb v in _visits)
{
_temp.Add(new ServiceVisitViewModel { service_visit = v, client = (v.client_type ? _corporateclients.Where(p => p.id == v.client_id).First().name : _retailclients.Where(p => p.id == v.client_id).First().name) });
//}
return View(_temp);
}
Methode Zwei : 8.59 Sekunden (Aufruf aus 3 Tabellen, und führen Sie dann Gießen in View-Model mithilfe von LINQ)
public IEnumerable<service_visit_tb> GetAllServiceVisits()
{
var _visits = _db.service_visit_tbs;
_db.Log = new DebuggerWriter();
return _visits.AsEnumerable();
}
public ActionResult Index()
{
var _retailclients = _cr.GetRetailClientNames().ToArray();
var _corporateclients = _cr.GetCorporateClientNames().ToArray();
var _visits = _db.GetAllServiceVisits();
var _temp = _visits.Select(o => new ServiceVisitViewModel
{
service_visit = o,
client = (o.client_type ? _corporateclients.Where(p => p.id == o.client_id).First().name : _retailclients.Where(p => p.id == o.client_id).First().name)
});
return View(_temp);
}
Methode Drei : 5.76 Sekunden (Alles in einer einzigen LINQ - Abfrage ausgeführt, auf die Datenbank)
public IEnumerable<ServiceVisitViewModel> GetAllServiceVisitsNew()
{
var _visits = _db.service_visit_tbs.Select(o => new ServiceVisitViewModel
{
service_visit = o,
client = (o.client_type ? _db.corporate_client_tbs.Where(c=> c.id == o.client_id).First().company_name : _db.retail_client_tbs.Where(c=> c.id == o.client_id).First().name)
});
_db.Log = new DebuggerWriter();
return _visits;
}
public ActionResult Index()
{
var _visits = _db.GetAllServiceVisitsNew();
return View(_visits());
}
Denke, dass entscheidet Ihr. Vielen Dank an alle für die Hilfe. Ich bin die Kennzeichnung Jon als richtige Antwort, da sein Ansatz, alles zu tun, die auf Datenbank-Seite ist das, was gebracht nach Hause das bacon.
Vielen Dank an alle und jeden, der sich die Mühe nahm, um zu Antworten.
End Edit
.Select
selbst sollten nicht alles groß. Solche großen Unterschied gibt, dass die Datenbank-Abfragen unterscheiden. Können SQL Profiler ausführen und prüfen, ob die beiden alternativen erzeugt die gleiche SQL-workload?
InformationsquelleAutor Varun Vohra | 2011-08-13
Du musst angemeldet sein, um einen Kommentar abzugeben.
Den
Select
Aussage macht einen riesigen Unterschied - denn es ändert sich was getan wird, auf die Datenbank. (Ich gehe davon_db.GetAllServiceVisits()
gibt einIQueryable<T>
.)Sind Sie Holen alle privat-und Firmenkunden in den Speicher schon, weil man
ToArray
. IhreSelect
Anruf (ich vermute) dann senden Sie all diese Daten zurück auf die Datenbank, so dass dieSelect
undWhere
durchgeführt werden können, gibt es. Ich vermute, wenn man sich in SQL profiler, es wird ein ziemlich seltsam Abfrage.Wenn Sie Kraft alles getan werden, client-side, es sollte bassically die gleiche wie die Letztgenannte Ansatz. Sie können das leicht tun, mit:
... das bedeutet jedoch, dass Sie abziehst alle drei Tabellen aus der Datenbank zu jeder Zeit, die Sie auf dieser Seite. Das klingt nicht wie ein guter plan für mich.
Jedoch, es wäre besser, wenn Sie alles tun können in der Datenbank ohne Holen alle retail-und corportate Kunden zuerst in den Arbeitsspeicher.
Dass kann so einfach wie das ändern Sie Ihre repository-Methoden zurück
IQueryable<T>
stattIEnumerable<T>
, und entfernen Sie die AufrufeAsEnumerable
.Wenn ich zog alles aus der Datenbank nehmen, war gut über 60 Sekunden zu laufen. Und zur info: der server ist MS SQL 2008, das Netzwerk mit 10 Mbit / s (falls es relevant ist).
Wenn
_visits
ist eine Liste, dann haben Sie bereits packte alles aus der Datenbank, im Grunde. Ich kann nicht sehen, warum mitSelect
wäre langsamer als Schleife...Das ist genau der Grund, warum ich nie dachte, es wäre. Links zu mir, ich hätte nie versucht, jede andere Herangehensweise. Ein Kollege, der arbeitet, in php und ruby war der Blick auf, fragte mich, ob es in der regular c# ohne LINQ (er weiß nicht, LINQ). Also Tat ich das und jetzt bin ich ehrlich gesagt ratlos.
Können Sie sicherstellen, dass nichts auf die Datenbank, nachdem Sie angerufen haben
GetAllServiceVisits
? (Manche setzen die Protokollierung in Ihrem Kontext, so dass Sie sehen können alle Abfragen ausgeführt.) Ist der code, den Sie gezeigt haben, dass genau den code, den Sie ausgeführt haben? Welche code hast du Zeit? (Es wäre gut, verwenden Sie eine Stoppuhr, nur für dieSelect
/foreach
Teil.)InformationsquelleAutor Jon Skeet
Wow. Sie viel Schleifen gibt. Jeder
Where
innerhalb der Schleife landet Schleife ein array ein Element zu finden, mit jeder iteration in der Schleife.Machen Wörterbücher des Kunden, so dass Sie Aussehen, können Sie bis qickly. Das sollte Ihnen eine dramatische Erhöhung der Geschwindigkeit:
InformationsquelleAutor Guffa