Fluent-NHibernate: How to a create a many-to-many relationship with a unique contraint - fluent-nhibernate

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

Related

Nhibernate Fluent Mapping multiple table to one object

I have 3 tables that represents a many to many mapping.
Two tables with different ids and a third table with a composite key referencing the other two.
How can i map this using the classmap in nhibernate?
The following doesn´t work:
HasManyToMany(m =>
m.ListBlockStatus)
.Table("BlockTypeAction")
.ParentKeyColumn("IdBlockActionDefinition")
.ChildKeyColumn("IdBlockTypeCategory")
.Table("BlockTypeCategory")
.ParentKeyColumn("Id");
Found what i need, unfortunately the query in the end is not an inner join.
HasManyToMany(m => m.ListBlockStatus)
.Table("BlockTypeAction")
.ChildKeyColumns.Add("IdBlockActionDefinition")
.ParentKeyColumn("IdBlockTypeCategory")
.Cascade.All();

Fluent NHibernate: How to create a clustered index on a Many-to-Many Join Table?

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".

NHibernate many-to-many and deleting an item

I've got a many-to-many association between Lists and ListItems: a List knows about its Items, but a ListItem doesn't know about the containing lists. The cascade is saveupdate.
So, whenever I'm trying to delete a ListItem entity, I'm getting an SQLException saying I'm breaking the referential integrity. NHibernate tries to delete my ListItem without deleting the corresponding row in the linking table. The question is, is it possible to instruct NHibernate to delete my ListItem without breaking the referential integrity?
In case I have to manually remove the item from all containing lists, how do I properly do that?
Thanks a lot for any advice.
ulu
You need to set the mapping on the child to inverse=true. From another thread:
When you call SaveOrUpdate NHibernate
first deletes all of the child
objects. Then, because neither
relationship is marked as inverse,
NHibernate also tries to set the
foreign key column in your child table
to null. Since the rows have already
been deleted, you receive the second
error. You need to set inverse=true on
one side of your relationship to fix
this. This is usually done on the one
(primary key or parent) side. If you
do not do this, NHibernate will make
the appropriate updates for each side
of the relationship.
public class StoreMap : ClassMap<Store>
{
public StoreMap()
{
Id(x => x.Id);
Map(x => x.Name);
HasMany(x => x.Staff)
.Inverse() // Magic code!
.Cascade.All();
}
}

How do you map a composite id to a composite user type with Fluent NHibernate?

i'm working w/ a legacy database is set-up stupidly with an index composed of a char id column and two char columns which make up a date and time, respectively. I have created a ICompositeUserTypefor the date and time columns to map to a single .NET DateTime property on my entity, which works by itself when not part of the id. i need to somehow use a composite id with key property mapping that includes my ICompositeUserType for mapping to the two char date and time columns. Apparently w/ my version of Fluent NHibernate, CompositeIdentityPart doesn't have a CustomTypeIs() method, so i can't just do the following in my override:
mapping.UseCompositeId()
.WithKeyProperty(x => x.Id, CommonDatabaseFieldNames.Id)
.WithKeyProperty(x => x.FileCreationDateTime)
.CustomTypeIs<FileCreationDateTimeType>();
is something like this even possible w/ NHibernate let alone Fluent? I haven't been able to find anything on this.
UPDATE:
after reading the NHibernate documentation more carefully, i found out that NHibernate does support component composite IDs, but it doesn't look like it supports custom types (i.e. IUserType and ICompositeUserType) for composite IDs. Also, the version of Fluent i have doesn't have support for NHibernate's support of component composite IDs (see issue 387 and issue 346). i will probably try and upgrade my entire solution, see if it works, and post an answer to my own question if it does... unless someone can tell me w/o me having to do that... 8o)
Try adding this
mapping.Map(x => x.FileCreationDateTime).Columns.Add("FileCreationDate", "FileCreationTime").CustomSqlType("YourType");
Although I am not sure what sense CustomSqlType does when referring to two columns at once. Probably none. Can you create a view that projects these to columns to normal DateTime?
NHibernate doesn't support the use of IUserType or ICompositeUserTypes for composite IDs. It does, however, support component composite IDs, but Fluent NHibernate has limited support for this. From what i could see in the source code, it doesn't allow you to specify the column names for the component properties like NHibernate allows. I had to give-up for now and just create two string fields that the two string date fields in the database could map to seperately, and then have a DateTime? property that translated the two string properties. Props to HeavyWave for their comment: "Although, I think you should just map each column to its own field and provide additional property that combines them."
mapping.CompositeId()
.KeyProperty(x => x.Id, CommonDatabaseFieldNames.Id)
.KeyProperty(x => x.FileCreationDateString, CommonDatabaseFieldNames.FileCreationDate)
.KeyProperty(x => x.FileCreationTimeString, CommonDatabaseFieldNames.FileCreationTime);

Fluent Nibernate putting a where clause in the mapping

I've got two objects a parent and a child list. In my fluent nhibernate mapping for the parent I want to load the list of the children.
However I want this to be conditional, a column in the child table is called "IsDeleted" and I only want to return the children where "IsDeleted" is false.
Is it possible to set up a mapping to do this? If not is it possible to do it in just standard nhibernate?
Thanks
Yes, you can use a Where constraint in Fluent NHibernate to map this. Somehting like:
HasMany(x => x.Children).Where("IsDeleted = 0");
The Where constraint should use SQL syntax not HQL. For tables that allow soft deletes it's probably easier to map a view that filters the deleted records out.