1

j'ai les classes suivantes définies:table par la sous-classe Fluent NHibernate ne fonctionne pas

enter image description here

Et ces tables dans ma base de données:

enter image description here

Mes couramment applications NHibernate sont:

public class BusinessUnitMap : ClassMap<BusinessUnit> 
{ 
    public BusinessUnitMap() 
    { 
     Table("BusinessUnits"); 

     Id(x => x.Id); 

     Map(x => x.Code); 
     Map(x => x.Name); 
     Map(x => x.ParentId); 
     Map(x => x.Type).Column("Type").CustomType<BusinessUnitType>(); 
    } 
} 

public class CompanyMap : SubclassMap<Company> 
{ 
    public CompanyMap() 
    { 
     Table("CompanyData"); 

     KeyColumn("BusinessUnitID"); 

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

public class FranchiseeMap : SubclassMap<Franchisee> 
{ 
    public FranchiseeMap() 
    { 
     Table("FranchiseeData"); 

     KeyColumn("BusinessUnitID"); 

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

public class StoreMap : SubclassMap<Store> 
{ 
    public StoreMap() 
    { 
     Table("StoreData"); 

     KeyColumn("BusinessUnitID"); 

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

Question # 1 Pour autant que je sache, mon code et ma base de données sont configurés de la même façon que tous les exemples que j'ai pu trouver. Selon ces articles, NHibernate est supposé être assez intelligent pour déterminer quelle sous-classe instancier quand je demande une sous-classe particulière. Mais, quand j'exécute la déclaration suivante:

var result = Session.QueryOver<BusinessUnit>() 
        .Where(x => x.Code == "Acme") 
        .SingleOrDefault(); 

une exception est levée car il ne peut pas créer une instance de la classe abstraite BusinessUnit. La seule façon de faire fonctionner cette fonction est de spécifier Company comme argument de type pour QueryOver.

J'ai confirmé que l'utilisation d'un discriminateur se brise puisque NHibernate recherche toutes les colonnes dans une seule table. Sans cela, j'ai du mal à voir comment NHibernate sait quel type instancier.

Qu'est-ce que je fais mal? Est-ce que le problème est dans mes mappings, la façon dont j'interroge, ...?

Question # 2 Lorsque je change la requête à quelque chose comme ceci:

public T WithCode<T>(String code) 
    where T : BusinessUnit 
{ 
    var result = Session.QueryOver<T>() 
         .Where(x => x.Code == code) 
         .SingleOrDefault(); 

    return result; 
} 

je reçois une exception indiquant que les conflits d'instruction UPDATE avec une contrainte de clé étrangère. Mise à jour !!!! Clairement, quelque chose ne va toujours pas. Comment un appel QueryOver peut-il entraîner une instruction UPDATE? Qu'est-ce que je rate?

+0

NHibernate utilise l'existance dans l'un des sous-tables comme indicateur qui classe à instancier. Il semble qu'il y ait une entrée acme qui n'a pas d'entrée correspondante dans l'une des autres tables. – Firo

+0

Vous ne savez pas comment vous avez eu cela. "Acme" existe dans les tables BusinessUnits et CompanyData. – SonOfPirate

+0

J'ai reproduit l'erreur que vous décrivez avec une entrée dans la table de base et une entrée manquante dans la sous-table. Peut-être qu'il existe d'autres scénarios qui présentent la même erreur – Firo

Répondre

1

Il semble que vos données ne soient pas cohérentes. Il pourrait être préférable d'utiliser la cartographie discriminante avec facultatif. Si vous avez vraiment pas besoin d'une propriété dans le code BusinessUnitType puis supprimez simplement tout autour de la propriété Type

public enum BusinessUnitType 
{ 
    Company, 
    Franchisee 
} 

public abstract class BusinessUnit 
{ 
    public virtual int Id { get; set; } 
    public virtual string Code { get; set; } 
    public virtual string Name { get; set; } 
    public virtual BusinessUnit Parent { get; set; } 
    public abstract BusinessUnitType Type { get; } 
} 

public class Company : BusinessUnit 
{ 
    public virtual string Something { get; set; } 

    public override BusinessUnitType Type { get { return BusinessUnitType.Company; } } 
} 

public class Franchisee : BusinessUnit 
{ 
    public virtual string SomethingDifferent { get; set; } 

    public override BusinessUnitType Type { get { return BusinessUnitType.Franchisee; } } 
} 

public class BusinessUnitMap : ClassMap<BusinessUnit> 
{ 
    public BusinessUnitMap() 
    { 
     Table("BusinessUnits"); 

     Id(x => x.Id); 

     Map(x => x.Code); 
     Map(x => x.Name); 
     References(x => x.Parent); 

     DiscriminateSubClassesOnColumn("Type"); 

     Map(x => x.Type, "Type") 
      .Access.None() 
      .CustomType<BusinessUnitType>().ReadOnly(); 
    } 
} 

public class CompanyMap : SubclassMap<StrangeTablePerSubclass.Company> 
{ 
    public CompanyMap() 
    { 
     DiscriminatorValue((int)new Company().Type); 

     Join("CompanyData", join => 
     { 
      join.KeyColumn("BusinessUnitID"); 
      join.Optional(); 
      join.Map(x => x.Something); 
     }); 
    } 
} 

public class FranchiseeMap : SubclassMap<Franchisee> 
{ 
    public FranchiseeMap() 
    { 
     DiscriminatorValue((int)new Franchisee().Type); 
     Join("FranchiseeData", join => 
     { 
      join.KeyColumn("BusinessUnitID"); 
      join.Optional(); 
      join.Map(x => x.SomethingDifferent); 
     }); 
    } 
} 
+0

Les discriminateurs exigent que toutes les données soient dans un seul tableau. Alors que l'échantillon est simple, c'est juste un échantillon et j'ai éliminé les nombreuses propriétés de chaque sous-classe. – SonOfPirate

+0

Voir le code. Alors que discriminant a tout dans une table, les éléments de jointure obtiennent les propriétés des autres tables mais peuvent spécifier Optional() pour le cas où il n'y a aucune entrée dans l'autre table – Firo

+0

Je reçois une exception quand j'exécute votre code parce que le BusinessUnits La colonne .Type dans la base de données est un entier et le code SQL généré le traite comme une chaîne. Des idées? J'ai mappé les énumérations aux entiers dans la base de données de la même manière que dans le passé sans problème, à l'exception de la partie .Access.None(). – SonOfPirate