Look, avoid using FluentAPI
for the most basic cases, in this case it is best to use DataAnnotations
:
Below an implementation, note that in EntityBase I'm using EntityFramework.Triggers
to automate the update of some fields.
// Não esqueça do abstract
public abstract class EntityBase
{
// Acredite, colocar o ID na classe base não é uma boa.
// Como você faria com uma entidade com chave multipla
// public Guid EntityID { get; set; }
public Guid UsuarioCriacaoID { get; set; }
public Guid UsuarioAtualizacaoID { get; set; }
public DateTime DataCriacao { get; set; }
public DateTime DataAtualizacao { get; set; }
public bool IsDeleted { get; set; }
[ForeignKey("UsuarioCriacaoID")]
public virtual Pais UsuarioCriacao { get; set; }
[ForeignKey("UsuarioAtualizacaoID")]
public virtual Pais UsuarioAtualizacao { get; set; }
static EntityBase()
{
Triggers<EntityBase>.Inserting += entry =>
{
var contexto = entry.Context as MyContext;
entry.Entity.DataCriacao = DateTime.Now;
entry.Entity.DataAtualizacao = DateTime.Now;
entry.Entity.UsuarioCriacaoID = contexto.UsuarioID;
entry.Entity.UsuarioAtualizacaoID = contexto.UsuarioID;
entry.Entity.IsDeleted = false;
};
Triggers<EntityBase>.Updating += entry =>
{
var contexto = entry.Context as MyContext;
entry.Entity.DataCriacao = DateTime.Now;
entry.Entity.DataAtualizacao = DateTime.Now;
entry.Entity.UsuarioCriacaoID = contexto.UsuarioID;
entry.Entity.UsuarioAtualizacaoID = contexto.UsuarioID;
entry.Entity.IsDeleted = false;
};
Triggers<EntityBase>.Deleting += entry =>
{
var contexto = entry.Context as Contexto;
entry.Entity.IsDeleted = true;
entry.Cancel = true;
};
}
}
[Table("Usuarios")]
public class Usuario
{
[Key]
public Guid UsuarioID { get; set; }
[Required]
[Index(IsUnique=true)]
[MaxLength(50)]
public string Logon { get; set; }
[Required]
[MaxLength(64)]
public byte[] Senha { get; set; }
[Required]
[MaxLength(16)]
public byte[] Salt { get; set; }
}
[Table("Paises")]
public class Pais : EntityBase
{
[Key]
public Guid PaisID { get; set; }
[Required]
[MaxLength(150)]
public string Descricao { get; set; }
[Required]
[MaxLength(3)]
public string Sigla { get; set; }
public virtual ICollection<Estado> Estados { get; set; } = new List<Estado>();
}
[Table("Estados")]
public class Estado : EntityBase
{
[Key]
public Guid EstadoID { get; set; }
public Guid PaisID { get; set; }
[Required]
[MaxLength(150)]
public string Descricao { get; set; }
public string Sigla { get; set; }
[Required]
[MaxLength(2)]
public long PaisHandle { get; set; }
// Utilize o Atributo ForeignKey apenas se a propriedade usada configurar o relacionamento tenha um nome diferente da Key da Entidade Referenciada.
// [ForeignKey("PaisID")]
public virtual Pais Pais { get; set; }
public virtual ICollection<Cidade> Cidades { get; set; } new List<Cidade>();
}
[Table("Cidades")]
public class Cidade : EntityBase
{
public Cidade()
{
Pais = new Pais();
Estado = new Estado();
}
[Key]
public Guid CidadeID { get; set; }
public Guid EstadoID { get; set; }
[Required]
[MaxLength(150)]
public string Descricao { get; set; }
[Required]
[MaxLength(2)]
public string Sigla { get; set; }
// Utilize o Atributo ForeignKey apenas se a propriedade usada configurar o relacionamento tenha um nome diferente da Key da Entidade Referenciada.
// [ForeignKey("EstadoID")]
public virtual Estado Estado { get; set; }
}
public class MyContext : DbContext
{
public Guid UsuarioID { get; private set; }
public MyContext(Guid usuarioID)
{
this.UsuarioID = usuarioID;
}
public DbSet<Usuario> Usuarios { get; set; }
public DbSet<Pais> Paises { get; set; }
public DbSet<Estado> Estados { get; set; }
public DbSet<Cidade> Cidades { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Filter("IsDeleted", (EntityBase d) => d.IsDeleted, false);
// Faça aqui os mapeamentos que não conseguir fazer por DataAnnotations, como por exemplo algo especifico para o SGBD.
}
}
Finally, if you decide to use Soft Delete as in the example above, it will be interesting to use some global filter scheme, such as EntityFramework.DynamicFilters
EDIT - About the Fluent API
Let's look at just the following snippet of code:
public class EstadoMap : EntityTypeConfiguration<Estado>
{
public EstadoMap()
{
...
HasMany(x => x.Cidades);
}
}
public class CidadeMap : EntityTypeConfiguration<Cidade>
{
public CidadeMap()
{
...
HasRequired(x => x.Estado)
.WithMany(x => x.Cidades)
.HasForeignKey(x => x.EstadoHandle);
}
}
When doing HasRequired(x => x.Estado).WithMany(x => x.Cidades).HasForeignKey(x => x.EstadoHandle)
on CidadeMap
you are already mapping the two sides of the relationship, so calling HasMany(x => x.Cidades)
on EstadoMap
is unnecessary, just remove this type of mapping.
public class PaisMap : EntityTypeConfiguration<Pais>
{
public PaisMap()
{
ToTable("Pais");
HasKey(x => x.Handle);
Property(x => x.Descricao).HasMaxLength(150).IsRequired();
Property(x => x.Sigla).HasMaxLength(3).IsRequired();
}
}
public class EstadoMap : EntityTypeConfiguration<Estado>
{
public EstadoMap()
{
ToTable("Estado");
HasKey(x => x.Handle);
Property(x => x.Descricao).HasMaxLength(150).IsRequired();
Property(x => x.Sigla).HasMaxLength(2).IsRequired();
HasRequired(x => x.Pais).WithMany(x => x.Estados).HasForeignKey(x => x.PaisHandle);
}
}
public class CidadeMap : EntityTypeConfiguration<Cidade>
{
public CidadeMap()
{
ToTable("Cidade");
HasKey(x => x.Handle);
Property(x => x.Descricao).HasMaxLength(150).IsRequired();
Property(x => x.Sigla).HasMaxLength(2).IsRequired();
HasRequired(x => x.Estado).WithMany(x => x.Cidades).HasForeignKey(x => x.EstadoHandle);
}
}