Error Nhibernate System.Invalidcastexception - Manytoonetype to Componenttype

Asked

Viewed 265 times

3

Exception:

System.Invalidcastexception: It is not possible to 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:

  • In the database fk_classe_main in tb_SubClasse is primary_key;

  • The error appeared after I added the annotation ".GeneratedBy.Foreign("Classeprincipal.Codigo");".

  • The same occurs when I invoke the Session method. Save() right after the "select nextval('sequence');"

Thank you.

  • I’m not sure I understand, but your model says FK: fk_classe_principal, would also be the PK in the table tb_SubClasse, right? And you’d like to map that out to Nhibernate?

  • This, I realized that the error really happens due to annotation: ".GeneratedBy.Foreign("Classeprincipal.Codigo");". I’m trying to implement Generatedby.Hilo() to see the behavior. Do you know how it works? Because from this table that is "around" the main will be inserted first, and its id has to be the same as the main one.

  • I posted an answer, with the possible solution that I believe works for you, check if it is suitable to your problem and if it solves it?

2 answers

1


@Fernando managed to solve the problem. It was really the note:.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 solved by changing .Cascade.All() for .Cascade.SaveUpdate();. I did it both in the ClassePrincipal how much in () SubClasse.

In this way Nhibernate inserts the Classeprincipal (parent class) first and then the Subclasses (daughter classes).

Thanks for the tips!

  • Nice that you solved Pedro, good to know that was the problem, this can help me in the future!

1

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

I had this problem long ago and found this excellent solution at Soen.

To map a FK also as 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 make a hack, in your class in SubClasse, since Nhibernate tries to look for a field called "Code" in his 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

Maybe you should have to abandon the approach of Cascade.All() in the mapping of its ClassePrincipal, and do that Scade 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 time of persisting entities can do something on that 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);
  • Fernando, Nhibernate accuses null value for fk_classe_principal at the time of insertion. Do I need to make any changes to the model? The Insert where the error occurs is this: "INSERT INTO tb_subclass(attribute 1, attribute 2) VALUES(?, ?); select lastval()"; :)

  • Sorry, now that I’ve seen Edit. I did the test this way and it gives Nullreferenceexception as soon as I start the system. If I make a constructor for the subclass instantiating a main class object works? public subclass() { Classeprincipal = new Classeprincipal(); }

  • I couldn’t quite understand what might be happening, but I believe it’s because in my case, I don’t use .Cascade.All() in the Father class, and I saw that you are using, maybe if you can remove the HasOne of the mapping of ClassePrincipal and insert the class SubClasse in the hand just after inserting the ClassePrincipal, setting the attribute of the sub-class stating the ClassePrincipal recem salva may already work.

  • Okay, I’ll take this test now

  • @Pedro, check the edition in the reply, with details of my previous comment.

  • Ok, I will make the necessary changes and test. I will post the result soon

Show 1 more comment

Browser other questions tagged

You are not signed in. Login or sign up in order to post.