我有以下情况:
假设此旧数据库中的“Product”表具有string类型的“Categories”列。此列存储由某种ascii字符分隔的类别ID。例如:“| 1 |” (对于第1类),“| 1 | 2 | 3 |” (对于类别1,2和3)等。
我希望公开一个IEnumerable,而不是公开一个字符串属性,以便我的Product类用户不必担心解析这些值。
我正在创建一个只有IEnumerable的SelectedCatories类型,我的Product类看起来像这样:
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 :(得分:0)
执行ICriteria查询的另一种方法是......
return Session
.CreateCriteria(typeof(Product), "product")
.Add(Expression.Sql(
"{alias}.categories LIKE ?",
string.Format("%|{0}|%", category.Id),
NHibernateUtil.String))
.List<Product>();
但是,您可能需要考虑在Product和Category之间设置多对多表,并在Product类中设置Categories集合。您仍然可以保留连接类别ID的字段(我认为它是遗留用途所需的),但是将其与此类集合绑定在一起。
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>();