Nhibernate entity with multiple Many-To-Many lists of the same type? - nhibernate

Does anybody know how I would map an entity with two many-to-many collections of the same child type.
My database structure is this....
The "normal" relationship will be....
tbl_Parent
col_Parent_ID
tbl_Parent_Child_Xref
col_Parent_ID
col_Child_ID
tbl_Child
col_Child_ID
The alternative relationship is...
tbl_Parent
col_Parent_ID
tbl_Include_ParentChild_Xref
col_Parent_ID
col_Child_ID
tbl_Child
col_Child_ID
The entity and mapping look like this...
public partial class ParentEntity : AuditableDataEntity<ParentEntity>
{
public virtual IList<ChildEntity> Children { get; set; }
public virtual IList<ChildEntity> IncludedChildren { get; set; }
}
public partial class ParentMap : IAutoMappingOverride<ParentEntity>
{
public void Override(AutoMapping<ParentEntity> mapping)
{
mapping.Table("tbl_Parent");
mapping.HasManyToMany(x => x.Children)
.Table("tbl_Parent_Child_Xref")
.ParentKeyColumn("col_Parent_ID")
.ChildKeyColumn("col_Child_ID")
.Inverse()
.Cascade.All();
mapping.HasManyToMany(x => x.IncludedChildren)
.Table("tbl_Include_ParentChild_Xref")
.ParentKeyColumn("col_Parent_ID")
.ChildKeyColumn("col_Child_ID")
.Inverse()
.Cascade.All();
}
}
The error that I'm getting is
"System.NotSupportedException: Can't figure out what the other side of the many-to-many property 'Children' should be."
I'm using NHibernate 2.1.2, FluentNhibernate 1.0.

It seems FNH is confused because you seem to map the same object (ChildEntity) to two different tables, if I'm not mistaken.
If you don't really need the two lists to get separated, perhaps using a discriminating value for each of your lists would solve the problem. Your first ChildEntity list would bind to the discriminationg value A, and you sesond to the discriminating value B, for instance.
Otherwise, I would perhaps opt for a derived class of your ChildEntity, just not to have the same name of ChildEntity.
IList<ChildEntity> ChildEntities
IList<IncludedChildEntity> IncludedChildEntities
And both your objects classes would be identitical.
If you say it works with NH, then it might be a bug as already stated. However, you may mix both XML mappings and AutoMapping with FNH. So, if it does work in NH, this would perhaps be my preference. But think this workaround should do it.

You know I'm just shooting in the dark here, but it almost sounds like your ChildEntity class isn't known by Hibernate .. that's typically where I've seen that sort of message. Hibernate inspects your class and sees this referenced class (ChildEntity in this case) that id doesn't know about.
Maybe you've moved on and found the issue at this point, but thought I'd see anyway.

Fluent is confused because you are referencing the same parent column twice. That is a no-no. And as far as I can tell from the activity i have seen, a fix is not coming any time soon.
You would have to write some custom extensions to get that working, if it is possible.

To my great pity, NHibernate cannot do that. Consider using another ORM.

Related

Few questions about nhibernate & fluent nhibernate

I have a couple questions.
I been reading in nhibernate beginners guide 3 about using auto mapper with fluent. I read about this before(and I use auto mapper in my project already) however I am still not sure about a couple things.
What happens when you need to put like Not.Null(), or have to set a length, or inverse on something. How do you set those up? Won't you have to setup auto mapper for each of these properties that have these settings? Won't that sort of default the purpose?
I also been reading about common mistakes and one mistake was talking about when you need readonly. I am actually suffering from this problem and had to make a fix that I was never that happy about.
From reading this I am wondering if it is wise to have 2 mappings of these classes that I need to have readonly
Say I have this
public PlanMap()
{
Table("Plans");
Id(x => x.Id);
Map(x => x.Name).Not.Nullable().NvarcharWithMaxSize();
Map(x => x.Description).Not.Nullable().NvarcharWithMaxSize();
Map(x => x.Price).Not.Nullable();
Map(x => x.Discount).Not.Nullable();
Map(x => x.LengthInMonths).Not.Nullable();
References(x => x.Role).Not.Nullable();
HasMany(x => x.Students).Cascade.All();
}
So would it be wise to have that and then have
public ReadOnlyPlanMap()
{
Table("Plans");
ReadOnly();
SchemaAction.None();
Id(x => x.Id);
Map(x => x.Name).Not.Nullable().NvarcharWithMaxSize();
Map(x => x.Description).Not.Nullable().NvarcharWithMaxSize();
Map(x => x.Price).Not.Nullable();
Map(x => x.Discount).Not.Nullable();
Map(x => x.LengthInMonths).Not.Nullable();
References(x => x.Role).Not.Nullable();
HasMany(x => x.Students).Cascade.All();
}
Then when I need ReadOnly I use that mapping when I don't I use the other mapping? The only thing I see wrong about this is duplicate code. I am not sure if I can use inheritance or something to solve that problem though.
3.I read in the book that it recommend not to use the "auto" incrementing option in your database but instead use a hi-lo one setup in nhibernate to handle this.
In the book it says if you did something like session.Save(object); it would actually go and contact the server and this would break the unit of work. Does this happen when "auto" incrementing is set on the database? I never saw evidence of that happening and actually I had to commit a record before I would actually see the id.
When you use hi-lo what datatype does your column have to be? I usually use for my pk a incrementing int. Can I still use a int?
Finally from alot of examples I seen they usually make their PK properties like this
public virtual int Id { get; private set; }
Yet in the book I constantly saw
public virtual int Id { get; set; }
I thought using private set was the way to go to stop people from making their own number for the PK.
You would have better luck getting all of your questions answered if you broke them up into separate questions, but I'll address a couple of your questions anyway:
Automapping, Custom Conventions, and Overrides
If one of your business requirements is that most properties should not be nullable then you should make that the default by providing your own convention to the automapper. Take a look at this blog post for how you can do this.
Then if you have a mapping that needs to differ slightly from your conventions, then you can provide an automapping override by implementing IAutoMappingOverride<T> where in you only specify the columns/ids/relationships that are aberrant to the conventions.
The documentation at the FluentNHibernate wiki on Overrides and Conventions is actually quite good, I highly recommend reading it.
Readonly Entities
If I was doing this, what I'd do is have an NHibernate ignored, internal set property called something like IsReadonly { get; internal set; }, when an object is retrieved from somewhere that it should be read-only, then set that property before returning it to the caller.
If you have an explicit Save method on a repository, you can check that property and not do the actual NHibernate save if it's true. If you rely on the NHibernate dirty checking for saving on session Flush then you could implement an NHibernate listener which would not save the entity if that property was true.
Identifiers
One word (acronym) GUID; hi-lo can work, but it can get complicated and a bit finicky. For NHibernate to properly track the object it has to have a unique ID. If you're using auto ids then NHibernate will go to the database to get an ID when you Save your entity and before you do the Flush.
Comb GUIDs solve the problems that you'll run into with auto ids and hi-lo in exchange for taking a little bit more space in your DB, and memory. When using FluentNHibernate automapping, if your entity has a GUID as the type of it's Id property, it will automatically use the Guid Comb strategy.

two aggregate roots in fluent nhibernate

The problem is that I have two aggregate roots
The Aggregate roots are
Project.
NewsArticle.
A Project can have a collection of related NewsArticle.
A NewsArticle can have a collection of related Projects.
The requirements are that:
A user can assoicate a number of NewsArticle from Projects.
A user can assoicate a number of Project from NewsArticles.
In the database.
NewsArticle --* NewsArticleProject *-- Project.
In the mappings
NewsArticle side
public void Override(AutoMapping<NewsArticle> mapping)
{
mapping.HasManyToMany(c => c.FeaturedProjects).Cascade.All().Table("NewsArticleProject").AsSet();
}
project side
public void Override(AutoMapping<Project> mapping)
{
mapping.HasManyToMany(c => c.FeaturedNewsArticles).Table("NewsArticleProject").Inverse().AsSet();
}
I also tried HasMany() but that gives me an error message complaining about the ColumnName which I also set.
I am struggling to get fluent nHibernate to play nicely into the mappings it so that it can for fulfil my requirements.
I can manage to get it to work for one side only but when I try to get it to work with the other side I get this error message.
Can't figure out what the other side of the many-to-many property 'FeaturedNewsArticles' should be.
Thanks in advance if anyone can help me come up with a solution.
Try and give responsibility for saving to both sides by using Inverse() on "NewsArticle" too.
public void Override(AutoMapping<NewsArticle> mapping)
{
mapping.HasManyToMany(c => c.FeaturedProjects).Cascade.All().Table("NewsArticleProject").Inverse().AsSet();
}

Programmatically ignore children with nHibernate

I'm just trying to get my head around nHibernate and have a query. When setting up the mappings file (with Fluent or regular .hbm.xml files) you specify relationships (bags; one-to-many, etc) and sub-types - the idea being (I believe) is that when you fetch an object it also fetches and matching data. My question is can I programmatically tell my query to ignore that relationship?
So, below, there is a Foo class with a list of Bar objects. Within the mappings file this would be a one-to-many relationship and sometimes I want to retrieve a Foo with all Bars BUT sometimes I want to just retrieve the Foo object without the Bar, for performance reasons. How can I do this?
public class Foo { public int Id { get; set; } public List<Bar> { get; set; } }
public class Bar { public int Id { get; set; }
Cheers
The relationship shouldn't be loaded automatically unless you turn off Lazy Loading or specify it to be eager loaded in the query.
Edit:
To answer your questions in the comment below.
1) It's done as part of the query. An basic example using QueryOver in NHibernate 3.0 would look something like:
var result = Session.QueryOver()
.Fetch(x => x.Category).Eager
.Where(x => x.Price > 10)
.List();
I think with ICriteria it's "SetFetchMode("Category", FetchMode.Eager)"
2) If you turn off lazy-loading on the mapping for an object, it will effectively always be eager loaded. Tho I suggest you eager load on a query-by-query basis to avoid the possibility of having a massive chain of data loaded, or loading data you don't actually need.

Fluent NHibernate: Foreign Key violation or Null values

Hey guys, I am having some real issues with mapping using fluent nhibernate. I realise there are MANY posts both on this site and many others focusing on specific types of mapping but as of yet, I have not found a solution that solves my issue.
Here is what I have:
namespace MyProject.Models.Entites
{
public class Project
{
public virtual Guid Id {get; set;}
// A load of other properties
public virtual ProjectCatagory Catagory{get;set;}
}
}
and then the map:
namespace MyProject.DataAccess.ClassMappings
{
public class ProjectMap : ClassMap<Project>
{
public ProjectMap()
{
Id(x => x.Id);
Map(x => x.Title);
Map(x => x.Description);
Map(x => x.LastUpdated);
Map(x => x.ImageData).CustomSqlType("image");
HasOne(x => x.Catagory);
}
}
}
So as you can see, I have a project which contains a catagory property. Im not so hot on relational databases but from what I can figure, this is a many-one relationship where many Projects can have one catagory. No, projects cannot fall into more than one category.
So now we have:
namespace MyProject.Models.Entities
{
public class ProjectCatagory
{
public virtual Guid Id { get; set; }
public virtual String Name { get; set; }
}
}
and its map:
public ProjectCatagoryMap()
{
Id(x => x.Id);
Map(x => x.Name);
}
Issue is, well, it doesn't work ! I will do something similar to the following in a unit test:
Project myproject = new Project("Project Description");
// set the other properties
myProject.Catagory = new ProjectCatagory(Guid.New(), "Test Catagory");
repository.Save(myProject);
Now I have tried a number of mapping and database configurations when trying to get this to work. Currently, the Project database table has a column, "Catagory_id" (which i didnt put there, i assume NH added it as a result of the mapping) and I would LIKE it set to not allow nulls. However, when set as such, I get exceptions explaining that I cannot insert null values into the table (even though during a debug, i have checked all the properties on the Project object and they are NOT null).
Alternatively, I can allow the table to accept nulls into that column and it will simply save the Project object and totally disregard the Category property when saving, therefore, when being retrieved, tests to check if the right category has been associated with the project fails.
If i remember correctly, at one point I had the ProjectMap use:
References(x => x.Catagory).Column("Catagory_id").Cascade.All().Not.Nullable();
this changed the exception from "Cannot insert null values" to a foreign key violation.
I suspect the root of all this hassle comes from my lack of understanding of relational database set up as I have other entities in this project that do not have external dependencies which work absolutely fine with NHibernate, ruling out any coding issues I may of caused when creating the repository.
Any help greatly appreciated. Thank you.
The main issue here is a common misunderstand about the "one-to-one" relation in a relational database and the HasOne mapping in Fluent. The terms in the mapping are relational terms. (Fluent tries to "beautify" them a bit which makes it worse IMO. HasOne actually means: one-to-one.)
Take a look at the Fluent wiki:
HasOne is usually reserved for a
special case. Generally, you'd use a
References relationship in most
situations (see: I think you mean a
many-to-one).
The solution is very simple, just exchange HasOne with References (one-to-one to many-to-one in an XML mapping file). You get a foreign key in the database which references the ProjectCatagory.
A real one-to-one relation in a relational database is ideally mapped by a primary key synchronization. When two objects share the same primary key, then you don't waste space for additional foreign keys and it is ensured to be one-to-one.
To synchronize primary key, you need to hook up one's key to the others. However this works, it is not what you need here.
After playing around with all the available options for mapping. I found the answer to be similar to that suggested.
As was suspected, HasOne() was clearly wrong and References(x => x.Catagory) was part of the solution. However, I still received foreign key violation exceptions until:
References(x => x.Catagory).Column("Catagory_id").Cascade.SaveUpdate().Not.Nullable().Not.LazyLoad();
Just thought id update the thread in case someone else stumbles across this with a similar issue as just using References() did not work.
Its seems ProjectCatagory class is parent class of Project Class. So without parent class how can child class exist.
You have to use -
References(x => x.Catagory).Column("Catagory_id").Foreignkey("Id");
here Foreign Key is your ProjectCatagory table ID.

nHibernate Collection Count

I have the following model which I have created and mapped with nHibernate.
Using lazy loading so I don't need to get the Vehicles for the Dealer at the start.
Public class Dealer
{
public virtual string Name { get;set;}
public virtual IList<Vehicles> Vehicles { get;set;}
}
Now let's assume the Dealer has thousands of vehicles.
If I do Dealer.Vehicles.Count then NH will select and pull all the data.
What is the best way to simply get a count? Is there any way in which I can get a count with out declaring A new property dealerCount within the Dealer Class?
Also there is a feature in Hibernate which I believe will be implemented in a newer version of NH called Extra Lazy Loading. Would this solve the problem?
extra lazy loading would issue sql instead of populating the collection for certain operations such as Count or Contains. In fluent mappings its used as:
HasMany(x => x.CollectionProperty).ExtraLazyLoad();
or HBM
<one-to-many lazy="extra" ...
It's only usefull if you have large collections and need the special behavior.
Use count projection (Projections.RowCount)