В этом ответе основное внимание уделяется операциям «select» vs update/create/delete. Я думаю, что реже обновлять более одного или нескольких записей за раз, и поэтому я также думаю, что «выбор» - это то место, где узкие места имеют тенденцию возникать. Тем не менее, вам нужно знать ваше приложение (профиль). Лучшее место для фокусировки вашего времени оптимизации почти всегда находится на уровне базы данных в самих запросах, а не на клиентском коде. Клиентский код - это всего лишь сантехника: это не основная сила вашего приложения. Однако, поскольку сантехника, как правило, повторно используется во многих разных приложениях, я сочувствую желанию максимально приблизить ее к оптимальному, и поэтому у меня есть много возможностей сказать, как построить этот код.
У меня есть универсальный метод для некоторых запросов/процедур в моем слое данных, который выглядит примерно так:
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();
}
}
}
И это позволяет мне писать методы общественных слоев данных, которые используют анонимные методы для добавления параметров. Код, показанный работы с .Net 2.0+, но можно записать еще короче, используя .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;
}
);
}
Я собираюсь остановиться прямо здесь, так что я могу указать вам еще раз код чуть выше который использует анонимный метод для создания параметров.
Это очень чистый код, в котором он ставит определение запроса и создание параметров в одном и том же месте, в то же время позволяя абстрагироваться от кода подключения/вызова кода шаблона к чему-то более пригодному для повторного использования. Я не думаю, что эта техника покрыта какой-либо из пулевых точек в вашем вопросе, и это тоже довольно быстро. Я думаю, что это касается вопроса вашего вопроса.
Я хочу продолжить, хотя, чтобы объяснить, как это все совмещается. Остальное довольно прямолинейно, но также легко бросить это в список или подобное, и что-то не так, что в конечном итоге ухудшает производительность. Таким образом, бизнес-уровень затем использует фабрику для преобразования результатов запроса в объекты (C# 3.0 или более поздняя версия):
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]
//...
};
}
}
Вместо того, они живут в своем классе, вы также можете сгруппировать их вместе в статический класс, специально предназначенные для хранения фабричных методов.
Мне нужно внести одно изменение в исходный метод извлечения. Этот метод «возвращает» один и тот же объект снова и снова, и это не всегда так хорошо работает. То, что мы хотим сделать по-другому, чтобы заставить его работать, - заставить копию объекта, представленного текущей записью, так что, когда читатель мутирует для следующей записи, мы работаем с чистыми данными. Я ждал, пока не покажу заводский метод, чтобы мы могли использовать это в конечном коде. Новый Восстановите метод выглядит следующим образом:
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();
}
}
}
И теперь мы называем этот новый метод Получить(), как это:
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;
}
);
}
Очевидно, что этот последний метод может быть расширен, чтобы включать в себя любую дополнительную бизнес-логику, необходимую , Также выясняется, что этот код является исключительно быстрым, потому что он использует ленивые функции оценки IEnumerable. Недостатком является то, что он имеет тенденцию создавать много короткоживущих объектов, что может повредить транзакционную производительность, о которой вы просили. Чтобы обойти это, я иногда ломаю хороший n-уровень и передаю объекты IDataRecord непосредственно на уровень представления и избегаю ненужного создания объектов для записей, которые просто привязаны к элементу управления сетью сразу.
Обновление/создание кода аналогично, с той разницей, что вы обычно меняете только одну запись за раз, а не многие.
Или я мог бы спасти вас читают эту длинную почту и просто посоветует использовать Entity Framework;)
Хороший трюк, действие <> обратного вызова параметризовать запрос и закрытие анонимный делегат. –
+1, для последней строки :) –
Поместите последнюю строку вверху. :-) –