2010-11-15 19 views
2

我有以下情形:在NHibernate中查詢用戶類型

假設我在此舊數據庫中的「產品」表具有字符串類型的「類別」列。此列存儲類別ID由某種ascii字符分隔。例如:「| 1 |」 (對於類別1),「| 1 | 2 | 3 |」 (對於類別1,2,3)等

而不是公開一個字符串屬性,我想公開一個IEnumerable,以便我的產品類的用戶不必擔心解析這些值。

我創建一個SelectedCatories類型簡直是一個IEnumerable,我的產品類看起來是這樣的:

public class Product 
{ 
    public virtual Guid Id { get; set; } 
    public virtual string Name { get; set; } 
    public virtual bool Discontinued { get; set; } 
    public virtual SelectedCategories Categories { get; set; } 
} 

然後,我創建了一個SelectedCategoriesUserType類,像這樣:

public class SeletedCategoriesUserType : IUserType 
{ 
    static readonly SqlType[] _sqlTypes = {NHibernateUtil.String.SqlType}; 

    public bool Equals(object x, object y) 
    { 
     // Fix this to check for Categories... 
     if (ReferenceEquals(x, y)) return true; 
     if (x == null || y == null) return false; 
     return x.Equals(y); 
    } 

    public int GetHashCode(object x) 
    { 
     return x.GetHashCode(); 
    } 

    public object NullSafeGet(IDataReader rs, string[] names, object owner) 
    { 
     object obj = NHibernateUtil.String.NullSafeGet(rs, names[0]); 
     if (obj == null) return null; 

     string[] stringCategories = obj.ToString().Split(new[] {'|'}, StringSplitOptions.RemoveEmptyEntries); 

     var categories = new Categories(); 

     return 
      new SelectedCategories(
       stringCategories.Select(
        stringCategory => categories.Single(cat => cat.Id == int.Parse(stringCategory))) 
        .ToList()); 
    } 

    public void NullSafeSet(IDbCommand cmd, object value, int index) 
    { 
     if (value == null) 
     { 
      ((IDataParameter) cmd.Parameters[index]).Value = DBNull.Value; 
     } 
     else 
     { 
      var theCategories = (SelectedCategories) value; 

      var builder = new StringBuilder(); 
      builder.Append("|"); 
      theCategories.ForEach(i => builder.AppendFormat("{0}|", i.Id.ToString())); 

      ((IDataParameter) cmd.Parameters[index]).Value = builder.ToString(); 
     } 
    } 

    public object DeepCopy(object value) 
    { 
     return value; 
    } 

    public object Replace(object original, object target, object owner) 
    { 
     throw new NotImplementedException(); 
    } 

    public object Assemble(object cached, object owner) 
    { 
     throw new NotImplementedException(); 
    } 

    public object Disassemble(object value) 
    { 
     throw new NotImplementedException(); 
    } 

    public SqlType[] SqlTypes 
    { 
     get { return _sqlTypes; } 
    } 

    public Type ReturnedType 
    { 
     get { return typeof (SelectedCategories); } 
    } 

    public bool IsMutable 
    { 
     get { return false; } 
    } 
} 

我當時想建立一個查詢,讓我回到屬於特定類別(比如2類)的任何產品,匹配「| 2 |」和「| 1 | 2 | 3 |」。

現在,我天真的實現,勉強讓我測試通過如下:

public IEnumerable<Product> GetByCategory(Category category) 
    { 
     using (ISession session = NHibernateHelper.OpenSession()) 
     { 
      return session 
       .CreateSQLQuery("select * from product where categories LIKE :category") 
       .AddEntity(typeof(Product)) 
       .SetString("category", string.Format("%|{0}|%", category.Id)) 
       .List() 
       .Cast<Product>(); 
     } 
    } 

我的問題是:什麼是要正確的查詢的正確方法?

+0

只是爲了闡明:該查詢不適合您,或者您想知道一種「更乾淨」的方式來編寫您的查詢嗎?而且它是強制性的,你的類別ID由|分隔或者他們也可以用逗號(,)分隔? – Max 2010-11-15 21:03:00

回答

0

一種不同的方式來做到這一點的ICriteria查詢會是這樣......

return Session 
    .CreateCriteria(typeof(Product), "product") 
    .Add(Expression.Sql(
     "{alias}.categories LIKE ?", 
     string.Format("%|{0}|%", category.Id), 
     NHibernateUtil.String)) 
    .List<Product>(); 

然而,你可能要考慮設立一個多一對多產品和類別,並建立表間產品類別中的類別集合。你仍然可以保留你的連接類別Ids的領域(我認爲它是爲了傳統目的而需要的),但將它與這樣的東西綁定到集合。

public virtual ISet<Category> Categories { get; private set; } 

public virtual string CategoriesString 
{ 
    get { return string.Join("|", Categories.Select(c => c.Id.ToString()).ToArray()); } 
} 

做這樣的事情可以讓你在你的表上設置外鍵,並使查詢更容易構建。

return Session 
    .CreateCriteria(typeof(Product), "product") 
    .CreateCriteria("product.Categories", "category") 
    .Add(Restrictions.Eq("category.Id", category.Id)) 
    .List<Product>(); 
+0

謝謝,亞當。這適用於我現在需要的。我同意最好有一個「類別」表,但在我需要實現該用戶類型的真實應用程序中,事情比我在這裏描述的場景稍微複雜一些(有很多遺留數據以及我們無法輕易改變的事物......)。但我正在保存您的提示,因爲我們可能會根據事情的變化進行更改。 – 2010-11-16 22:27:27