I have two classes Order and Items
I want a method like this
class Order
{
public virtual IList<Item> GetItems(Order order)
{
//get items for that order.
}
}
class Item
{
public virtual IList<Order> GetOrders(Item item)
{
//get all the orders in which that items is present.
}
}
Is it write to create a method like this or instead should I create a property
public virtual IList<Item> Items { get; set; }
And how should I do the mapping for this is nhibernate??
Apparently you have a many-to-many relationship: An order can have many items and an item can belong to many orders. In a relational database you need to express this with a separate table which I presume you have - let's assume this table is called OrdersItems.
Following the Store/Product example from the Fluent NHibernate documentation you would create an Items property in an Order and an Orders property in Item:
class Order
{
public virtual IList<Item> Items { get; protected set; }
}
class Item
{
public virtual IList<Order> Orders { get; protected set; }
}
And the mappings:
public class OrderMap : ClassMap<Order>
{
public OrderMap()
{
HasManyToMany(x => x.Items)
.Cascade.All()
.Table("OrdersItems");
}
}
public class ItemMap : ClassMap<Item>
{
public ItemMap()
{
HasManyToMany(x => x.Orders)
.Cascade.All()
.Inverse()
.Table("OrdersItems");
}
}
Related
I have a problem with mapping in NHibernate.
The Order table has the Invoice_Id column which is the nullable FK to the Invoice table.
The problem is, when I load an Invoice which Id exists in the Order table, I see that ConnectedOrder property is null, why?
public class Invoice
{
public virtual Order ConnectedOrder { get; set; }
}
public class Order
{
public virtual Invoice ConnectedInvoice { get; set; }
}
public class InvoiceMap : ClassMap<Invoice>
{
public InvoiceMap()
{
this.References(x => x.ConnectedOrder).Nullable();
}
}
public class OrderMap : ClassMap<Order>
{
public OrderMap()
{
this.References(x => x.ConnectedInvoice).Nullable();
}
}
edit
I've changed my classes and mappings like Radim Köhler said, then I found that topic
Fluent NHibernate One-To-Many Mapping
and there was the need to also add:
this.HasMany(x => x.Orders)
.KeyColumn("Invoice_id")
.Inverse()
.Cascade
.AllDeleteOrphan();
and now it works
You may not like it, but the table structure described above, is not representing Entity relations you've created (so called one-to-one).
In case, that one table contains column referencing the another table (FK), we have scenario:
Each Order has exactly one (or null) Invoice. (many-to-one)
Invoice can be referenced by none or one or many Orders. (one-to-many)
That means, that we should express Entities like this:
public class Invoice
{ // many orders could reference us
public virtual IList<Order> Orders { get; set; }
...
public class Order
{ // unchanged
public virtual Invoice ConnectedInvoice { get; set; }
...
And the mapping should be:
public InvoiceMap()
{ // HasMany is one-to-many
this.HasMany(x => x.Orders)
...
}
public OrderMap()
{ // References is many-to-one
this.References(x => x.ConnectedInvoice).Nullable();
...
I am trying to model a parent/child association where a Parent class (Person) owns many instances of a child class (OwnedThing) - I want the OwnedThing instances to be saved automatically when the Person class is saved, and I want the association to be bi-directional.
public class Person
{
public class MAP_Person : ClassMap<Person>
{
public MAP_Person()
{
this.Table("People");
this.Id(x => x.ID).GeneratedBy.GuidComb().Access.BackingField();
this.Map(x => x.FirstName);
this.HasMany(x => x.OwnedThings).Cascade.AllDeleteOrphan().KeyColumn("OwnerID").Inverse();
}
}
public virtual Guid ID { get; private set; }
public virtual string FirstName { get; set; }
public virtual IList<OwnedThing> OwnedThings { get; set; }
public Person()
{
OwnedThings = new List<OwnedThing>();
}
}
public class OwnedThing
{
public class MAP_OwnedThing : ClassMap<OwnedThing>
{
public MAP_OwnedThing()
{
this.Table("OwnedThings");
this.Id(x => x.ID).GeneratedBy.GuidComb().Access.BackingField();
this.Map(x => x.Name);
this.References(x => x.Owner).Column("OwnerID").Access.BackingField();
}
}
public virtual Guid ID { get; private set; }
public virtual Person Owner { get; private set; }
public virtual string Name { get; set; }
}
If I set Person.OwnedThings to Inverse then the OwnedThing instances are not saved when I save the Person. If I do not add Inverse then the save is successful but person.OwnedThings[0].Owner is always null after I retrieve it from the DB.
UPDATE
When saving the data NHibernate will set the single association end in the database because it is set via the many-end of the association, so when I retrieve the OwnedThing from the DB it does have the link back to the Person set. My null reference was from Envers which doesn't seem to do the same thing.
Am I understanding you correctly that your problem only occur on "history" entities read by nhibernate envers?
If so, it might be caused by this bug
https://nhibernate.jira.com/browse/NHE-64
The workaround for now is to use Merge instead of (SaveOr)Update.
OwnedThings[0].Owner is most likely null because you are not setting it when you do the add. When using bidirectional relationships you have to do something like the below:
Person person = new Person();
OwnedThing pwnedThing = new OwnedThing();
pwnedThing.Owner = person;
person.OwnedThings.Add(pwnedThing);
If you do not explicity set the pwnedThing.Owner and you query that same object in the same ISession that you created it on it will be null. Typically I have add or remove methods that do this "extra" work for me. Take the below example:
public class Order : Entity
{
private IList<OrderLine> orderLines;
public virtual IEnumerable<OrderLine> OrderLines { get { return orderLines.Select(x => x); } }
public virtual void AddLine(OrderLine orderLine)
{
orderLine.Order = this;
this.orderLines.Add(orderLine);
}
public virtual void RemoveLine(OrderLine orderLine)
{
this.orderLines.Remove(orderLine);
}
}
public class OrderMap : ClassMap<Order>
{
public OrderMap()
{
DynamicUpdate();
Table("ORDER_HEADER");
Id(x => x.Id, "ORDER_ID");
HasMany(x => x.OrderLines)
.Access.CamelCaseField()
.KeyColumn("ORDER_ID")
.Inverse()
.Cascade.AllDeleteOrphan();
}
}
Hopefully the title of this question makes sense, if not, here is my elaboration.
With two entities, Brand and Affiliate and a many-to-may relationship between them i would like to be able to use a query to find the Affiliates where the BrandName is a variable value.
Here is the Affiliate class and Affiliate MapClass (simplified of course)
public class Affiliate
{
public virtual int Id { get; private set; }
public virtual DateTime DateReceived { get; set; }
public virtual IList<Brand> Brands { get; set; }
public Affiliate()
{
Brands = new List<Brand>();
}
}
public class AffiliateApplicationRecordMap : ClassMap<Affiliate>
{
public AffiliateApplicationRecordMap()
{
Id(x => x.Id).GeneratedBy.Identity();
Map(x => x.DateReceived, "TimeStampCreated");
HasManyToMany(x => x.Brands)
.Cascade.All()
.ParentKeyColumn("AffiliateID")
.ChildKeyColumn("BrandID")
.Table("AffiliateBrand");
}
}
There is a mapping table called AffiliateBrand which provides the many to many mapping.
Here is the Brand class and ClassMap
public class Brand
{
public virtual int ID { get; private set; }
public virtual string Name { get; set; }
public virtual IList<Affiliate> Affiliates{ get; set; }
public Brand()
{
Affiliates = new List<Affiliate>();
}
public virtual void AddAffiliateApplication(Affiliate affiliate)
{
affiliate.Brands.Add(this);
Brands.Add(affiliate);
}
}
public class BrandMap : ClassMap<Brand>
{
public BrandMap()
{
Id(x => x.ID).GeneratedBy.Identity();
Map(x => x.Name);
HasManyToMany(x => x.Affiliates)
.Cascade.All()
.Inverse()
.ParentKeyColumn("BrandID")
.ChildKeyColumn("PartnerID")
.Table("AffiliateBrand");
}
}
Now i'm tyring to write this query with NHibernate:
var result = session
.CreateCriteria(typeof(Partner))
.AddOrder(Order.Asc("DateReceived"))
.Add(Restrictions.Eq("Brands.Name", brandName))
.SetMaxResults(10)
.List<Partner>();
Now clearly this isn't working and i didn't really think it would. What i'm trying to do is get all Affiliates back where the Brand has a specific name. How do i write this query?
You need to add a join to your criteria using CreateAlias
var result = session
.CreateCriteria(typeof(Partner))
.AddOrder(Order.Asc("DateReceived"))
.CreateAlias("Brands", "brand")
.Add(Restrictions.Eq("brand.Name", brandName))
.SetMaxResults(10)
.List<Partner>();
I am using Fluent NHibernate to map the following classes:
public abstract class DomainObject
{
public virtual int Id { get; protected internal set; }
}
public class Attribute
{
public virtual string Name { get; set; }
}
public class AttributeRule
{
public virtual Attribute Attribute { get; set; }
public virtual Station Station { get; set; }
public virtual RuleTypeId RuleTypeId { get; set; }
}
public class Station : DomainObject
{
public virtual IList<AttributeRule> AttributeRules { get; set; }
public Station()
{
AttributeRules = new List<AttributeRule>();
}
}
My Fluent NHibernate mappings look like this:
public class AttributeMap : ClassMap<Attribute>
{
public AttributeMap()
{
Id(o => o.Id);
Map(o => o.Name);
}
}
public class AttributeRuleMap : ClassMap<AttributeRule>
{
public AttributeRuleMap()
{
Id(o => o.Id);
Map(o => o.RuleTypeId);
References(o => o.Attribute).Fetch.Join();
References(o => o.Station);
}
}
public class StationMap : ClassMap<Station>
{
public StationMap()
{
Id(o => o.Id);
HasMany(o => o.AttributeRules).Inverse();
}
}
I would like to order the AttributeRules list on Station by the Attribute.Name property, but doing the following does not work:
HasMany(o => o.AttributeRules).Inverse().OrderBy("Attribute.Name");
I have not found a way to do this yet in the mappings. I could create a IQuery or ICriteria to do this for me, but ideally I would just like to have the AttributeRules list sorted when I ask for it.
Any advice on how to do this mapping?
I think the OrderBy-method takes in the string that it inserts to the generated SQL-clause. So just doing
HasMany(o => o.AttributeRules).Inverse().OrderBy("Name");
Where the "Name" is the name of the column that contains Attribute's name. It should be in the column list because Attribute is joined to the AttributeRule.
Did you solve this other way? Please share.
Im trying to map the following classes:
public abstract class ScheduleType
{
public virtual int Id { get; set; }
public virtual TypeDiscriminatorEnum Discriminator { get; set; }
}
public class DerivedScheduleType : ScehduleType
{
public virtual bool MyProperty { get; set; }
}
public class ScheduleTypeMap : ClassMap<ScheduleType>
{
public ScheduleTypeMap()
{
Id(p => p.Id);
Map(p => p.Discriminator).CustomType<TypeDiscriminatorEnum>().Not.Nullable();
}
}
public class DerivedScheduleTypeMap : SubclassMap<DerivedScheduleType>
{
public DerivedScheduleTypeMap()
{
//DiscriminatorValue(TypeDiscriminatorEnum.DerivedSchedule);
Map(p => p.MyProperty);
}
}
The problem is that queries on ScheduleType joins with all derived tables to find the right one.
I need something that says to NHibernate to join only with the table that represents the right subclass.
Any sugestions?
Thanks in advance!
Use DiscriminateSubClassesOnColumn<TypeDiscriminatorEnum>("discriminator") instead of Map(p => p.Discriminator).
I'm not quite sure what you're trying to achieve though, because you're talking about joining other tables; discriminators aren't used with table-per-subclass, only in table-per-class-hierarchy.