I have a parent-child relationship that I've put a test case together between Users and Groups. I did this to replicate a failure in a Parent-Child relationship when trying to perform a cacade insert using thes relationship.
The two SQL tables are as follows:
CREATE TABLE [dbo].[User]
(
[Id] [int] IDENTITY(1,1) NOT NULL PRIMARY KEY,
[Name] [varchar](50) NOT NULL,
)
CREATE TABLE [dbo].[Group]
(
[Id] [int] IDENTITY(1,1) NOT NULL PRIMARY KEY,
[GroupName] [varchar](50) NOT NULL,
[UserId] [int] NOT NULL,
)
ALTER TABLE [dbo].[Group] WITH CHECK ADD CONSTRAINT [FK_Group_User] FOREIGN KEY([UserId])
REFERENCES [dbo].[User] ([Id])
The objects represent these two tables with the following mappings:
public class UserMap : ClassMap<User>
{
public UserMap()
{
Table("[User]");
Id(x => x.Id).GeneratedBy.Identity();
Map(x => x.Name).Not.Nullable();
HasMany(x => x.Groups).KeyColumn("UserId").Cascade.SaveUpdate();
}
}
public class GroupMap : ClassMap<Group>
{
public GroupMap()
{
Table("[Group]");
Id(x => x.Id).GeneratedBy.Identity();
Map(x => x.GroupName).Not.Nullable();
References(x => x.User).Column("UserId").Not.Nullable();
}
}
The code to created the objects is simply:
User u = new User() { Name = "test" }; Group g = new Group() { GroupName = "Test Group" }; u.Groups.Add(g);
using (var session = factory.OpenSession())
{
session.SaveOrUpdate(u);
}
However it fails with exception "Cannot insert the value NULL into column 'UserId', table 'test.dbo.Group'; column does not allow nulls. INSERT fails. The statement has been terminated". I suspect that this is dude to the parent object's Id (an identity column) being passed through as NULL and not the new values. Is this a bug or is there a way to fix these mappings so that this casca开发者_如何学编程de relationship succeeds?
I recently had this exact type of mapping working fine in a project. My advice is:
- Learn how the Inverse attribute of a HasMany relationship works. Great explanation here
- You need a two way association between the parent and child object. This is explained at the bottom of the article linked to above.
Another good advice is to encapsulate your collections better - Don't access your collections modification methods directly. The collection properties should be read-only and the parent (User class in your case) should have AddGroup() and RemoveGroup() methods that changes the private collection. In order for this to work you have to let NHibernate access the private collection member by using the .Access.CamelCaseField(Prefix.Underscore) or similar mapping attribute. Good discussion about it here
I can post an example mapping and class files if needed.
You will have to save the user first then assign the group to the user and save that:
using (var session = factory.OpenSession())
{
User u = new User() { Name = "test"};
session.SaveOrUpdate(u);
Group g = new Group() { GroupName = "Test Group", User = u };
session.SaveOrUpdate(g)
}
I have found that you cannot cascade save child /parent related objects which have only just been created.
精彩评论