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
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 am using fluent nHibernate for my mappings as follow:
public class ContentTagMap: ClassMap<Employee>
{
public EmployeeMap()
{
Id(t => t.Id);
Map(t => t.Name);
HasManyToMany(t => t.Company);
}
}
public class CompanyMap: ClassMap<Company>
{
public HelpMap()
{
Id(h => h.Id);
Map(h => h.CompanyName).Length(6000);
Map(h => h.address).Length(6000);
HasManyToMany(h => h.Employee);
}
}
These mappings produce Employee Table ,Company Table and EmployeeToCompany Table
Employee Table
Id Name
1 John
2 MAX
Company Table
Id CompanyName address
1 HTC ABC
2 HTC2 India
EmployeeToCompany Table
Employee_Id Company_Id
1 1
2 1
How can I delete the employee with Id 1?
Unless I'm misunderstanding your question you should be asking:
How can i delete the content of the table using NHibernate?
Fluent NHibernate is only a tool to aid in the mapping of your entities. NHibernate is what you use to create, read, update and delete data. In any event:
9.5. Deleting persistent objects
ISession.Delete() will remove an object's state from the database. Of
course, your application might still hold a reference to it. So it's
best to think of Delete() as making a persistent instance transient.
From the NHibernate Documentation
You probably want to also define a Cascading style on your many to many relationship (HasManyToMany) in your mapping.
If you use Cascade.SaveUpdate in your many to many whenever you delete an entity on one side of the relationship it will delete that entity and any relationships. If you remove the association (ex. if you removed an Employee from a Company) it will only delete the relationship (EmployeeToCompany). This is what I've typically found to work in the case of many to many relationships.
Look at this article for more details on mapping and using a many to many relationship in Fluent NHibernate.
how to keep many to many relationships in sync with nhibernate?
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.
In order to use my Fluent NHibernate mappings on SQL Azure, I need to have a clustered index on every table. The default heap tables that Fluent NHibernate creates for many-to-many joins obviously don't do this as they don't have primary keys.
I want to be able to tell one side of the relationship to create a clustered index for its join table, but I'm not sure how. Here's what my mappings look like:
public class UserMap : ClassMap<User>{
public UserMap()
{
Table("Users");
Id(x => x.UserId).GeneratedBy.Identity().Column("UserId");
Map(x => x.UserName).Unique().Not.Nullable().Length(DataConstants.UserNameLength).Column("UserName");
Map(x => x.Email).Unique().Not.Nullable().Length(DataConstants.EmailAddressLength).Column("Email");
Map(x => x.Password).Not.Nullable().Length(DataConstants.PasswordHashLength).Column("Password");
HasMany(x => x.Clicks).Cascade.AllDeleteOrphan();
HasManyToMany(x => x.Roles).Cascade.SaveUpdate().Table("UsersInRole").ParentKeyColumn("UserId").
ChildKeyColumn("RoleId");
}
}
Please let me know if you need any more information!
I don't know if Fluent supports it directly (if not, just include the xml), but you can do it with Auxiliary Database Objects
<nhibernate-mapping>
<database-object>
<create>create clustered index ix on UsersInRole(UserId, RoleId)</create>
<drop>drop index UsersInRole.ix</drop>
</database-object>
</nhibernate-mapping>
I struggled with the same problem as the topic starter (as I'm combining Fluent NHibernate and Sql Azure as well) but the given answer didn't satify. This is because it is not dynamic by convention. Of course the HBM file could be dynamically created and added to the configuration afterwards with configuration.AddXmlFile("AddClusteredIndexesToManyToManyTables.hbm.xml"); but I just don't like the HBM files and I can't imagine there is a better way.
After several hours I found another solution which is dynamic (and readable!) and does not deal with hbm xml files. The solution is as follows:
Assumption #1: I will create a composite primary key for each junction table that results in a clustered index in SQL Server.
After the configuration has been build (thus the mappings are parsed), (Fluent) NHibernate gives us the oppertunity to look into the actual mappings with configuration.ClassMappings and configuration.CollectionMappings. The latter is used because we are interested in the many-to-many mappings.
foreach (var collectionMapping in configuration.CollectionMappings
// Filter on many-to-many
.Where(x => !x.IsOneToMany)) {
// Build the columns (in a hacky way...)
const string columnFormat = "{0}_id";
var leftColumn = new Column(string.Format(
columnFormat,
collectionMapping.Owner.MappedClass.Name));
var rightColumn = new Column(string.Format(
columnFormat,
collectionMapping.GenericArguments[0].Name));
// Fetch the actual table of the many-to-many collection
var manyToManyTable = collectionMapping.CollectionTable;
// Shorten the name just like NHibernate does
var shortTableName = (manyToManyTable.Name.Length <= 8)
? manyToManyTable.Name
: manyToManyTable.Name.Substring(0, 8);
// Create the primary key and add the columns
var primaryKey = new PrimaryKey {
Name = string.Format("PK_{0}", shortTableName),
};
primaryKey.AddColumn(leftColumn);
primaryKey.AddColumn(rightColumn);
// Set the primary key to the junction table
manyToManyTable.PrimaryKey = primaryKey;
}
And yes, the logic to get the left and right hand columnsAfter that the columns are a bit hacky but it works and you are free to adjust and edit my solution (^_-). The problem is that the collection mapping is fairly empty/unfilled.
Good luck with coding and creating conventions!
Any set of column(s) can be a clustered index... there is no requirement that I know of which forces you to use a PK constraint in order to build a clustered index.
More over I do not understand how a client could REQUIRE a clustered index. It might make them as a default but that's different than require. This is often reported as a "best practice" for SQL Server, but to the client, there's no real distinction between a secondary b-tree index on a column and the clustered index which orders the table's record. How would the client be able to distinguish the underlying storage of the data? One stores the data ordered by the cluster key, the other doesn't.
Maybe fluent-nhibernate performs better, or claims to - but it will "work" without any indexes.
But I'm not an expert in either so YMMV.
Great solution M.Mimpen.
When need map interfaces, put the ChildKeyColumn with interface name.
Ex:
HasManyToMany(x => x.Acessos).("IRole_id");
The class Acesso implements IRole interface. If you donĀ“t inform child key name, the column created will be "Acesso_id", but when create the key will try "IRole_id".
I have a table A that has a references to a table B through a third table C. C contains the primary key of A and B. For each A there is at most one record in C. When I try to create a mapping for A such that I am referencing B, I use the References function, but it does not allow me to specify that the mapping goes through another table and not directly. What is the proper way to do that?
The only mapping that I know that could do that would be a HasManyToMany in the mapping of A :
HasManyToMany(x => x.B)
.WithTableName("C")
.WithParentKeyColumn("A_Id")
.WithChildKeyColumn("B_Id");
The problem is that the mapping is for A having a list of Bs, not just one. I don't know how you could do it to get only one in a clean way.
I believe I have found the answer in google code samples. In the mapping class, it is possible to write an additional:
WithTable("SomeTable", c => { c.Map(x => x.Col1); });