Árvore de páginas

Versões comparadas

Chave

  • Esta linha foi adicionada.
  • Esta linha foi removida.
  • A formatação mudou.

...

Abaixo temos um exemplo de uma tabela "Professional" representada pela classe ProfessionalPoco (PKs: ProfessionalId, Code):

Bloco de código
languagec#
firstline1
titleProfessionalPoco.cs
linenumberstrue
using System;
using System.Collections.Generic;
using Tnf.Architecture.Dto.Registration;
using Tnf.AutoMapper;
using Tnf.Domain.Entities;

namespace Tnf.Architecture.EntityFrameworkCore.Entities
{
    [AutoMap(typeof(ProfessionalDto))]
    public class ProfessionalPoco : Entity
    {
        public ProfessionalPoco()
        {
        }
        public decimal ProfessionalId { get; set; }
        public string Name { get; set; }
        public Guid Code { get; set; }
        public string Address { get; set; }
        public string AddressNumber { get; set; }
        public string AddressComplement { get; set; }
        public string ZipCode { get; set; }
        public string Phone { get; set; }
        public string Email { get; set; }
        public List<ProfessionalSpecialtiesPoco> ProfessionalSpecialties { get; set; }
    }
}

Tendo a definição de nossa entidade vamos agora configura-la em nosso Configuração do contexto:

Bloco de código
languagec#
firstline1
titleLegacyDbContext.cs
linenumberstrue
using Microsoft.EntityFrameworkCore;
using Tnf.EntityFrameworkCore;
using Tnf.Architecture.EntityFrameworkCore.Entities;

namespace Tnf.Architecture.EntityFrameworkCore
{
    public class LegacyDbContext : TnfDbContext
    {
        public DbSet<ProfessionalPoco> Professionals { get; set; }
        public DbSet<ProfessionalSpecialtiesPoco> ProfessionalSpecialties { get; set; }
        public DbSet<SpecialtyPoco> Specialties { get; set; }

        public LegacyDbContext(DbContextOptions<LegacyDbContext> options) : base(options)
        {
        }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<ProfessionalPoco>(m =>
            {
                // PKs
                m.Ignore(i => i.Id);
                m.HasKey(i => new { i.ProfessionalId, i.Code });

                // Configure PKs auto generated
                m.Property(i => i.Code).ValueGeneratedOnAdd().HasColumnName("SYS009_PROFESSIONAL_CODE");

                // Hack pois o método ValueGeneratedOnAdd do EF Core ainda não aceita propriedades decimal
                m.Property(i => i.ProfessionalId).HasDefaultValueSql("SELECT ISNULL(MAX(SYS009_PROFESSIONAL_ID), 1) FROM SYS009_PROFESSIONAL").HasColumnName("SYS009_PROFESSIONAL_ID");

                m.Property(p => p.Name).HasColumnName("SYS009_NAME").HasMaxLength(50).IsRequired();
                m.Property(p => p.Address).HasColumnName("SYS009_ADDRESS").HasMaxLength(50).IsRequired();
                m.Property(p => p.AddressComplement).HasColumnName("SYS009_ADDRESS_COMPLEMENT").HasMaxLength(100).IsRequired();
                m.Property(p => p.AddressNumber).HasColumnName("SYS009_ADDRESS_NUMBER").HasMaxLength(9).IsRequired();
                m.Property(p => p.Email).HasColumnName("SYS009_EMAIL").HasMaxLength(50).IsRequired();
                m.Property(p => p.Email).HasColumnName("SYS009_EMAIL").HasMaxLength(50).IsRequired();
                m.Property(p => p.Phone).HasColumnName("SYS009_PHONE").HasMaxLength(50).IsRequired();
                m.Property(p => p.ZipCode).HasColumnName("SYS009_ZIP_CODE").HasMaxLength(15).IsRequired();

                m.ToTable("SYS009_PROFESSIONAL");
            });

            modelBuilder.Entity<SpecialtyPoco>(m =>
            {
                // PKs
                m.HasKey(i => i.Id);
                // Configure PKs auto generated
                m.Property(i => i.Id).ValueGeneratedOnAdd().HasColumnName("SYS011_SPECIALTIES_ID");
                m.Property(p => p.Description).HasColumnName("SYS011_SPECIALTIES_DESCRIPTION").HasMaxLength(100).IsRequired();
                m.ToTable("SYS011_SPECIALTIES");
            });

            modelBuilder.Entity<ProfessionalSpecialtiesPoco>(m =>
            {
                // PKs
                m.Ignore(i => i.Id);
                m.HasKey(i => new { i.ProfessionalId, i.Code, i.SpecialtyId });
                
                m.Property(i => i.ProfessionalId).HasColumnName("SYS009_PROFESSIONAL_ID");
                m.Property(i => i.Code).HasColumnName("SYS009_PROFESSIONAL_CODE");
                m.Property(i => i.SpecialtyId).HasColumnName("SYS011_SPECIALTIES_ID");
                m.HasOne(o => o.Professional)
                    .WithMany(w => w.ProfessionalSpecialties)
                    .HasPrincipalKey(k => new { k.ProfessionalId, k.Code })
                    .HasForeignKey(k => new { k.ProfessionalId, k.Code });

                m.HasOne(o => o.Specialty)
                    .WithMany(w => w.ProfessionalSpecialties)
                    .HasPrincipalKey(k => k.Id)
                    .HasForeignKey(k => k.SpecialtyId);

                m.ToTable("SYS010_PROFESSIONAL_SPECIALTIES");
            });
        }
    }
}

Note que no contexto acima temos a configuração de nossa entidade Professional (SYS009_PROFESSIONAL) com um relacionamento de N para N (SYS010_PROFESSIONAL_SPECIALTIES) com a entidade Specialty (SYS011_SPECIALTIES) que representa as especialidades de um profissional.

Como o repositório por default exige uma chave primaria essa é ignorada por ser do tipo Int pela , nossa entidade comporta apenas uma chave. Dessa forma devemos ignora-la na hora de representar a tabela em nosso contexto utilizando a configuração m.Ignore(i => i.Id). 

...

Como em muitos casos temos que criar chaves auto incremento podemos utilizar o método chamado ValueGeneratedOnAdd que irá configurar a chave como Identity.

Obs: Existem alguns tipos de dados ainda no Entity Framework Core que não estão funcionando com valores auto incremento como decimal. Para esses valores podemos contornar o problema usando o método HasDefaultValueSql, onde é passada uma instrução SQL que irá recuperar o valor do campo na hora de usar o seu auto incremento (Esse problema já está previsto para ser resolvido em futuras releases do Entity Framework Core).

Note que além das chaves auto incremento configuramos também nomes das colunas, relacionamentos N para N e nomes das tabelas.

...

).

Note que além das chaves auto incremento configuramos também nomes das colunas, relacionamentos N para N e nomes das tabelas.

Abaixo temos a reescrita de nosso repositório com métodos que atendem o cenários com chaves primarias compostas:

Bloco de código
languagec#
firstline1
titleIProfessionalRepository.cs
linenumberstrue
using System.Collections.Generic;
using Tnf.Architecture.Dto;
using Tnf.Architecture.Dto.Registration;
using Tnf.Domain.Repositories;

namespace Tnf.Architecture.Domain.Interfaces.Repositories
{
    public interface IProfessionalRepository : IRepository
    {
        PagingResponseDto<ProfessionalDto> GetAllProfessionals(GetAllProfessionalsDto request);
        ProfessionalDto GetProfessional(ProfessionalKeysDto keys);
        ProfessionalDto CreateProfessional(ProfessionalDto entity);
        ProfessionalDto UpdateProfessional(ProfessionalDto dto);
        bool DeleteProfessional(ProfessionalKeysDto keys);
        void AddOrRemoveSpecialties(ProfessionalKeysDto keys, List<SpecialtyDto> dto);
        bool ExistsProfessional(ProfessionalKeysDto keys);
    }
}
Bloco de código
languagec#
firstline1
titleProfessionalRepository.cs
linenumberstrue
using Tnf.Architecture.Domain.Interfaces.Repositories;
using Tnf.EntityFrameworkCore.Repositories;
using Tnf.EntityFrameworkCore;
using Tnf.Architecture.EntityFrameworkCore.Entities;
using Tnf.Architecture.Dto;
using System.Collections.Generic;
using System.Linq;
using Tnf.AutoMapper;
using Tnf.Architecture.Dto.Registration;
using Microsoft.EntityFrameworkCore;
using System;

namespace Tnf.Architecture.EntityFrameworkCore.Repositories
{
    public class ProfessionalRepository : EfCoreRepositoryBase<LegacyDbContext, ProfessionalPoco>, IProfessionalRepository
    {
        public ProfessionalRepository(IDbContextProvider<LegacyDbContext> dbContextProvider)
            : base(dbContextProvider)
        {
        }

        public bool DeleteProfessional(ProfessionalKeysDto keys)
        {
            var dbEntity = Context.Professionals
                .Include(i => i.ProfessionalSpecialties)
                .SingleOrDefault(s => s.ProfessionalId == keys.ProfessionalId && s.Code == keys.Code);

            if (dbEntity != null)
            {
                dbEntity.ProfessionalSpecialties.ForEach(w => Context.ProfessionalSpecialties.Remove(w));
                Context.Professionals.Remove(dbEntity);
            }

            return dbEntity != null;
        }

        private ProfessionalPoco GetProfessionalPoco(ProfessionalKeysDto keys)
        {
            var dbEntity = Context.Professionals
                .Include(i => i.ProfessionalSpecialties)
                    .ThenInclude(i => i.Specialty)
                .SingleOrDefault(w => w.ProfessionalId == keys.ProfessionalId && w.Code == keys.Code);

            return dbEntity;
        }

        public ProfessionalDto GetProfessional(ProfessionalKeysDto keys)
        {
            var dbEntity = GetProfessionalPoco(keys);
            return dbEntity != null ? dbEntity.MapTo<ProfessionalDto>() : null;
        }

        public ProfessionalDto CreateProfessional(ProfessionalDto entity)
        {
            var dbEntity = entity.MapTo<ProfessionalPoco>();
            Context.Professionals.Add(dbEntity);
            Context.SaveChanges();
            return dbEntity.MapTo<ProfessionalDto>();
        }

        public ProfessionalDto UpdateProfessional(ProfessionalDto entity)
        {
            var mappedEntity = GetProfessionalPoco(new ProfessionalKeysDto(entity.ProfessionalId, entity.Code));
            entity.MapTo(mappedEntity);
            Context.Professionals.Update(mappedEntity);
            Context.SaveChanges();
            return mappedEntity.MapTo<ProfessionalDto>();
        }

        public PagingResponseDto<ProfessionalDto> GetAllProfessionals(GetAllProfessionalsDto request)
        {
            var response = new PagingResponseDto<ProfessionalDto>();
            var dbBaseQuery = Context.Professionals
                .Include(i => i.ProfessionalSpecialties)
                .Include("ProfessionalSpecialties.Specialty")
                .Where(w => request.Name == null || w.Name.Contains(request.Name));

            var dbQuery = dbBaseQuery
                .Skip(request.Offset)
                .Take(request.PageSize)
                .ToArray();

            response.Total = base.Count();
            response.Data = dbQuery.MapTo<List<ProfessionalDto>>();
            return response;
        }

        public void AddOrRemoveSpecialties(ProfessionalKeysDto keys, List<SpecialtyDto> dto)
        {
            var dbProfessional = GetProfessionalPoco(keys);
            if (dbProfessional != null)
            {
                var idsToAdd = dto.Select(s => s.Id).ToArray();
                if (dbProfessional.ProfessionalSpecialties == null)
                    dbProfessional.ProfessionalSpecialties = new List<ProfessionalSpecialtiesPoco>();
                dbProfessional.ProfessionalSpecialties.RemoveAll(w => !idsToAdd.Contains(w.SpecialtyId));
                dto.ForEach(w =>
                {
                    var dbProfessionalSpecialties = dbProfessional.ProfessionalSpecialties
                        .FirstOrDefault(s => s.SpecialtyId == w.Id);
                    if (dbProfessionalSpecialties == null)
                    {
                        dbProfessional.ProfessionalSpecialties.Add(new ProfessionalSpecialtiesPoco()
                        {
                            ProfessionalId = dbProfessional.ProfessionalId,
                            Code = dbProfessional.Code,
                            SpecialtyId = w.Id
                        });
                    }
                });
            }
        }

        public bool ExistsProfessional(ProfessionalKeysDto keys)
        {
            var dbEntity = Context.Professionals
                .SingleOrDefault(s => s.ProfessionalId == keys.ProfessionalId && s.Code == keys.Code);
            return dbEntity != null;
        }
    }
}

Este exemplo com umas estrutura legada esta disponível em nosso github: https://github.com/totvsnetcore/tnf-samples/tree/master/TnfSample-Architecture