Запрос с помощью NHibernate
-
10-07-2019 - |
Вопрос
Я новичок в NHibernate и пытаюсь научиться запрашивать свои данные.
Ниже приведен XML-файл конфигурации.Показан только рецепт.
Я хочу иметь возможность запрашивать рецепты по названию рецепта из введенных ключевых слов а также ингредиенты из ingredientname.
Таким образом, вы можете ввести, например, "вино для пасты".
Это то, что я пробовал, но выдает мне ошибку.
hql = "from Recipe r " +
"left join r.Images " +
"inner join r.User " +
"inner join r.Ingredients i " +
"where i.IngredientName Like '%pasta%' OR i.IngredientName Like '%wine%' OR r.RecipeTitle Like '%pasta' OR r.RecipeTitle Like '%wine%'";
Я также хочу с нетерпением загрузить коллекции.
Правильно ли я подхожу к вопросу??Мне нужно иметь возможность создавать строку запроса на основе моих критериев поиска.Это было бы легко сделать мне в SQL.
Малкольм
<class name="Recipe" table="Recipes" xmlns="urn:nhibernate-mapping-2.2">
<id name="RecipeID" type="Int32" column="RecipeID">
<generator class="identity" />
</id>
<property name="RecipeTitle" type="String">
<column name="RecipeTitle" />
</property>
<property name="Completed" type="Boolean">
<column name="Completed" />
</property>
<property name="ModifiedOn" type="DateTime">
<column name="ModifiedOn" />
</property>
<property name="Rating" type="Double">
<column name="Rating" />
</property>
<property name="PrepTime" type="Int32">
<column name="PrepTime" />
</property>
<property name="CookTime" type="Int32">
<column name="CookTime" />
</property>
<property name="Method" type="String">
<column name="Method" />
</property>
<bag name="Images" inverse="true" cascade="all">
<key column="RecipeID" />
<one-to-many class="OurRecipes.Domain.RecipeImage, OurRecipes.Domain, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
</bag>
<many-to-one name="Category" column="CategoryID" />
<bag name="Comments" inverse="true" cascade="all">
<key column="RecipeID" />
<one-to-many class="OurRecipes.Domain.Comment, OurRecipes.Domain, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
</bag>
<many-to-one name="User" column="EnteredByID" />
<bag name="Ingredients" inverse="true" cascade="all">
<key column="RecipeID" />
<one-to-many class="OurRecipes.Domain.Ingredient, OurRecipes.Domain, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
</bag>
</class>
Решение
Для построения динамических запросов я бы использовал criterions API.Это делает динамический запрос намного более стабильным, потому что вам не нужны строковые операции для его создания.
ICriteria query = Session.CreateCriteria(typeof(Recipe), "r")
.CreateCriteria("Ingredients", "i", JoinType.InnerJoin)
.Add(
Expression.Disjunction() // OR
.Add(Expression.Like("i.IngredientName", "%pasta%"))
.Add(Expression.Like("i.IngredientName", "%wine%"))
.Add(Expression.Like("r.RecipeTitle", "%pasta%"))
.Add(Expression.Like("r.RecipeTitle", "%wine%")));
List<Recipe> result = query.List<Recipe>();
Редактировать:
Для быстрой загрузки вы могли бы установить режим выборки:
ICriteria query = Session.CreateCriteria(typeof(Recipe), "r")
.SetFetchMode("Images", FetchMode.Join)
.SetFetchMode("Comments", FetchMode.Join)
.SetFetchMode("Ingredients", FetchMode.Join)
Но я бы не стал этого делать потому что вы получаете результаты, умноженные на количество изображений, комментариев и Ингредиентов.Итак, если у вас было 4 изображения, 2 комментария и 12 Ингредиентов, вы получили свой рецепт 96 раз.Вы этого не узнаете, потому что NHibernate снова объединяет все воедино, но при этом генерирует трафик между приложением и базой данных.Так что лучше пусть NHibernate загружает его отдельными запросами.
Еще одна правка чтобы показать динамическую композицию запроса.
// filter arguments, all are optional and should be omitted if null
List<string> keywords;
TimeSpan? minCookingTime;
TimeSpan? maxCookingTime;
int? minRating;
int? maxRating;
ICriteria query = Session.CreateCriteria(typeof(Recipe), "r");
if (keyword != null)
{
// optional join
query.CreateCriteria("Ingredients", "i", JoinType.InnerJoin);
// add keyword search on ingredientName and RecipeTitle
var disjunction = Expression.Disjunction();
foreach (string keyword in keywords)
{
string pattern = String.Format("%{0}%", keyword);
disjunction
.Add(Expression.Like("i.IngredientName", pattern))
.Add(Expression.Like("r.RecipeTitle", pattern));
}
query.Add(disjunction)
}
if (minCookingTime != null)
{
query.Add(Expression.Ge(r.CookingTime, minCookingTime.Value));
}
if (maxCookingTime != null)
{
query.Add(Expression.Le(r.CookingTime, maxCookingTime.Value));
}
if (minRating != null)
{
query.Add(Expression.Ge(r.Rating, minRating.Value));
}
if (maxRating != null)
{
query.Add(Expression.Le(r.Rating, maxRating.Value));
}
Другие советы
Оба примера Стефана и Сатиша объединяют операторы % в SQL.Это излишне в качестве ограничений.Like (nhib 2.0+) и Expression.Like (до версии 2.0) имеют 3 версии параметров с MatchMode.
Disjunction keywordsCriteria = Restrictions.Disjunction();
foreach (var keyword in keywords)
{
keywordsCriteria.Add(Restrictions.Like("i.IngredientName", keyword, MatchMode.Anywhere));
keywordsCriteria.Add(Restrictions.Like("r.RecipeTitle", keyword, MatchMode.Anywhere));
}
Полнотекстовые запросы также доступны с помощью NHibernate Search.Видишь Пример Айенде для получения более подробной информации.
Вот приведенные выше критерии с динамическими ключевыми словами
string searchQuery = "wine pasta";
ICriteria query = Session.CreateCriteria(typeof(Recipe), "r")
.CreateCriteria("Ingredients", "i", JoinType.InnerJoin)
.SetFetchMode("Images", FetchMode.Join)
.SetFetchMode("Comments", FetchMode.Join)
.SetFetchMode("Ingredients", FetchMode.Join)
.SetResultTransformer(new DistinctRootEntityResultTransformer());
var keywords = searchQuery.Split(' ');
Disjunction keywordsCriteria = Restrictions.Disjunction();
foreach (var keyword in keywords)
{
keywordsCriteria.Add(Restrictions.Like("i.IngredientName", string.Format("%{0}%", keyword)));
keywordsCriteria.Add(Restrictions.Like("r.RecipeTitle", string.Format("%{0}%", keyword)));
}
query.Add(keywordsCriteria);
List<Recipe> result = query.List<Recipe>();