2009-05-08 13 views
7

Jestem nowy w NHibernate i staram się nauczyć, jak wysyłać zapytania do moich danych.Zapytanie z NHibernate

Poniżej znajduje się konfiguracja xml. Wyświetlany jest tylko przepis.

Chcę mieć możliwość wysyłania zapytań do receptur przez recipetitle ze słów kluczowych wpisanych , a także składników z nazwy składnika.

Możesz więc wpisać np. "Wino z makaronem".

To jest to, co próbowałem, ale daje mi błąd.

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%'"; 

Chcę również załadować kolekcje.

Czy mam zamiar zapytać w prawo? Potrzebuję móc zbudować ciąg kwerendy z moich kryteriów wyszukiwania. To byłoby łatwe formować mnie w SQL.

Malcolm

<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> 

Odpowiedz

22

Do tworzenia dynamicznych zapytań, chciałbym użyć kryteria API. Dzięki temu zapytanie dynamiczne staje się o wiele bardziej stabilne, ponieważ nie trzeba wykonywać operacji na łańcuchach, aby go zbudować.

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>(); 

Edit:

Dla chętnych załadunku można ustawić pobierał-mode:

ICriteria query = Session.CreateCriteria(typeof(Recipe), "r") 
    .SetFetchMode("Images", FetchMode.Join) 
    .SetFetchMode("Comments", FetchMode.Join) 
    .SetFetchMode("Ingredients", FetchMode.Join) 

Ale nie zrobi tego, ponieważ można uzyskać wyniki mnoży się przez liczba obrazów, komentarzy i składników. Więc jeśli masz 4 obrazy, 2 komentarze i 12 składników, otrzymasz przepis 96 razy. Nie rozpoznajesz tego, ponieważ NHibernate układa rzeczy razem, ale generuje ruch między aplikacją a bazą danych. Lepiej niech NHibernate załaduje go osobnymi zapytaniami.


Kolejny edit pokazać dynamiczną kompozycję zapytania.

// 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)); 
} 
+0

Aby rozwiązać ten problem, można uzyskać DistinctRootEntityResultTransformer –

+0

Dlaczego chcesz używać FetchMode.Join zamiast FetchMode.Eager dla tych obciążeń, jeśli naprawdę zamierzasz używać obiektów podrzędnych? –

+0

Nie widzę, jak to jest dynamiczne, gdy masz mocno zakodowane słowa kluczowe.Jak byś zapytał, czy dałem ci ciąg słów rozdzielonych spacjami ??? – Malcolm

1

Oto powyższe kryteria z dynamicznych słów kluczowych

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>(); 
5

Zarówno Stefan i przykłady Sathish za złączyć% operatorów do SQL. Nie jest to konieczne, ponieważ Ograniczenia.Lek (nhib 2.0+) i Expression.Like (przed wersją 2.0) mają 3 wersje parametrów z funkcją 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)); 
} 

Pełny tekst dostępny zapytań z NHibernate wyszukiwania są również dostępne. Aby uzyskać więcej informacji, patrz Ayende's example.