Whenever I load a Task class, the Document property is always null, despite there being data in the db.
Task class:
public class Task
{
public virtual Document Document { get; set; }
Task Mapping override for AutoPersistenceModel:
public void Override(AutoMap<Task> mapping)
{
mapping.HasOne(x => x.Document)
.WithForeignKey("Task_Id");
As you can see form what NHProf says is being run, the join condition is wrong, the WithForeignKey doesnt seem to take effect. In fact, i can write any string in the above code and it makes no difference.
FROM [Task] this_
left outer join [Document] document2_
on this_.Id = document2_.Id
It should be:
FROM [Task] this_
left outer join [Document] document2_
on this_.Id = document2_.Task_Id
If i hack the data in the db so that the ids match, then data is loaded, but obviously this is incorrect - but at least it proves it loads data.
Edit: rummaging in the fluent nhib source to find the XML produces this:
<one-to-one foreign-key="Task_Id" cascade="all" name="Document" class="MyProject.Document, MyProject, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
Edit: heres the schema:
CREATE TABLE [dbo].[Document](
[Id] [int] IDENTITY(1,1) NOT NULL,
[Task_Id] [int] NOT NULL,
CREATE TABLE [dbo].[Task](
[Id] [int] IDENTITY(1,1) NOT NULL,
Anyone got any ideas?
Thanks
Andrew
I ran into the same issue today. I believe the trick is not to use .ForeignKey(...) with the .HasOne mapping, but to use .PropertyRef(...) instead. The following is how I define a One-to-one relationship between an Organisation (Parent) and its Admin (Child):
HasOne(x => x.Admin).PropertyRef(r => r.Organisation).Cascade.All();
The Admin has a simple reference to the Organisation using its Foreign Key:
References(x => x.Organisation, "ORAD_FK_ORGANISATION").Not.Nullable();
When retrieving an Organisation, this will load up the correct Admin record, and properly cascades updates and deletes.
You should use:
References(x => x.Document, "DocumentIdColumnOnTask")
I think the problem here is that the "HasOne" convention means that you are pointing at the other thing(the standard relational way to say "Many To One"/"One to One"); By putting a Task_ID on the document the actual relationship is a HasMany but you have some kind of implicit understanding that there will only be one document per task.
Sorry - I don't know how to fix this, but I will be interested in seeing what the solution is (I don't use NHibernate or Fluent NHibernate, but I have been researching it to use in the future). A solution (from someone with very little idea) would be to make Documents a collection on Task, and then provide a Document property that returns the first one in the collection (using an interface that hides the Documents property so no one thinks they can add new items to it).
Looking through documentation and considering eulerfx's answer, Perhaps the approach would be something like:
References(x => x.Document)
.TheColumnNameIs("ID")
.PropertyRef(d => d.Task_ID);
EDIT: Just so this answer has the appropriate solution: The correct path is to update the database schema to match the intent of the code. That means adding a DocumentID to the Task table, so there is a Many-To-One relationship between Task and Document. If schema changes were not possible, References() would be the appropriate resolution.
I've tried this solution:
just in Document:
mapping.HasOne(x => x.Task).ForeignKey("Task_ID").Constrained().Cascade.All();
As eulerfx pointed out,
the table structure indicates that there maybe mulitple documents for a task
and Chris stated:
By putting a Task_ID on the document the actual relationship is a HasMany but you have some kind of implicit understanding that there will only be one document per task.
This is of course correct so I have reversed it so Task has a nullable Document_Id.
Thanks to both of you for you help!
I flipped a coin for the accepted answer, if i could tick both i would!
I have been struggling with the same Has One problem and finally found that this worked:
public class ParentMap : ClassMap<Parent>
{
public ParentMap()
{
Id(x => x.Id);
HasOne(s => s.Child).Cascade.All();
}
}
public class ChildMap : ClassMap<Model.Child>
{
public ChildMap()
{
Id(x => x.Id);
HasOne(s => s.Parent).Constrained().ForeignKey();
}
}
Related
Having a seemingly bizzare N+1 select problem in NHibernate. I am executing a query where I'm asking for a bunch of entities where one of its linked properties is null. I don't actually need the linked property to be returned in this case by NHibernate as its only for the purpose of selecting the right data.
First Entity is a booking Window
public class BookingWindow : Entity<BookingWindow>
{
// Blah blah blah
/// <summary>
/// Gets or sets the booking order item.
/// </summary>
/// <value>
/// The booking order item.
/// </value>
public virtual BookingWindowOrderItem BookingOrderItem { get; set; }
}
And the BookingWindowOrderItem as follows
public class BookingWindowOrderItem : OrderItem
{
// Blah blah blah
public virtual BookingWindow BookingWindow { get; set; }
}
Here are the respective mappings
public BookingWindowMap()
{
this.Schema("Customer");
this.Table("BookingWindows");
this.Id(x => x.Id).GeneratedBy.Guid();
this.Component(x => x.WindowPeriod, m =>
{
m.Map(x => x.Min, "StartTime");
m.Map(x => x.Max, "EndTime");
});
this.References(window => window.BookingOrderItem).PropertyRef("BookingWindow").Column("Id").LazyLoad().Nullable().ReadOnly();
this.Map(x => x.Price);
this.References(x => x.CustomerRoom).ForeignKey("RoomId").Column("RoomId");
}
And
public BookingWindowOrderItemMap()
{
this.DiscriminatorValue(1);
this.References(x => x.BookingWindow).LazyLoad().Column("OrderItemForeignId").ForeignKey("OrderItemForeignId");
}
Now When I execute the following query I get back the correct Booking windows that don't have an order item.
Session.QueryOver<BookingWindow>().Where(w => w.CustomerRoom.Id == Guid.Parse(roomId)).Left.JoinQueryOver(bw => bw.BookingOrderItem).WhereRestrictionOn(item => item.Id).IsNull.List<BookingWindow>();
So the first query gets issued to the database like so (the order item columns are selected which is a bit annoying but the real problem comes in a minute)
SELECT this_.Id as Id2_1_, this_.Price as Price2_1_, this_.RoomId as RoomId2_1_, this_.StartTime as StartTime2_1_, this_.EndTime as EndTime2_1_, bookingwin1_.Id as Id4_0_, bookingwin1_.Price as Price4_0_, bookingwin1_.Description as Descript4_4_0_, bookingwin1_.OrderId as OrderId4_0_, bookingwin1_.OrderItemParentId as OrderIte6_4_0_, bookingwin1_.OrderItemForeignId as OrderIte7_4_0_ FROM Customer.BookingWindows this_ left outer join Payment.OrderItem bookingwin1_ on this_.Id=bookingwin1_.OrderItemForeignId and bookingwin1_.OrderItemTypeId='1' WHERE this_.RoomId = ? and bookingwin1_.Id is null
But then for each booking window returned there is an extra select for the linked order item even though I haven't asked for it or need it. This happens within the query over method so I'm not doing any kind of iterating over the returned booking windows manually.
SELECT bookingwin0_.Id as Id4_0_, bookingwin0_.Price as Price4_0_, bookingwin0_.Description as Descript4_4_0_, bookingwin0_.OrderId as OrderId4_0_, bookingwin0_.OrderItemParentId as OrderIte6_4_0_, bookingwin0_.OrderItemForeignId as OrderIte7_4_0_ FROM Payment.OrderItem bookingwin0_ WHERE bookingwin0_.OrderItemForeignId=? and bookingwin0_.OrderItemTypeId='1'
Can anyone explain to me the error I have made here. Maybe its obvious but I've struggled for a few hours and at the end of my patience :)
I see one weird part in your mapping: Using References as a one-to-one mapping style. Maybe it is intended, but this is causing that issue you have.
Firstly, as documentation says [References / many-to-one][1]
References is for creating many-to-one relationships between two
entities, and is applied on the "many side." You're referencing a
single other entity, so you use the References method. #HasMany /
one-to-many is the "other side" of the References relationship, and
gets applied on the "one side."
Other words, in the table of the BookingWindowOrderItemMap you store reference to BookingWindow. It could mean (by the DB Design), that there could be more records of OrderItem, referencing the same BookingWindow. But maybe this is what you want, and you check "uniqueness" elsewhere. The more I tried to understand your problem, I would vote for moving the reference to OrderItem in a column in the BookingWindow
Problem revealed:
To your issue. When NHibernate recieves the list of BookingWindow, the next step is to build a proxy. In this process, all valueType/string properties are set, and for references... And for references NHibernate tries to prepare the lazy load.
Simplified version is, that into each property BookingWindowOrderItem BookingOrderItem is injected a promise for an instance of the BookingWindowOrderItem, to be returned when firstly touched. In standard cases, when mapping References is used, NHibernate in this moment already loaded from the table of the BookingWindow the ReferenceId.
In your case, this ReferenceID is represented by virtual, readonly 'current item ID'. The ID which definetly exists... but the reference does not! We've selected only BookingWindows which has NULL instead of the reference.
But we do have NOT NULL Reference ID (representd by Current instance ID).
And we've used .Left.JoinQueryOver. So NHibernate is sure, that it already loaded all data in the first query... but is confused, because in his session is no OrderItem with the id equal to BookingWindow.ID/ReferenceId
That's the reason (why it tries to fix it... and does load it again)
So this is the answer, why NHibernate does "weird selects". Not a suggestion how to fix it ;) it could be another question and answer...
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.
I finished following the summer of nhibernate screen casts and am trying to convert it to fluent, just for the sake of knowledge.
I have two classes (very simple)
public class Customer { ... }
public class PreferredCustomer : Customer { ... }
These follow table per sub class strategy and so the fluent mapping is so:
CustomerMap -
//nothing related to PreferredCustomer - the spec says not required
public class PreferredCustomerMap : SubclassMap<PreferredCustomer>
{
Map(x => x.CustomerSince);
Map(x => x.OrderDiscountRate);
}
Thats it.
My test is failing, upon inspection, its complaining that sql cant find column customer_id
this is the sql produced by hibernate:
SELECT customer0_.CustomerId as CustomerId1_0_,
customer0_.Version as Version1_0_,
customer0_.Firstname as Firstname1_0_,
customer0_.Lastname as Lastname1_0_,
customer0_1_.CustomerSince as Customer2_2_0_,
customer0_1_.OrderDiscountRate as OrderDis3_2_0_,
//here, customer0_1_.customer_id needs to be CustomerID really.
case when customer0_1_.Customer_id is not null then 1 when customer0_.CustomerId is not null then 0 end as clazz_0_
FROM [Customer] customer0_ left outer join [PreferredCustomer] customer0_1_ on customer0_.CustomerId=customer0_1_.Customerid
WHERE customer0_.Customer_Id=1
Its clearly doing that only on PreferredCustomer joined table. Cant find what needs to be done.
Any Ideas please?
edit: how do I read the xml's produced by fluent? that could be a good start.
You need a call to KeyColumn("CustomerID"); in your SubclassMaps.
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.
In this question I was answered hot to map a composed entity from the primary key of the table.
So given:
public UserMap()
{
WithTable("aspnet_Users");
Id(x => x.Id, "UserId")
.GeneratedBy.Guid();
Map(x => x.Name, "UserName");
Map(x => x.Login, "LoweredUserName");
WithTable("LdapUsers", m => {
m.Map(x => x.FullName, "FullName");
m.WithKeyColumn("UserId");
});
}
everithing works if in the "LdapUser" and in the "aspnet_Users" there is a column named "UserId".
What If I want to specify both the colum name for the foreign key table and the column name for the key from the main table as this is not the pk so use another column to do the join?
It looks like this is not currently supported at all in NHibernate. There is an open issue for it on NHibernate's JIRA. Actually, I think this is for collections but probably applies for what you're doing as well. The idea is you would add a property-ref attribute on your <key> element that points to the property you want to reference instead of the primary key.
Supposedly the change is already available in the Alpha2 release of NHibernate 2.1.0. Of course, it might be sometime after NH2.1 is a general release before this gets added to FNH. If you can, I would recommend getting a hold of the Alpha2 release and trying it with your situation. If it doesn't work for your composed entity, only collections, you might want to submit another issue on JIRA so it gets added as well.