views:

400

answers:

2

I'm polling mulitple systems (domains) for security info so I'm dealing domainUsers and their roles. I've got my entities setup as show below, but I'm having trouble setting up the domainUser.HasMany relationship in the AutoMapper override.

You'll notice that I don't have domainUser.DomainUserId and role.RoleId which make this much more simple (no compositeIds.) I've avoided those fields because I've already got a natural composite key and it will be populate when I pull this data from the downstream domain. If I add those artificial keys, I'll have to pre-fetch their values before I call session.Merge(domainUser). I'm trying to avoid doing that.

The entity objects are obvious (I hope) but here is what I've got.

public class DomainUser 
   {
      public virtual int Domain_Id { get; set; }
      public virtual string DomainUserLogin { get; set; }
      public virtual string EmployeeId { get; set; }

      // extra field removed for breviety


      public DomainUser()
      {
         this.Roles = new List<DomainUserRole>();
      }

      public virtual void AddRole(DomainUserRole role)
      {
         role.DomainUser = this;
         this.Roles.Add(role);
      }

      // overrides for equals and getHashCode
   }

and

   public class DomainUserRole
   {
      public virtual DomainUser DomainUser { get; set; }
      public virtual string DataSegment { get; set; }  // Some group of data a user has access to, like US or China
      public virtual string RoleName { get; set; }
      public virtual string RoleDescription { get; set; }

      // extra field removed for breviety

      // overrides for equals and getHashCode
   }

My db schema is pretty simple.

alt text

I've got the IAutoMappingOverride classes started like this. But, I'm at a loss of how to setup the hasMany for roles. It keeps giving me

NHibernate.FKUnmatchingColumnsException: 
   Foreign key (FK20531BE4163641BB:tblDomainUserRoles [DomainUser])) 
   must have same number of columns as the referenced primary key 
   (tblDomainUsers [Domain_Id, DomainUserLogin]). 

How do I setup that foreign key to use both those fields?

   public class DomainUserMap : IAutoMappingOverride<DomainUser>
   {
      public void Override(AutoMapping<DomainUser> mapping)
      {
         mapping.CompositeId()
            .KeyProperty(user => user.Domain_Id, "Domain_Id")
            .KeyProperty(user => user.DomainUserLogin, "DomainUserLogin");

         // I"ve tried this.
         // mapping.HasMany(x => x.Roles)
         //   .KeyColumns.Add("Domain_Id")
         //   .KeyColumns.Add("DomainUserLogin");

         //  also tried this where I define this FK in DB with both fields.
         // mapping.HasMany(x => x.Roles)
         //   .ForeignKeyConstraintName("FK_tblDomainUserRoles_tblDomainUsers")

      }
   }

    public class DomainUserRoleMap : IAutoMappingOverride<DomainUserRole>
       {
          public void Override(AutoMapping<DomainUserRole> mapping)
          {
             mapping.CompositeId()
                .KeyReference(role => role.DomainUser)
                .KeyProperty(role => role.DataSegment)
                .KeyProperty(role => role.RoleName);         
          }
       }
A: 

You may want to use a composite key class. Here's an example of using a key class: http://stackoverflow.com/questions/1329068/nhibernate-composite-key-class-type-mismatch. You may have to drop down and use an HBM file instead of using Fluent NHibernate however.

lcranf
A: 

I did endup hand editing the hbm files. It looks like the most recent build on fluent Nhibernate addresses this issue. Here is what my hbm file look like.

<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" default-access="property" auto-import="true" default-cascade="none" default-lazy="true">
  <class xmlns="urn:nhibernate-mapping-2.2" name="AAA.Core.Entities.DomainUser, AAA.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" table="`tblDomainUsers`">
    <composite-id mapped="false" unsaved-value="undefined">
      <key-property name="Domain_Id" type="System.Int32, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
        <column name="Domain_Id" />
      </key-property>
      <key-property name="DomainUserLogin" type="System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
        <column name="DomainUserLogin" />
      </key-property>
    </composite-id>
    ... properties hidden for breviety
    <bag inverse="true" cascade="all-delete-orphan" lazy="false" name="Roles">
      <key>
        <column name="Domain_Id" />
        <column name="DomainUserLogin" />
      </key>
      <one-to-many class="AAA.Core.Entities.DomainUserRole, AAA.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
    </bag>
  </class>
</hibernate-mapping>

here is the file for roles.

<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" default-access="property" auto-import="true" default-cascade="none" default-lazy="true">
  <class xmlns="urn:nhibernate-mapping-2.2" name="AAA.Core.Entities.DomainUserRole, AAA.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" table="`tblDomainUserRoles`">
    <composite-id mapped="false" unsaved-value="undefined">
      <key-property name="DataSegment" type="System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
        <column name="DataSegment" />
      </key-property>
      <key-property name="RoleName" type="System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
        <column name="RoleName" />
      </key-property>
      <key-many-to-one name="DomainUser" class="AAA.Core.Entities.DomainUser, AAA.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null">
        <column name="Domain_Id" />
        <column name="DomainUserLogin" />
      </key-many-to-one>
    </composite-id>
     ... properties hidden for breviety
  </class>
</hibernate-mapping>
DanielEli