How to create and use a generic class EntityTypeCo

2019-05-27 10:53发布

问题:

Code

I have two very simple interfaces in my application

Represents entities that are saved in the database

public interface IEntity
{
    int Id { get; set; }
}

Entities that have only the Nome field as required field to save the entity

public interface IEntityName : IEntity
{
    string Nome { get; set; }
}   

And many classes implementing this interface

public class Modalidade : IEntityName
{
    public int Id { get; set; }
    public string Nome { get; set; }
}
public class Nacionalidade : IEntityName
{
    public int Id { get; set; }
    public string Nome { get; set; }
}

public class Profissao : IEntityName
{
    public int Id { get; set; }
    public string Nome { get; set; }
}

public class TipoPessoa : IEntityName
{
    public int Id { get; set; }
    public string Nome { get; set; }
}

Although they the same structure, are completely different fields used for instance in this class

public class Pessoa : IEntity
{
    public int Id { get; set; }

    public string CPF { get; set; }
    public string PIS { get; set; }
    public string RG { get; set; }
    public string OrgaoExpedidor { get; set; }
    public string TipoDocumento { get; set; }
    public DateTime? DataEmissao { get; set; }

    public virtual Nacionalidade Nacionalidade { get; set; }
    public virtual Profissao Profissao { get; set; }

    public virtual TipoPessoa Tipo { get; set; }

    ....
}

Problem

Order to facilitate (and not have to create a configuration class for each new class) created a generic class configuration:

internal class EntityNameConfiguracao<TEntity> : EntityTypeConfiguration<TEntity>
    where TEntity: class, IEntityName
{
    public EntityNameConfiguracao()
    {
        Property(p => p.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
        Property(p => p.Nome).IsRequired().HasMaxLength(255);
    }
}

This way, my setup, would

modelBuilder.Configurations.Add(new EntityNameConfiguracao<Modalidade>());
modelBuilder.Configurations.Add(new EntityNameConfiguracao<TipoPessoa>());
modelBuilder.Configurations.Add(new EntityNameConfiguracao<Nacionalidade>());
modelBuilder.Configurations.Add(new EntityNameConfiguracao<Profissao>());   

Error

However, when trying to add a new migration, the following error occurs:

Command

Command in Package Manager Console: Add-Migration PessoaDadosAdicionais

Error

The property 'Id' is not a declared property on type 'Modalidade'. 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.

回答1:

I cannot explain why it doesn't work. It just doesn't. It doesn't work for EF 4.1 either.

This is especially surprising because it works when you convert the interfaces to abstract base classes:

public abstract class BaseEntity
{
    public int Id { get; set; }
}

public abstract class BaseEntityName : BaseEntity
{
    public string Nome { get; set; }
}

public class Modalidade : BaseEntityName
{
}

And then change the generic configuration to:

internal class EntityNameConfiguracao<TEntity> : EntityTypeConfiguration<TEntity>
    where TEntity: BaseEntityName
{
    public EntityNameConfiguracao()
    {
        Property(p => p.Id)
            .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
        Property(p => p.Nome).IsRequired().HasMaxLength(255);
    }
}

Now, adding a concrete configuration to the model builder doesn't throw an exception:

modelBuilder.Configurations.Add(new EntityNameConfiguracao<Modalidade>());

(You must not add BaseEntity and BaseEntityName to the model, for example by adding DbSets for these classes to the context or by providing their own configuration classes, otherwise you'll get the same exception again.)

Maybe, abstract base classes are alternatives for you. Otherwise, I am afraid, you need to create concrete configuration classes for every entity to get rid of the exception.