One-To-Many relation with explicit Foreign key name

Asked

Viewed 369 times

2

I’m performing a simple mapping of one-to-Many via Fluent API specifying which column is used in the mapping, however EF "ignores" the configuration and continues using the name default.

Mappings:

        public AtividadeMap()
        {
            HasKey(a => a.Codigo);

            ToTable("tb_atividade");

            Property(a => a.TituloAtividade).HasColumnName("titulo_atividade");
            Property(a => a.DescricaoAtividade).HasColumnName("descricao_atividade");
            Property(a => a.DataHoraInicio).HasColumnName("data_hora_inicio");
            Property(a => a.DataHoraFim).HasColumnName("data_hora_fim");
            Property(a => a.Codigo).HasColumnName("pk_atividade");
            Property(a => a.StatusAtividade.Identificador).HasColumnName("status_atividade");
            Property(a => a.EstimativaInicialAtividade).HasColumnName("estimativa_inicial_atividade");
            Property(a => a.TipoAtividade.Identificador).HasColumnName("tipo_atividade");

            HasRequired(a => a.Usuario)
           .WithMany()
           .Map(e => e.MapKey("fk_usuario"));

            HasRequired(a => a.Projeto)
           .WithMany()
           .Map(e => e.MapKey("fk_projeto"));

        }


        public ProjetoMap()
        {
            HasKey(a => a.Codigo);

            ToTable("tb_projeto");

            Property(p => p.Codigo).HasColumnName("pk_projeto");
            Property(a => a.Nome).HasColumnName("nome");
        }

Exception:

Exceção

As noted, SQL is performing a search for CodigoProjeto and CodigoUsuario instead of fk_projeto and fk_usuario

Besides the exception, a doubt: I need to map this one-to-Many also in the aggregation classes (Project and User)?

If possible, I would prefer to map only the class that consumes the entity, aiming at the isolation of responsibility only for this class.


---- EDIT : Inclusion of models related to the problem ------

    public class Atividade : ObjetoPersistente
    {
        public Atividade()
        {
            Projeto = new Projeto();
            StatusAtividade = EStatusAtividade.NaoIniciado;
            TipoAtividade = ETipoAtividade.NovaImplementacao;
            Usuario = new Usuario();
        }       

        public DateTime DataHoraFim { get; set; }

        public DateTime DataHoraInicio { get; set; }

        public string DescricaoAtividade { get; set; }
        public string EstimativaInicialAtividade { get; set; }
        public Projeto Projeto { get; set; }

        public long CodigoProjeto 
        {
            get
            {
                return Projeto.Codigo;
            }
            set
            {
                Projeto.Codigo = value;
            }
        }

        public string NomeProjeto
        {
            get { return Projeto.Nome; }
        }

        public Usuario Usuario { get; set; }

        public string LoginUsuario
        {
            get { return Usuario.Login; }
        }

        public long CodigoUsuario 
        {
            get
            {
                if(Usuario == null) Usuario = new Usuario();
                return Usuario.Codigo;
            }
            set
            {
                Usuario.Codigo = value;
            }
        }

        public EStatusAtividade StatusAtividade { get; set; }

        public string DescricaoStatusAtividade
        {
            get { return StatusAtividade.Descricao; }
        }           

        public string DescricaoTipoAtividade
        {
            get { return TipoAtividade.Descricao; }
        }

        public ETipoAtividade TipoAtividade { get; set; }
        public string TituloAtividade { get; set; }


        public override bool Equals(object obj)
        {
            return (obj is Atividade) && (obj as Atividade).Codigo.Equals(Codigo);
        }

        public override int GetHashCode()
        {
            return Codigo.GetHashCode();
        }
    }


    public class Projeto : ObjetoPersistente, IObjetoElementoOption
    {       
        public string Nome { get; set; }                

        public override bool Equals(object obj)
        {
            return (obj is Projeto) && (obj as Projeto).Codigo.Equals(Codigo);
        }

        public override int GetHashCode()
        {
            return Codigo.GetHashCode();
        }

        public string Valor
        {
            get { return Codigo.ToString(); }
        }

        public string Descricao 
        {
            get { return Nome; }
        }
    }

    public class Usuario : ObjetoPersistente
    {       
        public string Login { get; set; }
        public string Senha { get; set; }


        public override bool Equals(object obj)
        {
            return (obj is Usuario) && (obj as Usuario).Codigo.Equals(Codigo);
        }

        public override int GetHashCode()
        {
            return Codigo.GetHashCode();
        }
    }

    [Serializable]
    public abstract class ObjetoPersistente : IObjetoPersistente
    {
        public long Codigo { get; set; }

        public override bool Equals(object obj)
        {
            return (obj is ObjetoPersistente) && (obj as ObjetoPersistente).Codigo.Equals(Codigo);
        }

        public override int GetHashCode()
        {
            return Codigo.GetHashCode();
        }

        /// <summary>
        /// Obtêm lista de longos a partir dos códigos dos objetos persistentes.
        /// </summary>
        /// <param name="lista"></param>
        /// <returns></returns>
        public static List<long> ObtenhaListaDeCodigosPorListaDeObjetos<T>(List<T> lista) where T:ObjetoPersistente
        {
            var listaLongo = new List<long>();

            foreach (var objetoPersistente in lista)
            {
                listaLongo.Add(objetoPersistente.Codigo);
            }

            return listaLongo;
        }

        /// <summary>
        /// Obtêm lista de objetos do tipo genérico a partir de lista de códigos
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="listaDeCodigos"></param>
        /// <returns></returns>
        public static List<T> ObtenhaListaDeObjetoPorListaDeCodigos<T>(List<long> listaDeCodigos) where T  : ObjetoPersistente
        {
            var listaObjeto = new List<T>();

            foreach (var codigo in listaDeCodigos)
            {
                var objeto = Activator.CreateInstance<T>();
                objeto.Codigo = codigo;

                listaObjeto.Add(objeto);
            }

            return listaObjeto;
        }

        /// <summary>
        /// Obtêm objetos persistentes da lista que possuem a propriedade Código equivalente a 0.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="objetos"></param>
        /// <returns></returns>
        public static List<T> ObtenhaObjetosComCodigoZerado<T>(List<T> objetos) where T : ObjetoPersistente
        {
            var resultado = new List<T>();

            foreach (var objeto in objetos)
            {
                if(objeto.Codigo.Equals(0))
                    resultado.Add(objeto);
            }

            return resultado;
        }

        /// <summary>
        /// Obtêm objetos persistentes da lista que possuem a propriedade Código diferente de 0.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="objetos"></param>
        /// <returns></returns>
        public static List<T> ObtenhaObjetosComCodigoDiferenteDeZero<T>(List<T> objetos) where T : ObjetoPersistente
        {
            var resultado = new List<T>();

            foreach (var objeto in objetos)
            {
                if (!objeto.Codigo.Equals(0))
                    resultado.Add(objeto);
            }

            return resultado;
        }

        /// <summary>
        /// Compara duas listas de objetos persistentes
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="listaBase"></param>
        /// <param name="listaAnalisada"></param>
        /// <returns>Os objetos persistentes da 'listaAnalisada' que não existem na 'listaBase'</returns>
        public static List<T> ObtenhaDiferenteEntreListas<T>(List<T> listaBase, List<T> listaAnalisada)
            where T : ObjetoPersistente
        {
            var resultado = new List<T>();

            foreach (var objetoPersistente in listaAnalisada)
            {
                if(!listaBase.Contains(objetoPersistente))
                    resultado.Add(objetoPersistente);
            }

            return resultado;
        }
    }

---- EDIT2 : Details on the moment the Exception is fired ----

Exception occurs the moment I perform a find on Dbset:

_dbContext.Set<Atividade>.Find(codigoItem);

The SQL that the EF produced was this:

SELECT "Alias1"."status_atividade", "Alias1"."pk_atividade", "Alias1"."data_hora_fim", "Alias1"."data_hora_inicio", "Alias1"."descricao_atividade", "Alias1"."estimativa_inicial_atividade", "Alias1"."CodigoProjeto", "Alias1"."CodigoUsuario", "Alias1"."tipo_atividade", "Alias1"."titulo_atividade", "Alias1"."fk_projeto", "Alias1"."fk_usuario" FROM (SELECT "Extent1"."pk_atividade", "Extent1"."data_hora_fim", "Extent1"."data_hora_inicio", "Extent1"."descricao_atividade", "Extent1"."estimativa_inicial_atividade", "Extent1"."CodigoProjeto", "Extent1"."CodigoUsuario", "Extent1"."status_atividade", "Extent1"."tipo_atividade", "Extent1"."titulo_atividade", "Extent1"."fk_projeto", "Extent1"."fk_usuario" FROM "public"."tb_atividade" AS "Extent1" WHERE "Extent1"."pk_atividade" = ((4)) LIMIT 2) AS "Alias1"

The message of the exception:

ERROR: 42703: column Extent1.CodigoProjeto does not exist

PS: I am using Postgresql.

  • I wanted to understand why you make a point of changing the names of the columns in the bank.

  • There is a nomenclature standard for the database, and I am working on migrating a system that is already in production with ADO.NET (pure sql).

  • Not use Fluent API is an option?

  • It is not feasible. Since it is a migration, it is simpler to isolate the code that has this responsibility. Unable to load templates with more annotations.

  • But why not use Fluent API ? It cannot solve this particularide?

  • You can, but you don’t solve some cases very well. I’ll think of an answer. I need Models in the body of the question.

  • Okay, they were included. Disregard the access properties, they are not being used (bridges to Project.Code, for example). Could you explain more about 'but it doesn’t solve some cases very well' ? What are the situations?

  • Situations of direct association, indirect association, mapping and names, finally. Your Models are quite outside the standard of the Entity Framework, so the chance of it not working is quite large. What command is used to run the query raising Exception?

  • Well, I am making proof of concept in relation to the RU, to conclude on the feasibility or not of its use in this project. My goal is to use an ORM framework to improve. I will have models in the most diverse ways possible, with polymorphism strategies (interfaces) for example. I’m including what you asked the question.

Show 4 more comments

1 answer

1

Let’s start with the generated SQL:

SELECT "Alias1"."status_atividade", "Alias1"."pk_atividade",
"Alias1"."data_hora_fim", "Alias1"."data_hora_inicio",
"Alias1"."descricao_atividade", "Alias1"."estimativa_inicial_atividade",
"Alias1"."CodigoProjeto", "Alias1"."CodigoUsuario",
"Alias1"."tipo_atividade", "Alias1"."titulo_atividade",
"Alias1"."fk_projeto", "Alias1"."fk_usuario" FROM 
    (SELECT "Extent1"."pk_atividade", "Extent1"."data_hora_fim",
    "Extent1"."data_hora_inicio", "Extent1"."descricao_atividade",
    "Extent1"."estimativa_inicial_atividade", "Extent1"."CodigoProjeto", 
    "Extent1"."CodigoUsuario", "Extent1"."status_atividade",
    "Extent1"."tipo_atividade", "Extent1"."titulo_atividade",
    "Extent1"."fk_projeto", "Extent1"."fk_usuario" 
    FROM "public"."tb_atividade" AS "Extent1" 
    WHERE "Extent1"."pk_atividade" = ((4)) LIMIT 2) AS "Alias1"

Notice that at no time do you reference CodigoProjeto and CodigoUsuario here:

    public AtividadeMap()
    {
        HasKey(a => a.Codigo);

        ToTable("tb_atividade");

        Property(a => a.TituloAtividade).HasColumnName("titulo_atividade");
        Property(a => a.DescricaoAtividade).HasColumnName("descricao_atividade");
        Property(a => a.DataHoraInicio).HasColumnName("data_hora_inicio");
        Property(a => a.DataHoraFim).HasColumnName("data_hora_fim");
        Property(a => a.Codigo).HasColumnName("pk_atividade");
        Property(a => a.StatusAtividade.Identificador).HasColumnName("status_atividade");
        Property(a => a.EstimativaInicialAtividade).HasColumnName("estimativa_inicial_atividade");
        Property(a => a.TipoAtividade.Identificador).HasColumnName("tipo_atividade");

        HasRequired(a => a.Usuario)
       .WithMany()
       .Map(e => e.MapKey("fk_usuario"));

        HasRequired(a => a.Projeto)
       .WithMany()
       .Map(e => e.MapKey("fk_projeto"));

    }

It’s obviously not gonna work. Since the two properties are not part of the query (because they are not data fields, but rather navigation properties you have defined yourself), you need to mark these properties with [NotMapped] to remove the columns from the query.

    [NotMapped]
    public long CodigoProjeto {
        get
        {
            return Projeto.Codigo;
        }
        set
        {
            Projeto.Codigo = value;
        }
    }

    [NotMapped]
    public long CodigoUsuario {
        get
        {
            if(Usuario == null) Usuario = new Usuario();
            return Usuario.Codigo;
        }
        set
        {
            Usuario.Codigo = value;
        }
    }
  • I will make the Gypsy modification, but then I ask you, the mapping in Fluent-API is not aimed to clarify which properties I am working in the context of persistence? Missing I set some configuration in my Dbcontext to guide you to use only what is mapped?

  • I didn’t really analyze the generated SQL, it is generating the fk_user and fk_project perfectly, but it is also generating the 'Codigousuario' and 'Codigoprojeto'. That’s exactly what it is. Do I need to define a configuration that tells EF that, I just want it to work with what is mapped in the Entitytypeconfiguration class? I will search now. Because as I mentioned, I am in the process of migration, with the models already implemented. Defining properties that should not be mapped, one by one, will be a setback. I can also ignore all properties via reflection, but it would be bad.

  • @Joaquimmagalhães The most recommended mapping, speaking from experience, is that of attributes, in which you decorate the properties of Model in question. The [NotMapped] is a good example of this. Use Fluent API to set up everything becomes somewhat prolix in a more advanced stage of development. In your case, the settings are already set accordingly, and what happens is that the generated SQL is a default behavior of the Entity Framework: it deduces that each property exists in a database, without necessarily checking their implementation. You even tested as the answer?

  • Yes, it worked. But I was disappointed by the fact that, as I am already making explicit each property, expected the RU not to make decisions on its own'.

  • I am just waiting for an answer on the context of the question to conclude it: There is a property that defines EF use only mapped properties in my Entitytypeconfiguration configuration class?

  • About using Fluentapi or annotations, well, I think I would have to give you a lot of detail about the project and I would extend the subject a lot, maybe get away from the context of this question. It seems to me that there is a CHAT feature , if you are interested I can describe you there.

  • @JoaquimMagalhães http://chat.stackexchange.com/rooms/25342/c-asp-net-asp-net-mvc-entity-framework-etc

Show 2 more comments

Browser other questions tagged

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