Простая связь между двумя таблицами
Я начал использовать NHibernate сегодня, но не могу понять, как настроить простое отношение между двумя таблицами. Я действительно не знаю, как это называется, это может быть отношение "один ко многим" или "внешний ключ" (я не имею в виду дизайн базы данных и используемые термины), но вот очень простой пример.
У меня есть таблица Product с атрибутами Id (PK), ProductName и CategoryId. Тогда у меня есть таблица Категории с атрибутами Id (PK) и CategoryName.
Я создал эти классы:
public class Product
{
public virtual int Id { get; set; }
public virtual string ProductName { get; set; }
public virtual int CategoryId { get; set; }
public virtual Category Category { get; set; }
public virtual string CategoryName
{
get { return this.Category == null ? String.Empty : this.Category.CategoryName; }
}
}
public class Category
{
public virtual int Id { get; set; }
public virtual string CategoryName { get; set; }
}
Другими словами, я просто хочу, чтобы Продукт сохранял, к какой категории он принадлежит (через атрибут CategoryId, который указывает на Id в таблице Categories). Мне не нужен класс Category для хранения списка связанных продуктов, если это делает его немного проще.
Чтобы еще яснее понять, что мне нужно, вот SQL, который я ожидаю:
SELECT Products.*, Categories.*
FROM Products INNER JOIN Categories ON Products.CategoryId = Categories.Id
по крайней мере, я так думаю (опять же, я не очень хорош в дизайне базы данных или запросах).
Я не могу понять, какой вид картографии мне нужен для этого. Я полагаю, мне нужно сопоставить его в файле Product.hbm.xml. Но я также сопоставляю CategoryId? И как мне сопоставить свойство категории?
Похоже, мне нужно отношение "один ко многим", поскольку у меня есть ОДНА категория для каждого продукта (или это рассуждение в обратном направлении?), Но кажется, что нет сопоставления "один ко многим"...
Спасибо за любую помощь!
Дополнение:
Я попытался добавить отношение "многие к одному" в сопоставлении "Персона", но получаю исключение, в котором говорится "Ошибка создания прокси-сервера", а во внутреннем исключении - "Неопределенное совпадение найдено".
Возможно, мне следует упомянуть, что я использую старую версию NHibernate (1.2, я думаю), потому что это единственная версия, которую я запустил с MS Access, поскольку он не обнаружил JetDriver в более новых версиях.
Я поместил файлы сопоставления, классы и код, где ошибка возникает на скриншотах, потому что я не могу понять, как разместить здесь XML-код... Он продолжает читать его как теги html и пропускает половину его. Тем не мение.
Отображения:
http://www.nickthissen.nl/Images/tmp7B5A.png
Классы:
http://www.nickthissen.nl/Images/tmpF809.png
Код загрузки, где происходит ошибка:
http://www.nickthissen.nl/Images/tmp46B6.png
(Как я уже сказал, внутреннее исключение гласит: "Неоднозначное совпадение найдено".
(Продукт в моем примере был заменен человеком)
Классы Person и Category наследуют Entity, который является абстрактным базовым классом и определяет свойства Id, Deleted, CreatedTime и updatedTime. Код, в котором происходит ошибка, находится в общем классе 'manager' (параметр типа TEntity, который должен наследовать Entity). Просто предполагается загрузить все объекты с атрибутом Deleted false. В этом случае TEntity - это "Персона".
Это прекрасно работает, если я опускаю отображение категории "многие к одному" в отображении "Персона", но, очевидно, свойство "Категория" всегда равно нулю.
О да, извините за смесь между C# и VB, код C# находится в общей структуре, которую я использую для нескольких проектов, в то время как часть VB - это фактическая реализация этой инфраструктуры на моем веб-сайте, и я только что использовал VB для этого.
Помогите? Спасибо!
2 ответа
В вашем классе Product должен содержаться только объект Category, вам не нужно свойство CategoryId. Тогда в вашем картографировании товаров вы должны иметь эту запись
<many-to-one name="Category" column="CategoryId" />
ОБНОВЛЕНИЕ: в ваших сопоставлениях отсутствует полное имя сопоставленного класса в теге. См. http://nhibernate.info/doc/nh/en/index.html
ОБНОВЛЕНИЕ 2:
Посмотрите, поможет ли вам NHibernate 1.2 в решении.NET 4.0
Исключение "Обнаружено неоднозначное соответствие" было вызвано проектом, нацеленным на.NET Framework 4, который, похоже, не совместим с NHibernate 1.2.1. Я перешел на 3.5, и это, кажется, решить эту конкретную проблему.
Теперь перейдем к следующему. Как видите, у класса Person есть свойство CategoryName, которое должно возвращать имя текущего объекта Category, или пустую строку, если категория оказывается пустой. Это сделано для того, чтобы я мог привязать коллекцию объектов Person к сетке, указав "CategoryName" в качестве свойства, к которому нужно привязать столбец.
По-видимому, это не работает с NHibernate. Всякий раз, когда я пытаюсь связать свою коллекцию людей с данными, я получаю это исключение:
"Средство доступа к свойству" CategoryName "для объекта" NHibernateWebTest.Database.Person "вызвало следующее исключение:" Не удалось инициализировать прокси-сервер - сеанс-владелец был закрыт "."
Это происходит при вызове метода "DataBind" в этом коде:
public virtual void LoadGrid() { if (this.Grid == null) return;
this.Grid.DataSource = this.Manager.Load(); this.Grid.DataBind(); }
(Это проект ASP.NET, а Grid - это GridView)
this.Manager возвращает существующий экземпляр NHibernateEntityManager, и я уже показал его метод Load, который содержит следующее:
public virtual EntityCollection Load() { using (ISession session = this.GetSession()) { var entities = session .CreateCriteria(typeof (TEntity)) .Add(Expression.Eq("Deleted", false)) .List(); return new EntityCollection(entities); } }
(Там есть некоторые общие параметры типа, но этот веб-сайт, кажется, скрывает их (из-за тегов типа html, я думаю)... Извините за это).Это может иметь какое-то отношение к самому NHibernate, как я уже сказал, я совершенно новичок в этом. Когда я вызываю свой метод Load, я ожидаю, что он вернет EntityCollection(Of Person) со всеми уже установленными свойствами. Кажется, я должен держать ISession открытой, пока я по какой-то причине связываю данные..? Это кажется немного странным...
Могу ли я обойти это? Могу ли я сделать так, чтобы метод Load просто возвращал коллекцию людей, уже полностью загруженных, чтобы я мог получить доступ к CategoryName, когда захочу?Подожди... Может, это ленивая загрузка?