Запросы UserType в NHibernate - PullRequest
2 голосов
/ 15 ноября 2010

У меня следующий сценарий:

Предположим, что в моей таблице «Продукт» в этой устаревшей базе данных есть столбец «Категории» типа string.В этом столбце хранятся идентификаторы категории, разделенные каким-то символом ascii.Например: "| 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>();
        }
    }

У меня вопрос: как правильно исправить этот запрос?

1 Ответ

0 голосов
/ 16 ноября 2010

Другой способ сделать запрос ICriteria был бы таким ...

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

Однако вы можете подумать о настройке таблицы «многие ко многим» между «Продуктом» и «Категорией» и настройке коллекции «Категории» в классе «Продукт». Вы по-прежнему можете сохранять поле связанных идентификаторов категорий (я полагаю, оно необходимо для устаревших целей), но привязать его к коллекции с помощью чего-то подобного.

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>();
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...