Fluent NHibernate Collection Table-Per-Subclass

2019-06-28 07:30发布

问题:

I am having trouble with inheritance in Fluent NHibernate.

I have managed to get Table-Per-Subclass working before but I can't get a collection of the baseclass to work. It is looking for a table for the base class which I don't have.

Here's my schema:

User(Id, Name, Email)

User_Account_Password(Id, UserId, PasswordHash, Salt)

User_Account_Facebook(Id, UserId, FacebookId)

Here's my object model:

public class User
{
  public virtual int Id { get; set; }
  public virtual string Name { get; set; }
  public virtual string Email { get; set; }
  public virtual IList<UserAccount> Accounts { get; set; }
}

public abstract class UserAccount
{
  public virtual int Id { get; set; }
  public virtual User User { get; set; }
}

public class UserAccountPassword : UserAccount
{
  public virtual string PasswordHash { get; set; }
  public virtual string Salt { get; set; }
}

public class UserAccountFacebook : UserAccount
{
  public virtual long FacebookId { get; set; }
}

Here's the mapping files:

public class UserMap : ClassMap<User>
{
   public UserMap()
   {
      Table("user");

      Id(x=>x.Id);

      Map(x=>x.Name);
      Map(x=>x.Email);

      //This is the collection I am having trouble with.
      HasMany(x=>x.Accounts)
       .KeyColumn("UserId")
       .Cascade.SaveUpdate()
       .Not.LazyLoad();
   }
}

public class UserAccountMap : ClassMap<UserAccount>
{
    public UserAccountMap()
    {
       Id(x=>x.Id);

       References(x => x.User)
          .Column("UserId");
    }
}

public class UserAccountPasswordMap : SubclassMap<UserAccountPassword>
{
    public UserAccountPasswordMap()
    {
        Table("user_account_password");

        Map(x=>x.PasswordHash);
        Map(x=>x.Salt);
    }
}

public class UserAccountFacebookMap : SubclassMap<UserAccountFacebook>
{
    public UserAccountFacebookMap()
    {
         Table("user_account_facebook");

         Map(x=>x.FacebookId);
    }
}

This would work fine if I had on user a collection of Facebook accounts and a collection of password accounts. However, the idea is to make these link to the user so that we can later allow people to not use facebook or to add facebook as a login method but to keep the same account.

The error I'm getting is "Table myschema.useraccount" does not exist. Which suggests it is ignoring the fact UserAccount is abstract..

This seems to be the recommended way to do Table-Per-Subclass according to http://wiki.fluentnhibernate.org/Fluent_mapping#Subclasses but it feels like I'm missing something.

Any ideas?

回答1:

Table-per-subclass is anti-intuitive name because a table is required for the base class (or super class) as well (see NH documentation). If the useraccount table does not exist, NH throws exception as in your case.

If you work with legacy database and a base class table is not available, then you could use table-per-concrete-class or so called union-subclass mapping:

public class UserAccountMap : ClassMap<UserAccount>
{
    public UserAccountMap()
    {
        Id(x => x.Id).GeneratedBy.Assigned(); // cannot use identity generator with union-subclass mapping

        References(x => x.User)
           .Column("UserId");

        this.UseUnionSubclassForInheritanceMapping(); // tell FNH that you are using table per concrete class mapping
    }
}

EDIT: UserAccount table must be present in database schema to use table-per-subclass mapping. The table must contain two columns: Id and UserId.