【问题标题】:Querying UserType's in NHibernate在 NHibernate 中查询 UserType
【发布时间】:2010-11-15 18:42:27
【问题描述】:

我有以下场景:

假设我在这个旧数据库中的“产品”表有一个字符串类型的“类别”列。此列存储由某种 ascii 字符分隔的类别 ID。例如:“|1|” (对于类别 1),“|1|2|3|” (适用于类别 1、2 和 3)等

我不想为此公开一个字符串属性,而是公开一个 IEnumerable,以便我的 Product 类的用户不必担心解析这些值。

我正在创建一个 SelectedCatories 类型,它只是一个 IEnumerable,我的 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>();
        }
    }

我的问题是:正确的查询方法是什么?

【问题讨论】:

  • 只是为了澄清:该查询对您不起作用还是您想知道一种“更简洁”的方式来编写您的查询?您的类别 ID 是否必须用 | 分隔?或者它们也可以用逗号 (,) 分隔?

标签: nhibernate


【解决方案1】:

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

【讨论】:

  • 谢谢,亚当。这适用于我现在需要的东西。我同意最好有一个“类别”表,但是在我需要实现该用户类型的实际应用程序中,事情比我在这里描述的场景要复杂一些(有很多遗留数据以及我们无法轻易改变的事情......)。但我正在保存你的小费,因为我们可能会根据情况进行更改。
猜你喜欢
  • 1970-01-01
  • 2018-10-12
  • 1970-01-01
  • 1970-01-01
  • 2011-02-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-11-12
相关资源
最近更新 更多