Сохранение и запрос элементов в Nhibernate - PullRequest
0 голосов
/ 03 мая 2018

У меня есть два очень простых класса, один из которых является классом Store, который закодирован следующим образом

Магазин

class Store
{
    public virtual int StoreID { get; set; }
    public virtual string Name { get; set; }
    public virtual ICollection<Item> ItemID { get; set; }
}

И следующее для хранения предметов, которые будут храниться в магазине

Пункт

class Item
{
    public virtual int ItemID { get; set; }
    public virtual string Name { get; set; }
    public virtual double Price { get; set; }
    public virtual int Quantity { get; set; }
    public virtual Store StoreID { get; set; }        
}

Отображения с Fluent Nhiberate следующие

StoreMap

class StoreMap : ClassMap<Store>
{
   public StoreMap()
   {
       Id(x => x.StoreID).Column("idStore").GeneratedBy.Assigned();
       Map(x => x.Name).Column("Name").Not.Nullable();

       HasMany(x => x.ItemID).KeyColumn("idItem").Inverse().Table("Item").AsSet().Cascade.SaveUpdate();
    }
}

ItemMap

class ItemMap : ClassMap<Item>
{
    public ItemMap()
    {
       Id(x => x.ItemID).Column("idItem").GeneratedBy.Assigned();
       Map(x => x.Name).Column("Name").Not.Nullable();
       Map(x => x.Price).Column("Price").Default("0");
       Map(x => x.Quantity).Column("Quantity").Default("0");
       References(x => x.StoreID).Column("idStore");
    }
}

Теперь, когда я хочу сохранить два предмета и новый магазин, я делаю это следующим образом, но я не знаю, существует ли простой / лучший способ сделать это

Store s = new Store{...}
Item i1 = new Item {
     Name = "Item 1",
     ItemID = 1,
     Price = 100,
     Quantity = 1,
     StoreID = s
};
Item i2 = new Item {
     Name = "Item 2",
     ItemID = 2,
     Price = 100,
     Quantity = 1,
     StoreID = s
};
trans = session.BeginTransaction();
session.SaveOrUpdate(s);
session.SaveOrUpdate(i2);
session.SaveOrUpdate(i1);
//Commit changes
trans.Commit();
session.Flush();
session.Close();

И когда я запрашиваю все данные из магазина, в списке предметов отображается только первый элемент ...

Store s = session.QueryOver<Store>().Where(x => x.StoreID == 1).SingleOrDefault<Store>();

Итак, мой вопрос: как мне запросить и сохранить, чтобы получить как можно меньше строк кода и получить оба товара в списке магазина?

1 Ответ

0 голосов
/ 12 мая 2018
  • Для сохранения из-за того, как вы отобразили сущности, в частности, из-за набора параметров каскада (.Cascade.SaveUpdate()), вы можете счастливо удалить session.SaveOrUpdate(i2); и session.SaveOrUpdate(i1);. Просто сохранение s сохранит их автоматически.

  • Чтобы поддерживать семантику ОО, вам нужно добавить item s в `store. Фактически это означает добавление этих двух строк:

    s.ItemID.Add(i1);
    s.ItemID.Add(i2);
    
  • Вы можете использовать оператор using, чтобы упростить создание и удаление (закрытие) сессии.

Финальный код будет таким:

    Store s = // as defined earlier;
    Item i1 = // as defined earlier;
    Item i2 = // as defined earlier;

    s.ItemID.Add(i1);
    s.ItemID.Add(i2);

    using (var session = NhConfig.GetSession())
    {
        var trans = session.BeginTransaction();
        try
        {
            session.SaveOrUpdate(s);

            trans.Commit();
        }
        catch (Exception)
        {
            trans.Rollback();
            throw;
        }
    }

    // Later when you need to query
    using (var session = NhConfig.GetSession())
    {
        Store store = session.QueryOver<Store>().Where(x => x.StoreID == 1).SingleOrDefault();
        foreach (var item in store.ItemID) // store.ItemID.Count == 2.
        {
            Console.WriteLine($"Item - Id: {item.ItemID}. Name: {item.Name}. Store ID: {item.StoreID.StoreID}.");
        }
        /* The above loop will print:
           Item - Id: 1. Name: Item 1. Store ID: 1.
           Item - Id: 2. Name: Item 2. Store ID: 1.
        */
    }

В файлах сопоставления ...

ItemMap может оставаться таким, как вы его определили. Однако в StoreMap есть небольшая ошибка, которую нужно исправить.

Изменить строку:

HasMany(x => x.ItemID).KeyColumn("idItem").Inverse().Table("Item").AsSet().Cascade.SaveUpdate();

до

HasMany(x => x.ItemID).KeyColumn("idStore").Inverse().AsSet().Cascade.SaveUpdate();

Обратите внимание, что изменилось: .KeyColumn теперь называется «idStore» вместо «idItem». Имя ключевого столбца на стороне .HasMany(...) должно соответствовать имени столбца на стороне .References(...).

.Table("Item") является избыточным, поскольку по умолчанию "Item" будет именем таблицы для класса с именем Item. Поскольку вы не связались с именем таблицы в ItemMap, применяется правило по умолчанию (если вы не внедрили Convention для имен классов / таблиц).

Заключительные замечания:

  1. NHibernate теперь поддерживает от IQueryable<T> до session.Query<T>(); так что вы можете использовать его вместо session.QueryOver<T>(). Если вы уже используется для написания запросов Linq или если вы пришли из EF мир, вы можете просто продолжать писать запросы Linq, как вы обычно делают. Бывают случаи, когда Linq не отвечает вашим потребностям, и вам приходится использовать QueryOver. Еще меньше случаев, когда QueryOver не отвечает вашим потребностям, и вам приходится использовать CreateCriteria. Иногда ни один из них не будет полезен для вас, и вам придется писать raw sql. Но большую часть времени, в том числе и в этом случае, Linq будет направлять вас туда, куда вы идете.
  2. NHibernate также теперь поддерживает async/await шаблон. Все, что вам нужно, это добавить using NHibernate.Linq;, и они станут доступны. Для этого кода вы можете использовать API-интерфейсы SaveOrUpdateAsync() и SingleOrDefaultAsync() вместо не асинхронных версий.
  3. Поскольку вы используете Fluent NHibernate, рассмотрите возможность использования AutoMapping. Поверьте мне, написание картографических файлов - это не весело, и вы хотите писать только один, когда это абсолютно необходимо.
...