3
I’m studying and trying to implement the Repository Pattern in C# however I am having some difficulties in solving certain problems with inheritance among my classes of Repository, for example the inheritance between Pessoa
, PessoaFisica
and PessoaJuridica
, where I created 3 classes, PessoaRepository
, PessoaFisicaRepository
, PessoaJuridicaRepository
, where PessoaFisicaRepository
and PessoaJuridicaRepository
are inheriting from PessoaRepository
.
My problem is that class PessoaRepository
is allowing to include a Personal or Personal, but their respective classes Respository
would have specific business rules, then class PessoaRepository
, could not include its derivatives, remembering that I am also trying to follow some concepts of SOLID.
My classes are: Model
[Table("Pessoa")]
public class PessoaEntity: IEntity
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int PessoaId { get; set; }
[Required]
[MaxLength(100)]
public string Nome { get; set; }
[MaxLength(14)]
public string CpfCnpj { get; set; }
}
[Table("PessoaFisica")]
public class PessoaFisicaEntity : PessoaEntity, IEntity
{
public string NumeroRg { get; set; }
public Sexo Sexo { get; set; }
}
[Table("PessoaJuridica")]
public class PessoaJuridicaEntity : PessoaEntity
{
[MaxLength(100)]
public string NomaFantasia { get; set; }
}
public enum Sexo
{
Masculino = 1,
Feminino = 2
}
public class PessoaContext : DbContext
{
public DbSet<PessoaEntity> Pessoa { get; set; }
public DbSet<PessoaFisicaEntity> PessoaFisica { get; set; }
public DbSet<PessoaJuridicaEntity> PessoaJuridica { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
base.OnModelCreating(modelBuilder);
}
}
Meu Repository:
public class Repository<TEntity, TContext> : IRepository<TEntity>
where TEntity : class, IEntity
where TContext : DbContext, new()
{
protected TContext _context;
public Repository()
{
_context = new TContext();
}
public virtual TEntity Find(params object[] keyValues)
{
return _context.Set<TEntity>().Find(keyValues);
}
public virtual void InsertOrUpdate(TEntity obj)
{
var entity = Find(GetKeyValue(obj));
if (entity == null)
_context.Set<TEntity>().Add(obj);
else
{
entity = obj;
}
_context.SaveChanges();
}
public virtual void Delete(params object[] keyValues)
{
_context.Set<TEntity>().Remove(Find(keyValues));
_context.SaveChanges();
}
public virtual void Save()
{
_context.SaveChanges();
}
private object[] GetKeyValue<T>(T entity) where T : class
{
var properties = entity.GetType().GetProperties();
var keysProperties = properties.Where(p => p.GetCustomAttributes(typeof(KeyAttribute), true).Length != 0);
List<object> retorno = new List<object>();
foreach (var key in keysProperties)
{
retorno.Add(key.GetValue(entity, null));
}
return retorno.ToArray();
}
}
public class PessoaRepository<TPessoa> : Repository<TPessoa, PessoaContext>
where TPessoa : PessoaEntity, IEntity
{
public override void InsertOrUpdate(TPessoa obj)
{
if (ConsisteInsertOrUpdate(obj))
{
base.InsertOrUpdate(obj);
}
}
private bool ConsisteInsertOrUpdate(PessoaEntity obj)
{
if (!String.IsNullOrWhiteSpace(obj.CpfCnpj))
{
var cpfCnpjDuplicados = _context.Pessoa.Where(a => a.CpfCnpj == obj.CpfCnpj && obj.PessoaId != a.PessoaId).Count();
return cpfCnpjDuplicados == 0;
}
return true;
}
}
public class PessoaFisicaRespository<TPessoaFisica> : PessoaRepository<TPessoaFisica>
where TPessoaFisica: PessoaFisicaEntity, IEntity
{
public override void InsertOrUpdate(TPessoaFisica obj)
{
if (ConsisteInsertOrUpdate(obj))
{
base.InsertOrUpdate(obj);
}
}
private bool ConsisteInsertOrUpdate(PessoaFisicaEntity obj)
{
if (!String.IsNullOrWhiteSpace(obj.NumeroRg))
{
var numeroRgDuplicados = _context.PessoaFisica.Where(a => a.NumeroRg == obj.NumeroRg && obj.PessoaId != a.PessoaId).Count();
return numeroRgDuplicados == 0;
}
return true;
}
}
i see much more as inheritance, because individual is a person, same thing for legal person, always used as inheritance until pq future arises other types of people, as supplier, employee....
– Pablo Tondolo de Vargas
But I will reflect on your proposal and see if it is not better to undo even this legacy.
– Pablo Tondolo de Vargas
Yes, you are right from an object orientation point of view. But when it comes to data normalization, specializations end up entering different tables. Imagine making a report with people from different entities. You’ll need to do several
UNION ALL
of the different entities. You will also havePessoaID
equal to different entities (Legal Person = 1 is one thing and Physical Person = 1 is something else). To make this work legal, you need to declare an object of the Sequence type.– rodrigogq
I believe that there must be a way to make your objects become an inheritance and, at the same time, save into different entities. But I can’t help you in this sense. Already in this way proposed I believe that it is very simple to think of separate entities. I hope this helps!
– rodrigogq
Today I have 3 tables in the bank, Person, Personal and Personal, when I want to make a report based on the people of the system, I look for everything in the entity Person. I do not need to do UNION ALL to catch all the people in the system and using the inheritance, it does not happen that a Person is Physical and Juridical.
– Pablo Tondolo de Vargas
An alternative I made was to transform the class
PessoaRepository
inabstract
, thus preventing an instance of this class from being created for new types of persons, such as Customer, Carrier and the like, which may be both Legal and Physical, I am no longer making inheritance but declaring a property of Person, this being my abstract entity too.– Pablo Tondolo de Vargas
I’ll post my solution soon.
– Pablo Tondolo de Vargas
@rodrigogq, this is called TPC (Table per Concrete Type), where in the database we have two tables (PJ and PF) and three entities (Pessoa, PJ and PF), if you want to read more about it: http://weblogs.asp.net/manavi/inheritance-mapping-strategies-with-entity-framework-code-first-ctp5-part-3-table-per-Concrete-type-tpc-and-Choosing-Strategy-guidelines
– Tobias Mesquita
@Tobiasmesquita I know this answer is old already, but thank you very much for teaching how to do this in EF. Again I would like to comment that I still prefer to use 3 different tables. This has already saved us a lot of effort in several consultancies with different clients, besides saving a lot of bank space.
– rodrigogq