Dit antwoord richt zich voornamelijk op 'select' vs update/create/delete operaties. Ik denk dat het zeldzamer is om meer dan één of een paar records tegelijk bij te werken, en daarom denk ik ook dat 'selecteren' is waar de knelpunten zich meestal voordoen. Dat gezegd hebbende, moet u uw toepassing (profiel) kennen. De beste plaats om uw optimalisatietijd te besteden, is bijna altijd op databaseniveau in de query's zelf, in plaats van in de clientcode. De klantcode is alleen maar het loodgieterswerk:het is niet de belangrijkste kracht van uw app. Omdat sanitair echter vaak in veel verschillende apps wordt hergebruikt, sympathiseer ik met de wens om het zo optimaal mogelijk te krijgen, en daarom heb ik genoeg te zeggen over hoe die code te bouwen.
Ik heb een generieke methode voor het selecteren van query's/procedures in mijn gegevenslaag die er ongeveer zo uitziet:
private static IEnumerable<IDataRecord> Retrieve(string sql, Action<SqlParameterCollection> addParameters)
{
//ConnectionString is a private static property in the data layer
// You can implement it to read from a config file or elsewhere
using (var cn = new SqlConnection(ConnectionString))
using (var cmd = new SqlCommand(sql, cn))
{
addParameters(cmd.Parameters);
cn.Open();
using (var rdr = cmd.ExecuteReader())
{
while (rdr.Read())
yield return rdr;
rdr.Close();
}
}
}
En daardoor kan ik openbare gegevenslaagmethoden schrijven die anonieme methoden gebruiken om de parameters toe te voegen. De getoonde code werkt met .Net 2.0+, maar kan nog korter worden geschreven met .Net 3.5:
public IEnumerable<IDataRecord> GetFooChildrenByParentID(int ParentID)
{
//I could easily use a stored procedure name instead of a full sql query
return Retrieve(
@"SELECT c.*
FROM [ParentTable] p
INNER JOIN [ChildTable] c ON c.ParentID = f.ID
WHERE f.ID= @ParentID", delegate(SqlParameterCollection p)
{
p.Add("@ParentID", SqlDbType.Int).Value = ParentID;
}
);
}
Ik stop hier, zodat ik je nogmaals kan wijzen op de code net erboven die de anonieme methode gebruikt voor het maken van parameters.
Dit is zeer zuivere code, in die zin dat het de querydefinitie en het maken van parameters op dezelfde plaats plaatst, terwijl u toch de standaarddatabaseverbinding/aanroepcode kunt abstraheren naar een meer herbruikbare plaats. Ik denk niet dat deze techniek wordt gedekt door een van de opsommingstekens in uw vraag, en het is ook behoorlijk verdomd snel. Ik denk dat dit ongeveer de strekking van je vraag dekt.
Ik wil echter doorgaan om uit te leggen hoe dit allemaal in elkaar steekt. De rest is vrij eenvoudig, maar het is ook gemakkelijk om dit op een lijst of iets dergelijks te gooien en dingen verkeerd te doen, wat uiteindelijk de prestaties schaadt. Dus verder, de bedrijfslaag gebruikt vervolgens een fabriek om queryresultaten naar objecten te vertalen (c# 3.0 of hoger):
public class Foo
{
//various normal properties and methods go here
public static Foo FooFactory(IDataRecord record)
{
return new Foo
{
Property1 = record[0],
Property2 = record[1]
//...
};
}
}
In plaats van deze live in hun klasse te hebben, zou je ze ook allemaal kunnen groeperen in een statische klasse die specifiek bedoeld is om de fabrieksmethoden te bevatten.
Ik moet één wijziging aanbrengen in de oorspronkelijke ophaalmethode. Die methode "levert" hetzelfde object steeds weer op, en dit werkt niet altijd even goed. Wat we anders willen doen om het te laten werken, is een kopie te forceren van het object dat wordt vertegenwoordigd door het huidige record, zodat wanneer de lezer muteert voor het volgende record, we met schone gegevens werken. Ik heb gewacht tot na het tonen van de fabrieksmethode, zodat we die in de definitieve code kunnen gebruiken. De nieuwe Retrieve-methode ziet er als volgt uit:
private static IEnumerable<T> Retrieve(Func<IDataRecord, T> factory,
string sql, Action<SqlParameterCollection> addParameters)
{
//ConnectionString is a private static property in the data layer
// You can implement it to read from a config file or elsewhere
using (var cn = new SqlConnection(ConnectionString))
using (var cmd = new SqlCommand(sql, cn))
{
addParameters(cmd.Parameters);
cn.Open();
using (var rdr = cmd.ExecuteReader())
{
while (rdr.Read())
yield return factory(rdr);
rdr.Close();
}
}
}
En nu zouden we die nieuwe methode Retrieve() als volgt aanroepen:
public IEnumerable<Foo> GetFooChildrenByParentID(int ParentID)
{
//I could easily use a stored procedure name instead of a full sql query
return Retrieve(Foo.FooFactory,
@"SELECT c.*
FROM [ParentTable] p
INNER JOIN [ChildTable] c ON c.ParentID = f.ID
WHERE f.ID= @ParentID", delegate(SqlParameterCollection p)
{
p.Add("@ParentID", SqlDbType.Int).Value = ParentID;
}
);
}
Uiteraard kan deze laatste methode worden uitgebreid met eventuele aanvullende bedrijfslogica. Het blijkt ook dat deze code uitzonderlijk snel is, omdat hij profiteert van de luie evaluatiefuncties van IEnumerable. Het nadeel is dat het de neiging heeft om veel objecten met een korte levensduur te creëren, en dat kan de transactieprestaties waar je naar vroeg, schaden. Om dit te omzeilen, doorbreek ik soms de goede n-tier en geef ik de IDataRecord-objecten rechtstreeks door aan de presentatielaag en vermijd ik onnodige objectcreatie voor records die gewoon meteen aan een rasterbesturing zijn gebonden.
Code bijwerken/aanmaken is vergelijkbaar, met het verschil dat u meestal slechts één record tegelijk wijzigt in plaats van meerdere.
Of ik kan je het lezen van dit lange bericht besparen en je gewoon vertellen om Entity Framework te gebruiken;)