Как не загрузить одно конкретное свойство (вообще, даже ленивое) в NHibernate? - PullRequest
0 голосов
/ 06 июня 2019

Я использую Nhibernate 5.2.3 и ASP.NET Core 2.2. У меня есть одна сущность Кошка, у которой много котят. Я хочу настроить запрос таким образом, когда я загружаю Cat, чтобы вообще не загружать котят.

Я пытался обойтись с атрибутом 'Lazy' и LINQ. Затем я попытался использовать методы «ICriteria» и «Fecth», чтобы установить «SelectMode.Skip», но это не помогло. Есть мой запрос к БД

var cat = _session.CreateCriteria<Cat>()
    .Add(Restrictions.Eq(nameof(Cat.Id), id))
    .Fetch(SelectMode.Skip, nameof(Cat.Kittens))
    .UniqueResult<GH>();

Есть классы и их отображение (по Nhibernate.Mapping.Attribute)

public class Cat
{
    [Id(Column = "NUM_REC", Type = "Int32", Name = "Id", Generator = "identity")]
    public virtual int Id { get; set; }
    [Bag(Cascade = "all-delete-orphan", Inverse = true, Lazy = CollectionLazy.False)]
    [Key(Column = "NUM_CAT")]
    [OneToMany(ClassType = typeof(Kitten))]
    public virtual IList<Kitten> Kittens { get; set; }
}
public class Kitten
{
    [Id(Column = "NUM_REC", Type = "Int32", Name = "Id", Generator = "identity")]
    public virtual int Id { get; set; }
    [Property(Column = "NAME")]
    public virtual string Name { get; set; }
}

Я хочу сделать так: загружайте только свойство 'Id' для Cat и не загружайте (ленивый, извлекающий, нетерпеливый) набор котят.

Ответы [ 2 ]

2 голосов
/ 15 июня 2019

Нужно ли использовать Criteria API?

Я не слишком знаком с этим сопоставлением атрибутов, но для того, чтобы загрузка Lazy работала вообще, связь должна быть двунаправленной (я помню, что видел это в документации, но не могу ее найти). Это отношение необходимо указать для обеих сторон, т. Е. Объекту котенка требуется public virtual Cat Parent { get; set; }, и это свойство будет отображено [ManyToOne(Column="NUM_CAT", Lazy="Proxy")].

Обратное действительно имеет значение только для двунаправленных ассоциаций, в противном случае, независимо от того, что вы указываете для inverse, оно всегда будет inverse=true, потому что другая сущность ничего не знает о родителе, и поэтому вы автоматически делаете родителя Cat ответственный за ассоциацию.

После добавления свойства Parent к объекту Cat, а затем к объекту Cat, если для свойства Kittens указано inverse=false, это означает, что Cat отвечает за управление и присвоение себя Kitten. Другими словами, NHibernate устанавливает значение NUM_CAT во время операций сохранения. С inverse=true, Cat не будет управлять этой ассоциацией для вас, и вместо этого, каждый раз, когда Kitten добавляется к Cat, свойство Parent в Kitten должно быть установлено для родительского элемента экземпляр Cat, который добавляет Kitten в свою коллекцию.

Возьмите следующий код:

var mom = new Cat();
var child1 = new Kitten();
var child2 = new Kitten();

mom.Kittens.Add(child1);
mom.Kittens.Add(child2);

session.Save(mom);
session.Save(child1);
session.Save(child2);

С inverse=false:

insert into Cat (NUM_REC) values (1)
insert into Kitten (NUM_REC, NUM_CAT, NAME) values (1, 1, 'Foo')
insert into Kitten (NUM_REC, NUM_CAT, NAME) values (2, 1, 'Foo')

С inverse=true:

insert into Cat (NUM_REC) values (1)
--error!, NHibernate has no value for Cat/NUM_CAT

Так что, если мы установим inverse=false, нам нужно немного изменить код:

var mom = new Cat();
var child1 = new Kitten();
var child2 = new Kitten();

mom.Kittens.Add(child1); child1.Parent = mom;
mom.Kittens.Add(child2); child2.Parent = mom;

session.Save(mom);
session.Save(child1);
session.Save(child2);

И мы получим желаемый SQL. Я обычно сопоставляю это с котятами, являющимися inverse=false, и пытаюсь заставить сотрудника под управлением Cat все еще делать это, оборачивая сумку в Enumerable, чтобы внешние пользователи не могли добавлять котят непосредственно в список и устанавливать One-to-Many access как field.camelCase. Подробное определение свойства можно найти в 5.1.10

public class Cat {
  private IList<Kitten> kittens;
  public virtual IEnumerable Kittens => kittens;
  public virtual void Add(Kitten kitten){
    if (kitten != null){
      kitten.Cat = this;
      this.kittens.Add(kitten);
    }
  }
}

Чтобы получить желаемое поведение, добавьте двунаправленную ассоциацию для запуска и:

public class Cat {
  [Bag(Cascade="all-delete-orphan", Inverse=true, Lazy=True, Fetch=Select)]
  [Key(Column="NUM_CAT", NotNull="true")]
  [OneToMany(ClassType = typeof(Kitten))]
  public virtual IEnumerable Kittens => kittens;
}

Указание Fetch=Join переопределяет Lazy=true. На тебе котенок:

public class Kitten {
  [Id(Column="NUM_REC", Type="Int32", Name="Id", Generator="identity")]
  public virtual int Id { get; set; }
  [Property(Column="NAME")]
  public virtual string Name { get; set; }
  [ManyToOne(Column="NUM_CAT", Lazy="Proxy")]
  public virtual Cat Parent {get;set;}
}

Теперь у вас должен быть доступ к запросам ..., при выборе объекта по Id просто используйте session.Get(id), если вам нужен только Id, и ничего больше не используйте session.Load(id), так как это проксирует объект и избегает базы данных, если только свойство вызывается. Если вы хотите сделать что-то более сложное, просто оставьте котенка ...

0 голосов
/ 06 июня 2019

Если вы вообще не хотите загружать , просто не отображайте его.

Удалить следующие строки из Cat class:

[OneToMany(ClassType = typeof(Kitten))]
public virtual IList<Kitten> Kittens { get; set; }

Если вы по какой-то причине хотите сохранить свойство Kittens, но не хотите отображать его, удалите только сопоставление. Удалить только следующую строку:

[OneToMany(ClassType = typeof(Kitten))]

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...