FluentNhibernate HasManytoMany Relation - It doesnt add into the link table - fluent-nhibernate

Using fluentnhibernate i am having a problem with the link table insertion.
Here is my entities
public partial class Item
{
public virtual int Id
{
get;
set;
}
public virtual string Description
{
get;
set;
}
public virtual IList<Category> Categories
{
get;
set;
}
}
public partial class Category
{
public virtual int Id
{
get;
set;
}
public virtual string Name
{
get;
set;
}
public virtual string Description
{
get;
set;
}
public virtual IList<Item> Items
{
get;
set;
}
}
Here is my mappings.
public class ItemMapping : ClassMap<Item>
{
public ItemMapping()
{
Table("Item");
Schema("dbo");
Id(x => x.Id);
Map(x => x.Description);
HasManyToMany(x => x.Categories)
.ChildKeyColumn("Item_id")
.ParentKeyColumn("Category_id")
.Table("CategoriesToItems")
.AsSet();
}
}
public class CategoryMapping : ClassMap<Category>
{
public CategoryMapping()
{
Table("Category");
Schema("dbo");
Id(x => x.Id);
Map(x => x.Description);
Map(x => x.Name);
HasManyToMany(x => x.Items)
.ChildKeyColumn("Category_id")
.ParentKeyColumn("Item_id")
.Table("CategoriesToItems")
.AsSet();
}
}
Here is how i add it to collection in my mvc page
var category = CategoryTask.Query(x => x.Id == post.Category).FirstOrDefault();
var item = new Item
{
Categories = new List<Category> { category },
Tags = tags
};
ItemTasks.Save(item);
My question is why it doesnt add the relations in my link table "CategoriesToItems". The table is already in the database with Category_Id (FK, int, not null) and Item_Id (FK, int, not null).
Where is the problem? why it doesnt add it to relation table?

It's hard to say what's really wrong when we can't see what your ItemTasks.Save does under the covers. Are you wrapping your save in a transaction? If not, you should be.

You should call Session.Flush() just before the transaction.Commit() as well.

I am not certain if the problem has been solved, but it looks similar to my problem (fluentnhibernate hasmanytomany same identifier exception).
Also, it looks like your parent and child key columns are backward.

Related

NHibernate - Self referencing mapping interferes with foreign key mapping

I have the following classes:
public class Track
{
public virtual int Id { get; set; }
public virtual Track MainMix { get; set; }
public virtual IEnumerable<Track> SubMixes { get; set; }
public virtual IList<FileVersion> Files { get; set; }
}
public class FileVersion
{
public virtual int Id { get; set; }
public virtual Track Track { get; set; }
}
And the following mappings:
public class TrackMap : ClassMap<Track>
{
public TrackMap()
{
Id(x=>x.Id);
References(x => x.MainMix);
HasMany(x => x.SubMixes)
.Inverse()
.Cascade.All()
.KeyColumn("MainMix_id");
HasMany(a => a.Files)
.Access.CamelCaseField(Prefix.Underscore)
.Cascade.All();
}
}
public class FileVersionMap : ClassMap<FileVersion>
{
public FileVersionMap()
{
Id(x => x.Id);
References(x => x.Track);
}
}
There is omitted code for the sake of simplicity. The Track table has a "MainMix_id" column that is a self referencing column for a parent/child relationship among Track records.
When I try to fetch a track from the database the NHProfiler tells me that Nhibernate tries to fetch the fileversions of that track with the following query:
SELECT files0_.MainMix_id as MainMix9_1_,
files0_.Id as Id1_,
files0_.Id as Id9_0_,
files0_.Track_id as Track8_9_0_
FROM [FileVersion] files0_
WHERE files0_.MainMix_id = 3 /* #p0 */
It seems like it has confused the parent id column of the Track table with its primary key column. When I remove References(x => x.MainMix) from the Track mapping the query is correct, but I don't have the parent track record returned.
Let me know if I can clarify this any more and thanks in advance for your help!
Does this make a difference?
TrackMap :
References(x => x.MainMix).Column("MainMix_id");
FileVersionMap :
References(x => x.Track).Column("Track_id");

Nhibernate databinding foreign key to lookupedit

I would like to databind the foreign key property Product.CategoryId to a Devexpess Lookupedit in Windows Forms Application.
So
lookEditCategory.DataBindings
.Add(new Binding("EditValue", Product, "CategoryId ", true,
DataSourceUpdateMode.OnPropertyChanged));
lookEditCategory.Properties.Columns.Clear();
lookEditCategory.Properties.NullText = "";
lookEditCategory.Properties.DataSource = CatCol;
lookEditCategory.Properties.ValueMember = "CategoryId";
lookEditCategory.Properties.DisplayMember = "CategoryName";
var col = new LookUpColumnInfo("CategoryName") { Caption = "Type" };
lookEditCategory.Properties.Columns.Add(col);
The problem is that Nhibernate does not expose the foreign key Product.CategoryId. Instead my entity and mapping are like this
public partial class Product
{
public virtual int ProductId { get; set; }
[NotNull]
[Length(Max=40)]
public virtual string ProductName { get; set; }
public virtual bool Discontinued { get; set; }
public virtual System.Nullable<int> SupplierId { get; set; }
[Length(Max=20)]
public virtual string QuantityPerUnit { get; set; }
public virtual System.Nullable<decimal> UnitPrice { get; set; }
public virtual System.Nullable<short> UnitsInStock { get; set; }
public virtual System.Nullable<short> UnitsOnOrder { get; set; }
public virtual System.Nullable<short> ReorderLevel { get; set; }
private IList<OrderDetail> _orderDetails = new List<OrderDetail>();
public virtual IList<OrderDetail> OrderDetails
{
get { return _orderDetails; }
set { _orderDetails = value; }
}
public virtual Category Category { get; set; }
public class ProductMap : FluentNHibernate.Mapping.ClassMap<Product>
{
public ProductMap()
{
Table("`Products`");
Id(x => x.ProductId, "`ProductID`")
.GeneratedBy
.Identity();
Map(x => x.ProductName, "`ProductName`")
;
Map(x => x.Discontinued, "`Discontinued`")
;
Map(x => x.SupplierId, "`SupplierID`")
;
Map(x => x.QuantityPerUnit, "`QuantityPerUnit`")
;
Map(x => x.UnitPrice, "`UnitPrice`")
;
Map(x => x.UnitsInStock, "`UnitsInStock`")
;
Map(x => x.UnitsOnOrder, "`UnitsOnOrder`")
;
Map(x => x.ReorderLevel, "`ReorderLevel`")
;
HasMany(x => x.OrderDetails)
.KeyColumn("`ProductID`")
.AsBag()
.Inverse()
.Cascade.None()
;
References(x => x.Category)
.Column("`CategoryID`");
}
}
}
I cannot add the property CategoryID in my Product entity and mapping because then it will be mapped twice.
Is there any solution?
Yes. Do NOT use your domain entities in the UI.
Sometimes your UI doesn't need (and shouldn't be aware of) all the properties of your domain objects.
Other times, it needs DTOs that contain data from different domain sources (for example- a list of CourseNames for the Student screen), or, like in your case- it needs the data to be represented in a slightly different way.
So the best way would be to create your DTOs with all (and only) the properties needed by the UI.
See this SO question for further details.

Using References (many-to-one) in Fluent NHibernate creates a Foreign Key in both ends, the many end and the one-end

as the title says, I would like to create a many-to-one relationship using Fluent NHibernate. There are GroupEntries, which belong to a Group. The Group itself can have another Group as its parent.
These are my entities:
public class GroupEnty : IGroupEnty
{
public virtual long Id { get; set; }
public virtual string Name { get; set; }
...
public virtual IGroup Group { get; set; }
}
public class Group : IGroup
{
public virtual long Id { get; set; }
public virtual string Name { get; set; }
...
public virtual IGroup Parent { get; set; }
}
And these are the mapping files:
public class GroupEntryMap : ClassMap<GroupEntry>
{
public GroupEntryMap()
{
Table(TableNames.GroupEntry);
Id(x => x.Id).GeneratedBy.Native();
Map(x => x.Name).Not.Nullable();
...
References<Group>(x => x.Group);
}
}
public class GroupMap : ClassMap<Group>
{
public GroupMap()
{
Table(TableNames.Group);
Id(x => x.Id).GeneratedBy.Native();
Map(x => x.Name).Not.Nullable();
...
References<Group>(x => x.Parent);
}
}
With this configuration, Fluent NHibernate creates these tables:
GroupEntry
bigint Id string Name ... bigint Group_id
Group
bigint Id string Name ... bigint Parent_id bigint GroupEntry_id
I don't know why it creates the column "GroupEntry_id" in the "Group" table. I am only mapping the other side of the relation. Is there an error in my configuration or is this a bug?
The fact that "GroupEntry_id" is created with a "not null" constraint gives me a lot of trouble, otherwise I would probably not care.
I'd really appreciate any help on this, it has been bugging me for a while and I cannot find any posts with a similar problem.
Edit: I do NOT want to create a bidirectional association!
If you want a many-to-one where a Group has many Group Entries I would expect your models to look something like this:
public class GroupEntry : IGroupEntry
{
public virtual long Id { get; set; }
public virtual string Name { get; set; }
...
public virtual IGroup Group { get; set; }
}
public class Group : IGroup
{
public virtual long Id { get; set; }
public virtual string Name { get; set; }
...
public virtual IList<GroupEntry> GroupEntries { get; set; }
public virtual IGroup Parent { get; set; }
}
Notice that the Group has a list of its GroupEntry objects. You said:
I don't know why it creates the column "GroupEntry_id" in the "Group" table. I am only mapping the other side of the relation.
You need to map both sides of the relationship, the many side and the one side. Your mappings should look something like:
public GroupEntryMap()
{
Table(TableNames.GroupEntry);
Id(x => x.Id).GeneratedBy.Native();
Map(x => x.Name).Not.Nullable();
...
References<Group>(x => x.Group); //A GroupEntry belongs to one Group
}
}
public class GroupMap : ClassMap<Group>
{
public GroupMap()
{
Table(TableNames.Group);
Id(x => x.Id).GeneratedBy.Native();
Map(x => x.Name).Not.Nullable();
...
References<Group>(x => x.Parent);
//A Group has-many GroupEntry objects
HasMany<GroupEntry>(x => x.GroupEntries);
}
}
Check out the fluent wiki for more examples.
The solution was that I accidentally assigned the same table name for two different entities... Shame on me :(
Thanks a lot for the input though!

Fluent Nhibernate - search for an item based on value of a many to many relationship

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>();

FluentNHibernate Unidirectional One-To-Many Mapping

I have two classes. One is Order:
public class Order
{
public virtual int Id { get; set; }
public virtual IList<Product> Products { get; set; }
}
The other one is Product:
public class Product
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
}
They are fluently mapped like this:
public class OrderMap : ClassMap<Order>
{
public OrderMap()
{
Table("Orders");
Id(x => x.Id, "Id");
HasMany(x => x.Products)
.KeyColumn("OrderId")
.Cascade.All();
}
}
public class ProductMap : ClassMap<Product>
{
public ProductMap()
{
Table("Products");
Id(x => x.Id, "Id");
Map(x => x.Name);
}
}
The database does NOT have a not-null constraint on the OrderId column of the Products table.
Problem is: both the order and the products are being persisted, however the products are being persisted with a null value on the OrderId column.
Am I missing something?
instead of HasMany just use Reference
Reference(x => x.Products).Cascade.All();
Inverse on HasMany is an NHibernate term, and it means that the other end of the relationship is responsible for saving.
Well, as strange as it seems, we solved this issue here by re-working our Session management, using it to create an ITransaction in a using statement. Odd, but solved. Thanks everyone!
Try using:
HasMany(x => x.Products)
.KeyColumn("OrderId")
.Inverse()
.Cascade.All();
Which(Inverse()) states that OrderId is on the Products table