Error NHibernate System.InvalidCastException - ManyToOneType to ComponentType

3

Exception:

  

System.InvalidCastException: Can not convert an object of type 'NHibernate.Type.ManyToOneType' to type 'NHibernate.Type.ComponentType'.

The classes involved are:

public class ClassePrincipal
{
    public virtual long Codigo { get; set; }
    public virtual string Atributo1 { get; set; }
    public virtual EEnumeradorAbstrato EnumeradorAbstrato { get; set; }
    public virtual SubClasse SubClasse { get; set; }
}

public class SubClasse
{
    public virtual string Atributo1 { get; set; }
    public virtual double Atributo2 { get; set; }

    /// <summary>
    ///     Bi-direcionamento
    /// </summary>
    public virtual ClassePrincipal ClassePrincipal { get; set; }
}

The mappings:

public class ClassePrincipalMap : ClassMap<ClassePrincipal>
{
    public ClassePrincipalMap()
    {
        Table("tb_classe_principal");

        Id(ClassePrincipal => ClassePrincipal.Codigo)
            .Not.Nullable()
            .Column("pk_classe_principal")
            .GeneratedBy.Sequence("tb_classe_principal_pk_classe_principal_seq");

        Map(a => a.Atributo1).Column("atributo1");

        Component(ClassePrincipal => ClassePrincipal.EnumeradorAbstrato, 
            componentPart =>
                componentPart.Map(eEnumeradorAbstrato => eEnumeradorAbstrato.Identificador, 
                "enumerador_abstrato"));

        HasOne(ClassePrincipal => ClassePrincipal.SubClasse)
            .Cascade.All()
            .Constrained()
            .ForeignKey("fk_classe_principal");
    }
}

public class SubClasseMap : ClassMap<SubClasse>
{
    public SubClasseMap()
    {
        Table("tb_SubClasse");

        Id(SubClasse => SubClasse.ClassePrincipal.Codigo)
            .Column("fk_classe_principal")
            .GeneratedBy.Foreign("ClassePrincipal.Codigo");

        Map(SubClasse => SubClasse.Atributo1).Column("atributo1");
        Map(SubClasse => SubClasse.Atributo2).Column("atributo2");

        References(SubClasse => SubClasse.ClassePrincipal, "fk_classe_principal").Cascade.None();
    }
}

Specifications:

  • The error occurred after I added the annotation ".GeneratedBy.Foreign (" MasterClass.Code ");
  • The same occurs when I invoke the session.Save () method right after the "select nextval ('sequence');"

Thank you.

    
asked by anonymous 16.09.2015 / 14:50

2 answers

1

@Fernando managed to solve the problem. It really was the annotation: .Cascade.All();

The model classes remained the same, what changed were the mapping classes.

public class ClassePrincipalMap : ClassMap<ClassePrincipal>
{
    public ClassePrincipalMap()
    {
        Table("tb_classe_principal");

        Id(ClassePrincipal => ClassePrincipal.Codigo)
            .Not.Nullable()
            .Column("pk_classe_principal")
            .GeneratedBy.Sequence("tb_classe_principal_pk_classe_principal_seq");

        Map(a => a.Atributo1).Column("atributo1");

        Component(ClassePrincipal => ClassePrincipal.EnumeradorAbstrato, 
            componentPart =>
                componentPart.Map(eEnumeradorAbstrato => eEnumeradorAbstrato.Identificador, 
                "enumerador_abstrato"));

        HasOne(ClassePrincipal => ClassePrincipal.SubClasse)
            // ALTERADO
            .Cascade.SaveUpdate()
            .ForeignKey("fk_classe_principal");
    }
}

public class SubClasseMap : ClassMap<SubClasse>
{
    public SubClasseMap()
    {
        Table("tb_SubClasse");

        Id(SubClasse => SubClasse.ClassePrincipal.Codigo)
            .Column("fk_classe_principal");

        Map(SubClasse => SubClasse.Atributo1).Column("atributo1");
        Map(SubClasse => SubClasse.Atributo2).Column("atributo2");

        References(SubClasse => SubClasse.ClassePrincipal, "fk_classe_principal")
        // ALTERADO
        .Cascade.SaveUpdate();
    }
}

The error was resolved by changing .Cascade.All() to .Cascade.SaveUpdate(); . I did this both in ClassePrincipal and in SubClasse .

In this way, NHibernate inserts the parent class first and then the subclasses.

Thanks for the tips!

    
17.09.2015 / 14:40
1

If my question is true, you want to map an FK ( SubClasse.ClassePrincipal.Codigo ), like FK and also with PK, in SubClasse , right?

I've had this problem a long time ago and found this great SOen solution .

To map an FK as a PK (single and simple), you have to do the following:

public SubClasseMap()
{
    Table("tb_SubClasse");

    // essas duas linhas subsequentes fazem a magica, explicada no link e já adaptada para o seu caso

    // aqui ele mapeia a propriedade/atributo como Id normalmente, sem especificar nenhuma estrategia de geração de Id, já que não há! (talvez a estrategia que se adequaria a esse cenário seria 'Assigned', mas não é necessario)
    Id(SubClasse  => SubClasse.ClassePrincipal.Codigo).Column("fk_classe_principal");
    // aqui estamos mapeando a propriedade como referencia, já se trata de uma chave estrangeira, e o 'ReadOnly' é o grande ponto, onde o **NHibernate** não tenta fazer nada com essa propriedade no momento de persistir, deixando para o Id, a persista na base de dados evitando conflitos. (na verdade sem o 'ReadOnly' gera uma exceção). 
    References(SubClasse  => SubClasse.ClassePrincipal).Column("fk_classe_principal").ReadOnly();

    // ...
}

Edit

There is a need to hack at class in SubClasse , since NHibernate tries to look for a field called "Code" in its class, so you have to do this:

public virtual long Codigo {
    get { return ClassePrincipal.Codigo ; }
    set { /* O set não precisa ser implementado */ }
}

Edit 2

You may have to abandon the Cascade.All() approach in mapping your ClassePrincipal , and make this cascade behavior in hand:

public ClassePrincipalMap()
{
    Table("tb_classe_principal");

    // ...

    // remover essa parte do mapeamento
    /* HasOne(ClassePrincipal => ClassePrincipal.SubClasse)
        .Cascade.All()
        .Constrained()
        .ForeignKey("fk_classe_principal"); */
}

At the moment of persisting the entity can do something in this line:

// criando objetos para exemplo
ClassePrincipal classePrincipal = new ClassePrincipal();
classePrincipal.Atributo1 = "Attributo 1";
SubClasse subClasse = new SubClasse();
subClasse.Atributo1 = "Sub 1";
subClasse.Atributo2 = 2.0d;
classePrincipal.SubClasse = subClasse;

// --- salvando objetos

// persiste a classe principal
Session.Save(classePrincipal);

// popula a subclasse interna da classe principal com a classePrincipal recém persistida e com o codigo já gerado (talvez haja forma melhor de fazer isso, mas é só para fins de exemplo)
classePrincipal.SubClasse.ClassePrincipal = classePrincipal;

// persiste agora a sub classe, já que não há mais a configuração de cascade
Session.Save(classePrincipal.SubClasse);
    
16.09.2015 / 15:02