В Windows с распределением ActiveState Perl используйте команда ppm.
это можно сделать с помощью linq to sql ...
IQueryable<Article> query = yourDataContext.Articles;
if (catId > 0)
query = query.Where(x => x.CategoryId == catId);
return query.ToList();
Вы можете легко создавать запросы таким образом, используя NHibernate HQL (Hibernate Query Language) . Это будет почти идентичная реализация, но я бы лично использовал параметры.
public List<Article> GetCat(int cat)
{
string qry = "select ap from Article a";
if(cat > 0)
qry += " where a.categoryID = :cat";
IQuery query = session.CreateQuery(qry).SetInt32("cat",cat);
return query.List<Article>();
}
Это возвращает список <> объектов Article, готовых к использованию.
Не любите LLBLGen? Ну, он тоже может это сделать.
Использование стиля «адаптер»:
RelationPredicateBucket filters = new RelationPredicateBucket();
if (cat > 0)
filters.Predicate.Add(Article.Fields.CategoryID == cat);
if (userId > 0)
filters.Predicate.Add(Article.Fields.UserID == userId);
// And so on.
var adapter = new DataAccessAdapter();
var results = new EntityCollection<Article>(new ArticleFactory());
adapter.FetchEntityCollection(results, filters);
Я подозреваю, что большинство ORM справятся с этим довольно легко.
Я делаю такие вещи в NHibernate все время.
(Я делал подобные вещи в Rails. Я немного удивлен, что есть ORM, которые не поддерживают это.)
Вероятно, вы можете сделать это с любым поставщиком LINQ, но я знаю, что ORM LightSpeed поддерживает его:
var query = UnitOfWork.Articles;
if (cat > 0)
query = query.Where(a => a.CategoryId == cat);
NHibernate поддерживает это с помощью API критериев:
ICriteria criteria = session.CreateCriteria<Article>();
if (cat > 0)
criteria.Add(Expression.Eq("categoryID", cat));
You can use the Predicate Builder and LINQ to NHibernate to generate dynamic query's like this:
//using Predicate Builder
public List<Location> FindAllMatching(string[] filters)
{
var db = Session.Linq<Location>();
var expr = PredicateBuilder.False<Location>(); //-OR-
foreach (var filter in filters)
{
string temp = filter;
expr = expr.Or(p => p.Name.Contains(temp));
}
return db.Where(expr).ToList();
}
You get the advantage of Type Save Query's and Compiler check.
You can also use the same approach of predicate builder with Linq to Sql and Entity Framework.
EDIT: Added example. It could be something like get all the locations matching N regions of the world, where the user select the regions he want to see, we don't know how many the user will select, we must build the (OR) expression on the fly, you can do something like:
public ActionResult Action(string[] filters)
{
/*This values are provided by the user, maybe its better to use
an ID instead of the name, but for the example is OK.
filters will be something like : string[] filters = {"America", "Europe", "Africa"};
*/
List<Location> LocationList = FindAllMatchingRegions(filters);
return View(LocationList);
}
public List<Location> FindAllMatchingRegions(string[] filters)
{
var db = Session.Linq<Location>();
var expr = PredicateBuilder.False<Location>(); //-OR-
foreach (var filter in filters)
{
string temp = filter;
expr = expr.Or(p => p.Region.Name == filter);
}
return db.Where(expr).ToList();
}
You can Nest Predicates for a complex scenarios like this:
If you want to do something like
p => p.Price > 99 &&
p.Price < 999 &&
(p.Description.Contains ("foo") || p.Description.Contains ("far"))
you can build:
var inner = PredicateBuilder.False<Product>();
inner = inner.Or (p => p.Description.Contains ("foo"));
inner = inner.Or (p => p.Description.Contains ("far"));
var outer = PredicateBuilder.True<Product>();
outer = outer.And (p => p.Price > 99);
outer = outer.And (p => p.Price < 999);
outer = outer.And (inner);
And use it like :
var pr = db.Products.Where(outer).ToList();
The Predicate Builder Source and examples are available at http://www.albahari.com/nutshell/predicatebuilder.aspx
Как уже упоминалось здесь, LINQ позволяет расширить любой запрос, просто добавив к нему дополнительные критерии.
var query =
from x in xs
where x==1
select x;
if (mustAddCriteria1)
query =
from x in query
where ... // criteria 1
select x;
if (mustAddCriteria2)
query =
from x in query
where ... // criteria 2
select x;
И так далее. Этот подход работает идеально. Но, вероятно, вы знаете, что компиляция запросов LINQ довольно затратна: например, Entity Framework может компилировать около 500 относительно простых запросов в секунду (см., Например, ORMBattle.NET ).
С другой стороны, многие инструменты ORM поддерживают скомпилированные запросы:
IQueryable
некоторому методу Compile
и получаете делегат, позволяющий выполнять его намного быстрее. позже, потому что в этом случае не произойдет перекомпиляции. Но если мы попытаемся использовать здесь этот подход, мы сразу заметим, что наш запрос на самом деле динамический: IQueryable
, который мы выполняем каждый раз, может отличаться от предыдущий. Наличие частей запроса там определяется значениями внешних параметров.
Так можем ли мы выполнять такие запросы как скомпилированные, например, без явного кэширования?
DataObjects.Net 4 поддерживает так называемую функцию «логического ветвления». Это подразумевает, что любое постоянное логическое выражение оценивается во время компиляции запроса, и его фактическое значение вводится в SQL-запрос как истинная логическая константа (то есть не как значение параметра или как выражение, использующее параметры).
Эта функция позволяет создавать различные планы запроса в зависимости от на значениях таких логических выражений. Например, этот код:
int all = new Random().Next(2);
var query =
from c in Query<Customer>.All
where all!=0 || c.Id=="ALFKI"
select c;
будет выполняться с использованием двух разных SQL-запросов и, следовательно, - двух разных планов запросов:
Случай, когда all == null, SQL-запрос:
SELECT
[a].[CustomerId],
111 AS [TypeId] ,
[a].[CompanyName]
FROM
[dbo].[Customers] [a]
WHERE(( CAST( 0 AS bit ) <> 0 ) OR( [a].[CustomerId] = 'ALFKI' ) );
Случай, когда all == null, план запроса:
|--Compute Scalar(DEFINE:([Expr1002]=(111)))
|--Clustered Index Seek(OBJECT:([DO40-Tests].[dbo].[Customers].[PK_Customer] AS [a]), SEEK:([a].[CustomerId]=N'ALFKI') ORDERED FORWARD)
Второй случай (когда all! = Null), SQL-запрос:
SELECT
[a].[CustomerId],
111 AS [TypeId] ,
[a].[CompanyName]
FROM
[dbo].[Customers] [a]
WHERE(( CAST( 1 AS bit ) <> 0 ) OR( [a].[CustomerId] = 'ALFKI' ) );
-- Notice the ^ value is changed!
Второй случай (когда all! = Null), план запроса:
|--Compute Scalar(DEFINE:([Expr1002]=(111)))
|--Clustered Index Scan(OBJECT:([DO40-Tests].[dbo].[Customers].[PK_Customer] AS [a]))
-- There is index scan instead of index seek!
Обратите внимание, что почти любой другой ORM скомпилирует это в запрос, использующий целочисленный параметр:
SELECT
[a].[CustomerId],
111 AS [TypeId] ,
[a].[CompanyName]
FROM
[dbo].[Customers] [a]
WHERE(( @p <> 0 ) OR ( [a].[CustomerId] = 'ALFKI' ) );
-- ^^ parameter is used here
Поскольку SQL Server (как и большинство баз данных) генерирует единственную версию плана запроса для конкретного запроса, в этом случае у него есть единственный вариант - сгенерировать план со сканированием индекса:
|--Compute Scalar(DEFINE:([Expr1002]=(111)))
|--Clustered Index Scan(OBJECT:([DO40-Tests].[dbo].[Customers].[PK_Customer] AS [a]), WHERE:(CONVERT(bit,[@p],0)<>(0) OR [DO40-Tests].[dbo].[Customers].[CustomerId] as [a].[CustomerId]=N'ALFKI'))
Хорошо, это было «быстрое» объяснение полезности этой функции. Вернемся теперь к вашему случаю.
Логическое ветвление позволяет реализовать его очень просто:
var categoryId = 1;
var userId = 1;
var query =
from product in Query<Product>.All
let skipCategoryCriteria = !(categoryId > 0)
let skipUserCriteria = !(userId > 0)
where skipCategoryCriteria ? true : product.Category.Id==categoryId
where skipUserCriteria ? true :
(
from order in Query<Order>.All
from detail in order.OrderDetails
where detail.Product==product
select true
).Any()
select product;
Пример отличается от вашего, но он иллюстрирует идею. Я использовал другую модель в основном для того, чтобы проверить это (мой пример основан на модели Northwind).
Это запрос:
Query.Execute (...)
, чтобы он выполнялся как скомпилированный запрос. IQueryable
.