Thanks for the thorough explanation. It was very helpful. I went on to
add one more discriminator (ClientUser) to the User mapping.
<class name="User" table="Users" abstract ="false">
<id name="Id" type="Int32" column="UserId">
<generator class="identity" />
</id>
<discriminator column="Type"/>
<subclass name="Employee" discriminator-value="1">
<many-to-one name="Role" column ="RoleId" class
="Role"/>
</subclass>
<subclass name="ClientUser" discriminator-value="2">
<many-to-one name="Client" column ="ClientId" class
="Client"/>
</subclass>
Then I started to realize that there is a database design problem.
Here is the User table (simplified).
UserId Name Type RoleId
ClientId
1 Mary 1 (employee) 1 NULL
2 John 1 2 NULL
3 Steve 2 (client's user) null
1
RoleId and ClientId are foreign keys. Based on the current design,
either foreign key will be null for all records. An employee has a
role but no client while a client's user has a client but no role. So,
I need some kinds of conditional foreign key. But it cannot be done in
the database according to my research on the Internet. I am wondering
whether I should split the User table into Employee table and
ClientUser table. Then I will have to change the NH inheritance
mapping. The only downside is that I will have to repeat all the
columns that the discriminator columns currently share. So, is there a
way to keep my current design? Or is splitting User table the better
way to go?
Thanks.
On Sep 3, 11:51 am, Anne Epstein <[email protected]> wrote:
> Looks like you have a problem where you identified your foreign key....
> <many-to-one name="Role" column ="*Id*" class ="Role"/>
> ....
> <bag name="Employees" cascade ="all-delete-orphan" inverse="true">
> <key column="*Id*" />
>
> What you're telling Nhibernate is that the the column "ID" of your Users
> table is also the foreign key to the Roles table. so, when you attach a role
> to the user, it tries to update the role foreign key, which, again, is also
> your primary key of the Users table, and it's complaining about that. What
> you want is probably something like:
>
> <many-to-one name="Role" column ="*RoleId*" class ="Role"/>
> ....
> <bag name="Employees" cascade ="all-delete-orphan" inverse="true">
> <key column="*RoleId*" />
>
> Also, you probably want inverse = "true" on your Employees bag... bags work
> a bit more efficiently this way, and it will allow you to add a constraint
> to your db table (without inverse=true, NH will add the employees first with
> a null role, then go in afterward and add the role in).
>
> <bag name="Employees" cascade ="all-delete-orphan" *
> inverse="true"*>
> <key column="RoleId" />
> <one-to-many class="Employee" />
> </bag>
> This will say the child is in control of propagating the relationship to the
> database
>
> Lastly, you don't show it, and you may have this already, but you'll want to
> make sure the bidirectional parent-child relationship is synched on both
> sides, thinks could get weird otherwise (this does not happen automatically
> to the objects in memory) so if you don't have them, you'll want to add
> methods that group both assignments together, perhaps something like this in
> Role:
>
> public virtual class AddEmployee(Employee employee){
> Employees.Add(employee);
> employee.Role = this;
>
> }
>
> public virtual class RemoveEmployee(Employee employee){
> Employees.Remove(employee);
> employee.Role = null;}
>
> (that won't delete the employee, only detach it from the role)
>
>
>
>
>
> On Thu, Sep 3, 2009 at 9:27 AM, Roger<[email protected]> wrote:
>
> > I have these domain models(simplified):
>
> > public class Role : Entity
> > {
> > public Role() { }
>
> > public virtual IList<Employee> Employees { get; protected
> set; }
> > }
>
> > public class Employee : User
> > {
> > public Employee() { Role = new Role(); }
>
> > public virtual Role Role { get; protected set; }
> > }
>
> > public class User : Entity
> > {
> > public virtual UserType Type { get; set; }//UserType is an
> > emum. i.e. Employee = 1
> > }
>
> > Here are the mappings. Employee is a discriminator.
>
> > <class name="User" table="Users" abstract ="false">
> > <id name="Id" type="Int32" column="Id">
> > <generator class="identity" />
> > </id>
> > <discriminator column="Type"/>
> > <property name="Type"/>
> > <subclass name="Employee" discriminator-value="1">
> > <many-to-one name="Role" column ="Id" class
> ="Role"/>
> > </subclass>
>
> > <class name="Role" table="Roles">
> > <id name="Id" type="Int32" column="Id">
> > <generator class="identity" />
> > </id>
>
> > <bag name="Employees" cascade ="all-delete-orphan">
> > <key column="Id" />
> > <one-to-many class="Employee" />
> > </bag>
> > </class>
>
> > When I called IRepository.SaveOrUpdate(employee), I kept getting an
> > error. The generated sql query was trying to update the primary key of
> > the User table. That's why it failed. The problem was solved when I
> > removed the column and class attributes of the many-to-one element:
>
> > <many-to-one name="Role" />
>
> > Does the column or the class attribute do anything special to table
> > per hierarchy mapping? Is that an expected behavior?
>
> > Thanks.- Hide quoted text -
>
> - Show quoted text -
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups
"nhusers" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to
[email protected]
For more options, visit this group at
http://groups.google.com/group/nhusers?hl=en
-~----------~----~----~----~------~----~------~--~---