Here is my problem:
Domain: I have following Entities: [Sensor] that can be positioned at [Location]. It is a many-to-many relationship. I break it into two one-to-many to aggregate a [Position] of a [Sensor] at [Location]. Intermediate Entity is [SensorPosition].
Mapping for [SensorPosition] is as follows:
CompositeId().KeyReference(sp => sp.Sensor).KeyReference(sp => sp.Location);
References(sp => sp.Sensor).ForeignKey().Not.Nullable();
References(sp => sp.Location).ForeignKey().Not.Nullable();
Component(sp => sp.Position, p =>
{
p.Map(pos => pos.X);
p.Map(pos => pos.Y);
p.Map(pos => pos.Z);
});
I'm using a CompositeId() to enforce constraint of only one [Sensor] at one [Location]. (Same [Sensor] can be at different [Location]s, it is a business logic twist)
My question is: Can I add a generated primary key (Id) to this? I've tried it, but with CompositeId() in the mapping it is not being generated. Or is there any other way to enforce this constraint fluently?
I would avoid using composite primary keys. Uniquenes can be enforced by this mapping:
References(sp => sp.Sensor).UniqueKey("KeyName");
References(sp => sp.Location).UniqueKey("KeyName");
For more details see this question.
To generate composite primary key IDs you need to establish a bi-directional relationship between the foreign key properties and each entity referenced in the composite ID. When you create an instance of SensorPosition, assign each References property to the appropriate entities that make up the key and NHibernate will use their Id values for the SensorPosition key.
In the long run, it's much simpler (and generally recommended) to use a "surrogate" key instead of a composite key in NHibernate.
Related
Is there a way to map property with database column with custom column, that IS NOT a FK, just a candidate key ( it is unique for table )?
If not, what is my options here? (need to restrict select results with joined table restrictions)
NHibernate supports feature called property-ref. It is documented here: 5.1.10. many-to-one. Some extract:
The property-ref attribute should only be used for mapping legacy data
where a foreign key refers to a unique key of the associated table
other than the primary key. This is an ugly relational model. For
example, suppose the Product class had a unique serial number, that is
not the primary key. (The unique attribute controls NHibernate's DDL
generation with the SchemaExport tool.)
So, if the child table contains for example Guid, which is the same as in the target parent table... this could solve the issue. Example mapping:
<many-to-one name="Parent" property-ref="ParentGuid" column="THE_GUID_COLUMN"/>
Using the fluent syntax, it could look like this:
References(x => x.Parent)
...
.PropertyRef("ParentGuid")
.Column("THE_GUID_COLUMN");
Anyhow, this is not ideal and should be used mostly for solving legacy stuff.
I have to an existing schema and I want to map it with nhibernate.
entities / table schema:
post {
pk_id
prod_id
prod_internid
title
}
tag {
pk_t_id
prod_id
prod_internid
name
}
A post can have multiple tags and there is a foreign key contraint from the tag to the post table with the two columns prod_id and prod_internid.
I've tried this:
PostMap {
// tags is a list
HasMany(x => x.tags).KeyColumns.Add("prod_id", "prod_internid");
}
TagMap {
References(x => x.post).Columns("prod_id", "prod_internid");//.ForeignKey();
}
I get this error:
NHibernate.FKUnmatchingColumnsException: Foreign key (FK98806C8630C05A78:tag [prod_id, prod_internid])) must have same number of columns as the referenced primary key (post [pk_id])
How can I map it the right way?
I don't think this functionality is currently supported in NHibernate but it is in Hibernate. Seems like you or someone would need to port it over. Take a look at this NH Issue:
https://nhibernate.jira.com/browse/NH-1722
I also found this previous StackOverflow article regarding this:
many-to-one with multiple columns
I am trying to give a custom Unique Constraint Name as follows:
Map(x => x.Name).UniqueKey("MY_CONSTRAINT_NAME").Column("FUNCTION_NAME");
The Field is mapped with a unique constraint but the constraint name is self-managed and doesn't take the name I chose ("MY_CONSTRAINT_NAME")
Is this a BUG or am I using it incorrectly?
NHibernate itself does not allow you to supply a name for the unique key. https://nhibernate.jira.com/browse/NH-1955
I have a mapping like this:
HasMany(x => x.Orders).KeyColumn("CustomerID");
Which is causing a constraint like this to be generated by schemaexport:
alter table [CustomerOrder]
add constraint FK45B3FB85AF01218D
foreign key (CustomerID)
references [Customer]
I have tried adding .NotFound.Ignore() like on a References() mapping to disable the constraint from being generated but this does not work.
Can a mapping be defined that will force SchemaExport to not generate the constraint?
Figured it out:
HasMany(x => x.Orders).KeyColumn("CustomerID").ForeignKeyConstraintName("none");
buried in the source is a check to ignore creation if the name is "none"
I want to create a many to many relationship, but I want to have in the new table(MessageReceivers) a unique contraint on both columns (AdvanceMessageId,UserId):
mapping.HasManyToMany(x => x.Receivers)
.WithParentKeyColumn("AdvanceMessageId")
.WithChildKeyColumn("UserId")
.Cascade.All()
.LazyLoad()
.WithTableName("MessageReceivers");
Thanks for help
Old post... but in case someone else arrives here looking for the answer:
You need to add .AsSet() to the HasManyToMany mapping definintion.
i.e.
mapping.HasManyToMany(x => x.Users)
.WithTableName("MessageReceivers")
.WithParentKeyColumn("UserId")
.WithChildKeyColumn("AdvanceMessageId")
.Inverse().AsSet();
This will setup an unique, composite primary key constraint on the link table that uses both columns.
(clustered index)
The down side is AsSet() cannont be used with collection properties of type IList, so no for loops without casting.
I have been using ICollection and instantiating them as HashSet for my applications and it works well.
More info on collection management with Fluent Nhibernate:
List: Ordered collection of entities, duplicate allowed. Use a .net IList in code. The index column will need to be mapped in NHibernate.
Set: Unordered collection of unique entities, duplicates not allowed. Use Iesi.Collection.ISet in code. It is important to override GetHashCode and Equals to indicate the business definition of duplicate. Can be sorted by defining a orderby or by defining a comparer resulting in a SortedSet result.
Bag: Unordered list of entities, duplicates allowed. Use a .net IList in code. The index column of the list is not mapped and not honored by NHibernate.
You should also map the inverse side of the relationship like
mapping.HasManyToMany(x => x.Users)
.WithTableName("MessageReceivers")
.WithParentKeyColumn("UserId")
.WithChildKeyColumn("AdvanceMessageId")
.Inverse();
In newest Fluent NHibernate you will have to change
WithTableName -> Table
WithParentKeyColumn -> ParentKeyColumn
WithChildKeyColumn -> ChildKeyColumn