这是我的订单和订单项目类:
public class Order : AggregateRootBase<OrderId>
{
public string CustomerName { get; private set; }
public IList<OrderItem> Items { get; set; }
public DateTime RegisterDatetime { get; private set; }
}
public class OrderItem : ValueObjectBase
{
public long Id { get; private set; }
public long OrderId { get; set; }
public long Number { get; private set; }
public long Goods { get; private set; }
public double UnitPrice { get; private set; }
}
在映射此代码时,我希望订单位于订单表中,而订单项存储在名为OrderItems的不同表中。 这是我的映射:
public class OrderMapping : ClassMapping<Order>
{
public OrderMapping()
{
Table("Orders");
Lazy(false);
ComponentAsId(a => a.EntityId, a => { a.Property(x => x.DbId, x => x.Column("Id")); });
Property(a=>a.CustomerName);
Property(a => a.RegisterDatetime);
Bag(a => a.Items,
mapper => {
mapper.Inverse(true);
mapper.Cascade(Cascade.None);
mapper.Table("OrderItems");
mapper.Key(k => k.Column(columnMapper => columnMapper.Name("OrderId")));
},
relation => { relation.OneToMany(); });
}
}
public class OrderItemMapping : ClassMapping<OrderItem>
{
public OrderItemMapping()
{
Lazy(false);
Id(a => a.Id);
Table("OrderItems");
Property(a => a.OrderId);
Property(a => a.Number);
Property(a => a.Goods);
Property(a => a.UnitPrice);
}
}
我也在数据库中创建了表,但是当我插入带有 3 个订单项目的订单时,它会插入订单而不是订单项目 感谢您的帮助
您已将父Order
映射为Items
bag
的mapper.Inverse(true);
,该告诉NHibernate您不希望父级映射此关系。
由于子OrderItem
没有到父级的映射,因此不会保存关系。编写集合映射时,必须至少inverse(false)
一侧。
您还设置了mapper.Cascade(Cascade.None);
,它告诉 NHibernate,您不希望父Order
在Session
更改时处理Items
上的任何操作。
因此,除非您在每个Item
上显式调用Save()
,否则它们将永远不会被保存。
在 NHibernate 中,需要在自由class
布局和最佳数据库性能之间进行权衡(尽管在这种情况下非常小)。
如果您真的不希望OrderItem
将Order
属性链接回其父属性,那么每当OrderItem
的父Order
发生变化时,您都会收到对 SQL 的额外 UPDATE 调用,如果创建OrderItems
少于您使用它们执行的操作的 ~10%,则此成本实际上可以忽略不计。
在这种情况下,您可以将OrderMapping
上的inverse(false)
设置为Items
.
但我的任务是给OrderItem
一个Order
字段或属性(您可以使用NHibernate映射私人字段!),然后将OrderItemMapping
的映射inverse(false)
返回给父级,以便当孩子得救时,他们将处理这种关系。不过,在保存之前,您必须确保每个OrderItem
都填写了Order
字段/属性!
你可以通过使用OrderId
属性而不是对Order
的完整引用来解决这个问题,但你必须查找它。
至于将它们保存到数据库中,最简单的方法是将mapper.Cascade(Cascade.None);
更改为mapper.Cascade(Cascade.AllDeleteOrphan);
(可能不是确切的类名)。这将确保每当您在Order
上修改该集合时,Save()
/Update()
Order
,Items
集合中的所有OrderItem
都将相应地在数据库中更新。
您还可以签出不太严格的Cascade
或根据当前设置的需要手动保存它们。
最后查看 Nhibernate 文档中的bag
vsset
,我怀疑你想在这里set
,我只会inverse(true)
一起使用bag
。如果您使用inverse(false)
bag
则会出现性能损失,除非可以识别项目(您的OrderItem
有Id
,所以可以!),但如果可以识别项目,那么为什么不让它成为set
!
您inverse(false)
bag
可识别项目的唯一原因是,如果这些标识符与数据库中的主键不相关,则这种情况不会经常出现(或者在我的情况下!
谢谢你@starlight54但我的问题是这样解决的。 我不得不像这样改变我的课程:
public class Order : AggregateRootBase<OrderId>
{
private readonly IList<OrderItem> _items;
public string CustomerName { get; private set; }
public DateTime RegisterDatetime { get; private set; }
public IReadOnlyCollection<OrderItem> Items => new ReadOnlyCollection<OrderItem>(_items);
}
public class OrderItem : ValueObjectBase
{
private readonly long _number;
private readonly long _goods;
private readonly double _unitPrice;
public long Number => _number;
public long Goods => _goods;
public double UnitPrice => _unitPrice;
}
请注意,在订单项中,您必须创建_number,_goods和... 这是顺序的映射:
public class OrderMapping : ClassMapping<Order>
{
public OrderMapping()
{
Table("Orders");
Lazy(false);
ComponentAsId(a => a.EntityId, a => { a.Property(x => x.DbId, x => x.Column("Id")); });
Property(a => a.CustomerName);
Property(a => a.RegisterDatetime);
IdBag(a => a.Items, map => {
map.Access(Accessor.Field);
map.Table("OrderItems");
map.Key(a => a.Column("OrderId"));
map.Id(a => {
a.Column("Id");
a.Generator(Generators.Identity);
});
}, relation => relation.Component(map => {
map.Access(Accessor.Field);
map.Property(a => a.Number, a => a.Access(Accessor.Field));
map.Property(a => a.Goods, a => a.Access(Accessor.Field));
map.Property(a => a.UnitPrice, a => a.Access(Accessor.Field));
}));
}
}
在 nhibernate 中,有 IdBag 帮助我做我需要的事情。 请注意,不需要为 orderItem 类创建类映射。 nhibernate会自动插入它。 但是您必须手动创建数据库。 我希望这对你有所帮助。