2010-11-15 18 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