Inheritance with Repository Pattern

Asked

Viewed 257 times

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;
    }
}

1 answer

3


Pablo, do you intend to save this to a database? I don’t know much about this Pattern, but looking at the way the entities were created, it seems to me to be a bit strange.

The way it’s being done, you’ll have tables like Pessoa, and also tables PessoaFisica and PessoaJuridica with the same fields as Pessoa.

It would not be the case to have the 3 entity and, instead of inheritance, use an object of the type Pessoa within the classes of PessoaFisica and PessoaJuridica? It would look something like:

[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 : IEntity
{
    public string NumeroRg { get; set; }
    public Sexo Sexo { get; set; }

    [ForeignKey("Pessoa"), Required, Key]
    public int PessoaId { get; set; }
    public virtual PessoaEntity Pessoa { get; set; }
}

[Table("PessoaJuridica")]
public class PessoaJuridicaEntity : IEntity
{
    [MaxLength(100)]
    public string NomaFantasia { get; set; }

    [ForeignKey("Pessoa"), Required, Key]
    public int PessoaId { get; set; }
    public virtual PessoaEntity Pessoa { get; set; }
}
  • 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....

  • But I will reflect on your proposal and see if it is not better to undo even this legacy.

  • 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 have PessoaID 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.

  • 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!

  • 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.

  • An alternative I made was to transform the class PessoaRepository in abstract, 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.

  • I’ll post my solution soon.

  • 1

    @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

  • @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.

Show 4 more comments

Browser other questions tagged

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