How to do the mapping (EntityTypeConfiguration) of this complex type?

2

The enumerators in my application follow the 'secure enumerator' design pattern, where I have the enumerator declaration as static members, and the enumerators instance following a contract (IEnumeratorSecure interface).

This is the default interface for all enumerators:

public interface IEnumeradorSeguro
{
    object Id { get; }
    String Descricao { get; }
}

An example of an enumerator:

public class ETipoAtividade : EnumeradorAbstrato<ETipoAtividade>
{
    public ETipoAtividade(object id, string descricao) : base(id, descricao)
    {}

    public ETipoAtividade(object id) : base(id)
    {}

    public static ETipoAtividade NovaImplementacao
    {
        get { return new ETipoAtividade((short)1, "Nova implementação");     }
    }

    public static ETipoAtividade Melhoria
    {
        get { return new ETipoAtividade((short)2, "Melhoria"); }
    }

    public static ETipoAtividade CorrecaoDeBug
    {
        get { return new ETipoAtividade((short)3, "Correção de bug"); }
    }

    public static ETipoAtividade Reengenharia
    {
        get { return new ETipoAtividade((short)4, "Reengenharia"); }
    }

}

Using the enumerator:

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
        {
            if(Projeto == null) Projeto = new Projeto();
            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();
    }
}

How am I doing the mapping via fluent-api:

public class AtividadeMap : EntityTypeConfiguration<Atividade>
{
    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);
        HasRequired(a => a.Projeto);
    }

}

I have already identified that the two enumerators (ETipoAtividade and EStatusAtividade) are complex types, but even so, an exception is thrown when DbContext invokes the model construction method:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);

        modelBuilder.Configurations.Add(new ProjetoMap());

        modelBuilder.ComplexType<EStatusAtividade>();
        modelBuilder.ComplexType<ETipoAtividade>();
        modelBuilder.Configurations.Add(new AtividadeMap());
        modelBuilder.Configurations.Add(new UsuarioMap());
    }

Exception:

An exception of type 'System.InvalidOperationException' occurred in                                EntityFramework.dll but was not handled in user code

Additional information: The property 'Identificador' is not a declared property on type 'EStatusAtividade'. Verify that the property has not been explicitly excluded from the model by using the Ignore method or NotMappedAttribute data annotation. Make sure that it is a valid primitive property.

Edit:

How do I get the enumerator instance from the one in the database:

atividade.StatusAtividade = EStatusAtividade.ObtenhaItem((short)1);

Edit2: Abstract Enumerator Code

[Serializable]
public abstract class EnumeradorAbstrato<T> : IEnumeradorSeguro where T :    IEnumeradorSeguro
{
    /// <summary>
    /// Neste construtor são passados como parametros o  Id e Descricao 
    /// </summary>
    /// <param name="id"></param>
    /// <param name="descricao"></param>

    protected EnumeradorAbstrato(object id, String descricao)
    {
        _id = id;
        _descricao = descricao;
    }
    /// <summary>
    /// Neste construtor são passados como parametros o  Id
    /// </summary>
    /// <param name="id"></param>
    protected EnumeradorAbstrato(object id)
    {
        _id = id;
        _descricao = id.ToString();
    }
    #region IEnumeradorSeguro Members
    private object _id; //declarando o id do enumerador
    private String _descricao; //declarando a descrição do enumerador
    /// <summary>
    /// Este metodo retorna o Id do enumerador
    /// </summary>
    public virtual object Id
    {
        get
        {
            return _id;
        }
    }

    static bool IsNumeric(string inputString)
    {
        return Regex.IsMatch(inputString, "^[0-9]+$");
    }

    /// <summary>
    /// Identificador para usar nos switch-case
    /// Só funciona quando o Id for números inteiros.
    /// </summary>
    public int Identificador
    {
        get
        {
            Func<string,int> funcId;
            funcId = idx => { 
                if (IsNumeric(idx))
                    return int.Parse(idx);
                throw new Exception("Id não numérico!");};
            return funcId(Id.ToString());
        }
    }
    /// <summary>
    /// Este metodo retorna a descrição do enumerador
    /// </summary>
    public string Descricao
    {
        get
        {
            return _descricao;
        }
    }

    #endregion
    /// <summary>
    /// neste metodo retorna a descrição de enumerador
    /// </summary>
    /// <returns></returns>
    public override string ToString()
    {
        return Descricao;
    }

    public static bool IsNull(T valor)
    {
        try
        {
            return String.IsNullOrEmpty(valor.ToString());
        }
        catch (Exception)
        {
            return true;
        }
    }

    // override object.Equals
    public override bool Equals(object obj)
    {
        if (obj == null || !(obj is EnumeradorAbstrato<T>))
        {
            return false;
        }

        return _id.Equals(((EnumeradorAbstrato<T>)obj)._id);
    }

    public static bool operator ==(EnumeradorAbstrato<T> obj1, object obj2)
    {
        if(!(obj2 is EnumeradorAbstrato<T>))
        {
            return false;
        }
        var tmpObj2 = (EnumeradorAbstrato<T>) obj2;
        if(tmpObj2.Equals(obj1 ))
        {
            return true;
        }
        return false;
    }

    public static bool operator !=(EnumeradorAbstrato<T> obj1, object obj2)
    {
        return !(obj1 == obj2);
    }

    /// <summary>
    /// Este metodo retorna o Id do enumerador
    /// </summary>
    /// <returns></returns>
    public override int GetHashCode()
    {
        return Id.GetHashCode();
    }

    public static T ObtenhaItem(object id)
    {
        return ObtenhaItem(typeof(T), id);
    }

    public static T ObtenhaItem(string descricao)
    {
        return ObtenhaItem(typeof (T), descricao);
    }

    public static IList<T> ObtenhaTodos()
    {
        return ObtenhaTodos(typeof(T));
    }

    /// <summary>
    /// Obtem lista de short com identificadores dos enumeradores de uma lista.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="enumeradores"></param>
    /// <returns></returns>
    public static List<short> ObtenhaIdentificadoresDeListaDeEnumeradores<T>(List<T> enumeradores)
        where T : IEnumeradorSeguro
    {
        var lista = new List<short>();

        foreach (var enumerador in enumeradores)
        {
            lista.Add((short)enumerador.Id);
        }

        return lista;
    }

    /// <summary>
    /// Obtem lista de enumeradores a partir de lista contendo identificadores.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="identificadores"></param>
    /// <returns></returns>
    public static List<T> ObtenhaListaDeEnumeradoresPorIdentificadores<T>(List<short> identificadores) where T: IEnumeradorSeguro
    {
        var lista = new List<T>();

        foreach (var identificador in identificadores)
        {
            var enumerador = EnumeradorAbstrato<T>.ObtenhaItem(typeof(T), identificador);
            lista.Add(enumerador);
        }

        return lista;
    } 

    /// <summary>
    /// Este metodo retorna a lista do enumerador passado
    /// </summary>
    /// <param name="tipoDoEnumerador"></param>
    /// <returns></returns>
    protected static IList<T> ObtenhaTodos(Type tipoDoEnumerador)
    {
        List<T> lista = new List<T>();

        foreach (var campo in tipoDoEnumerador.GetProperties())
        {
            if (campo.PropertyType.IsPublic && (campo.PropertyType.Name.Equals(typeof(T).Name) || campo.PropertyType.IsSubclassOf(typeof(T))))
            {
                T obj = default(T);
                obj = (T)campo.GetValue(obj, BindingFlags.GetProperty | BindingFlags.Static, null, null,
                                     CultureInfo.CurrentCulture);
                lista.Add(obj);
            }
        }

        return lista;
    }
    /// <summary>
    /// Passa o tipo do enumerador e o Id
    /// </summary>
    /// <param name="tipoDoEnumerador"></param>
    /// <param name="Id"></param>
    /// <returns></returns>
    protected static T ObtenhaItem(Type tipoDoEnumerador, object Id)
    {
        return ObtenhaItem(tipoDoEnumerador, Id, "Id");
    }


    /// <summary>
    /// Passa o tipo do enumerador e um valor string que pode ser o Id ou Descricao
    /// </summary>
    /// <param name="tipoDoEnumerador"></param>
    /// <param name="valor"></param>
    /// <returns></returns>
    protected static T ObtenhaItem(Type tipoDoEnumerador, string valor)
    {
        var item = ObtenhaItem(tipoDoEnumerador, valor, "Descricao");
        if (item == null) item = ObtenhaItem(tipoDoEnumerador, valor, "Id");
        return item;
    }

    /// <summary>
    /// "varre" para ver se encontra o tipo de numerador informado e o Id 
    /// </summary>
    /// <param name="tipoDoEnumerador"></param>
    /// <param name="valor"></param>
    /// <param name="nomeDaPropriedade"></param>
    /// <returns></returns>
    protected static T ObtenhaItem(Type tipoDoEnumerador, object valor, string nomeDaPropriedade)
    {
        foreach (T item in ObtenhaTodos(tipoDoEnumerador))
        {//se encontrado retorna o item 
            //if (item.GetType().GetField(NomeDaPropriedade).GetValue(item).Equals(Valor))
            //    return item;
            if (item.GetType().GetProperty(nomeDaPropriedade).GetValue(item, null).Equals(valor))
                return item;
        }

        return default(T); //É o mesmo que return null;
    }
}

Edit2: Status Code Activity

public class EStatusAtividade : EnumeradorAbstrato<EStatusAtividade>
{
    public EStatusAtividade(object id, string descricao) : base(id, descricao)
    {}

    public EStatusAtividade(object id) : base(id)
    {}

    public static EStatusAtividade NaoIniciado
    {
        get{ return new EStatusAtividade((short)1, "Não iniciado");}
    }

    public static EStatusAtividade Iniciado
    {
        get { return new EStatusAtividade((short)2, "Iniciado"); }
    }

    public static EStatusAtividade EmTestes
    {
        get { return new EStatusAtividade((short)3, "Em testes"); }
    }

    public static EStatusAtividade Concluido
    {
        get { return new EStatusAtividade((short)4, "Concluído"); }
    }

}

Edit 3: After adding the set to the Identifier property, I had this exception:

{"The class 'SabreControleDesenvolvimento.PrototipoCriador.Uteis.Enumeradores.EStatusAtividade' has no parameterless constructor."}

Is EF not recovering the value of this property in the bank?

    
asked by anonymous 16.07.2015 / 13:35

1 answer

2

You declare a mapping to a.TipoAtividade.Identificador , where TipoAtividade is of type ETipoAtividade .

ETipoAtividade does not contain a Identificador member. Would it be Id ?

That seems to be the error. Maybe your EnumeradorAbstrato<T> has member Identificador , but the code is not posted.

---- EDIT ----

The Identificador property does not have Setter ... EntityFramework computes both get and set to work.

---- EDIT ----

Regarding the error of the absence of constructor without parameters:

The entity framework requires entities to have a constructor without parameters. The values are collected in the database, but still it is necessary to create, or instantiate, the objects. For this, it is necessary to have a constructor.

Create an empty constructor, and it will be solved.

    
16.07.2015 / 13:58