如何不在 NHibernate 中加载一个特定的 属性(根本,甚至是懒惰的)?

How not load one specific property (at all, even lazy) in NHibernate?

我正在使用 Nhibernate 5.2.3 和 ASP.NET Core 2.2。我有一个实体 Cat,它有很多 Kitten。我想在加载 Cat 时设置这样的查询,根本不加载任何 Kitten。

我试过使用 'Lazy' 属性和 LINQ。 然后我尝试使用 'ICriteria' 和 'Fecth' 方法来设置 'SelectMode.Skip' 但它没有帮助。 有我对 DB

的查询
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; }
}

我想要这样制作:仅加载 'Cat' 的 'Id' 属性 并且不加载(惰性、获取、急切)Kittens 集合。

是否必须使用条件 API?

我也不太熟悉这个属性映射,但是为了让延迟加载工作,所有关联都需要 bi-directional(我记得在文档中看到过这个但找不到它) .这种关系你需要指定双方的关系,即小猫对象需要一个 public virtual Cat Parent { get; set; } 而这个 属性 将被映射 [ManyToOne(Column="NUM_CAT", Lazy="Proxy")].

反向实际上只对 bi-directional 关联很重要,否则无论你为 inverse 指定什么,它总是 inverse=true 因为另一个实体对父实体一无所知,所以你自动让父 Cat 对关联负责。

Parent 属性 添加到 Cat 对象后,如果为 Kittens [=83= 指定了 inverse=false,则在 Cat 对象上] 那么这意味着 Cat 负责管理并将自己归于 Kitten。换句话说,NHibernate 在保存操作期间设置 NUM_CAT 的值。对于 inverse=trueCat 不会为您管理该关联,而是任何时候将 Kitten 添加到 CatParent [= Kitten 上的 83=] 需要设置为正在将 Kitten 添加到其集合的 Cat 的父实例。

取下面的代码:

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。我通常将其映射为 Kittens 是 inverse=false 并尝试通过将袋子包装在 Enumerable 中来使 Cat 管理的关联仍然如此,因此外部用户无法将小猫直接添加到列表和设置One-to-Many accessfield.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);
    }
  }
}

要获得所需的行为,请确保添加 bi-directional 关联以启动并:

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) 因为这将代理对象并避免使用数据库,除非调用 属性。如果您想做一些更复杂的事情,请忽略小猫...