Проект, над которым я сейчас работаю, включает рефакторинг объекта C# Com, который служит уровнем доступа к некоторым базам данных Sql 2005.
Автор существующего кода построил все запросы sql вручную, используя строку и множество операторов if, чтобы построить довольно сложный оператор sql (~ 10 объединений,> 10 подвыборов, ~ 15-25, где условия и GroupBy). Базовая таблица всегда одна и та же, но структура объединений, условий и группировок зависит от набора параметров, которые передаются в мой класс / метод.
Создание sql-запроса, подобного этому, действительно работает, но это, очевидно, не очень элегантное решение (и его довольно сложно читать / понимать и поддерживать) ... Я мог бы просто написать простой "конструктор запросов", но я почти уверен, что Я не первый, кто сталкивается с такой проблемой, поэтому мои вопросы:





LINQ - это то, что вам нужно.
Вы можете рассмотреть LINQ или O / R Mapper, подобный этому: http://www.llblgen.com/
Я использовал C# и Linq, чтобы сделать что-то подобное для фильтрации записей журнала при вводе пользователем (см. Условные запросы Linq):
IQueryable<Log> matches = m_Locator.Logs;
// Users filter
if (usersFilter)
matches = matches.Where(l => l.UserName == comboBoxUsers.Text);
// Severity filter
if (severityFilter)
matches = matches.Where(l => l.Severity == comboBoxSeverity.Text);
Logs = (from log in matches
orderby log.EventTime descending
select log).ToList();
Обновлено: запрос не выполняется до .ToList () в последнем операторе.
IQueryable <Log> преобразуется в SQL в последней строке, где фактически выполняется запрос; все операторы Where LINQ превращаются в одно предложение SQL WHERE. Таким же образом я и делаю SQL с большим разнообразием. Это намного чище, чем пытаться написать для этого сохраненную процедуру.
Я бы сделал это так:
public IQueryable<ClientEntity> GetClients(Expression<Func<ClientModel, bool>> criteria)
{
return (
from model in Context.Client.AsExpandable()
where criteria.Invoke(model)
select new Ibfx.AppServer.Imsdb.Entities.Client.ClientEntity()
{
Id = model.Id,
ClientNumber = model.ClientNumber,
NameFirst = model.NameFirst,
//more propertie here
}
);
}
Параметр Выражение, который вы передаете, будет динамическим запросом, который вы создадите с различными предложениями WHERE, JOINS и т. д. Это выражение получит Вызвано во время выполнения и даст вам то, что вам нужно.
Вот пример того, как это называть:
public IQueryable<ClientEntity> GetClientsWithWebAccountId(int webAccountId)
{
var criteria = PredicateBuilder.True<ClientModel>();
criteria = criteria.And(c => c.ClientWebAccount.WebAccountId.Equals(webAccountId));
return GetClients(criteria);
}
Стоит подумать, можете ли вы реализовать как параметризованную хранимую процедуру и оптимизировать ее в базе данных, а не динамически генерировать SQL через LINQ или ORM во время выполнения. Часто это работает лучше. Я знаю, что это немного старомодно, но иногда это самый эффективный подход.
Да, мы уже рассмотрели это, но на самом деле это не работает, поскольку наши запросы меняются слишком сильно ...
Если вы используете C# и .NET 3.5, с добавлением MS SQL Server, то LINQ to SQL определенно лучший вариант. Если вы используете что-то другое, кроме этой комбинации, я бы порекомендовал ORM-маршрут, например nHibernate или Дозвуковой.
Если время выполнения действительно не важно, я бы подумал о рефакторинге бизнес-логики, которая (так часто) имеет тенденцию находить свой путь вниз на уровень данных и в хранимые процессы длиной в несколько миллионов. С точки зрения удобства обслуживания, редактирования и добавления я всегда стараюсь (как программист на C#) поднять код до уровня бизнеса.
Пытаться разобраться в чьих-то 8000-строчных SQL-скриптах - не моя любимая задача.
:)
// W
Я понимаю потенциал Linq, но я еще не видел, чтобы кто-нибудь пытался выполнить запрос Linq той сложности, которую предлагает Бен.
the fairly complex sql statement (~10 joins, >10 sub selects, ~15-25 where conditions and GroupBy's)
Есть ли у кого-нибудь примеры больших запросов Linq и какие-либо комментарии по их управляемости?
Linq to SQL вместе с System.Linq.Dynamic предоставляет несколько хороших возможностей.
Я разместил здесь несколько примеров кода: http://blog.huagati.com/res/index.php/2008/06/23/application-architecture-part-2-data-access-layer-dynamic-linq
Я подхожу так поздно, и у меня нет шансов на голосование, но есть отличное решение, о котором я не видел: комбинация процедуры / функции с linq-to-object. Или, я полагаю, to-xml или to-datatable.
Я столкнулся с этим именно в этой ситуации, с массивным динамически построенным запросом, который был своего рода впечатляющим достижением, но сложность которого привела к кошмару обслуживания. У меня было так много зеленых комментариев, чтобы помочь бедняге, который пришел позже и понял это. Я был на классическом жерехе, поэтому у меня было мало альтернатив.
С тех пор я сделал комбинацию функция / процедура и linq. Часто полная сложность меньше, чем сложность попытки сделать это в одном месте. Передайте некоторые из ваших критериев в UDF, который станет намного более управляемым. Это дает вам управляемый и понятный набор результатов. Примените оставшиеся различия с помощью linq.
Вы можете использовать преимущества обоих:
Как решить, какие критерии обрабатывать в базе данных, а какие - в linq? Используйте свое суждение. Если вы можете эффективно обрабатывать сложные запросы к базе данных, вы справитесь с этим. Отчасти искусство, отчасти наука.
То, что вы говорите, позволяет БД делать то, в чем она хороша? Я думаю, что работа с linq KristoferA очень впечатляет. С другой стороны, мне больше нравится контролировать производительность базы данных с помощью некоторого транзакционного SQL. Обратите внимание, что уровень абстракции для базы данных может быть набором классов. Подход linq кажется подходом к общему программированию, но на самом деле тогда вам потребуется среда linq для оптимального управления запросами. Так что, хотя технически он впечатляет, он кажется немного непрозрачным, как минимум, и мои производственные среды все равно помещают этот материал в хранимые процессы.
@polyglot Да, здесь существует огромное количество факторов, и каждый из нас дал бы более конкретный совет, если бы у нас было больше конкретики. Для сложной поисковой системы я делаю хорошая догадка, основываясь на своем опыте, что это все спектакль и ремонтопригодность. Использование динамики в примерах KristoferA может иметь ограничение производительности, которое перевешивает ее гибкость. Linq to generic enumerables обеспечивает простоту по приемлемой цене, если уже проделана некоторая тяжелая работа. И с чрезвычайно сложной логикой я не доверяю Linq-to-sql эффективно настроить работу с базами данных. Еще нет.
Это своего рода экспериментальная попытка класса QueryBuilder по адресу http://www.blackbeltcoder.com/Articles/strings/a-sql-querybuilder-class. Стоит взглянуть.
Проверьте http://sqlom.sourceforge.net. Я думаю, он делает именно то, что вы ищете.
Как связка операторов IF является динамическим запросом?