Solucionado (ver solução)
Solucionado
(ver solução)
4
respostas

Coloquei mais campos no ReadSessaoDto e depois não consigo listar filmes na API

Ao invés de deixar somente o campo ID no ReadDto, incluí Filme e Cinema, o que gera referências circulares mais pra frente quando as listagem de filme e cinema deveriam mostrar as sessões também, o problema é que mesmo depois de comentar estes campos, fazer remove-migrations, make-migrations, drop-database, update-database continua dando erro na GET /filme. Parece que FilmeContextModelSnapshop permaneceu inalterado mesmo com as re-migrations. Rodando o projeto baixado do github funciona normal. Vou usando este até terminar...

AutoMapper.AutoMapperMappingException: Error mapping types.

Mapping types: EntityQueryable1 -> List1 Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable1[[FilmesAPI.Models.Filme, FilmesAPI, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]] -> System.Collections.Generic.List1[[FilmesAPI.Data.Dtos.ReadFilmeDto, FilmesAPI, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]] ---> AutoMapper.AutoMapperMappingException: Error mapping types.

Mapping types: Filme -> ReadFilmeDto FilmesAPI.Models.Filme -> FilmesAPI.Data.Dtos.ReadFilmeDto

Type Map configuration: Filme -> ReadFilmeDto FilmesAPI.Models.Filme -> FilmesAPI.Data.Dtos.ReadFilmeDto

Destination Member: Sessoes

---> System.InvalidOperationException: This MySqlConnection is already in use. See https://fl.vu/mysql-conn-reuse at MySqlConnector.Core.ServerSession.StartQuerying(ICancellableCommand command) in /_/src/MySqlConnector/Core/ServerSession.cs:line 283 at MySqlConnector.Core.CommandExecutor.ExecuteReaderAsync(IReadOnlyList1 commands, ICommandPayloadCreator payloadCreator, CommandBehavior behavior, Activity activity, IOBehavior ioBehavior, CancellationToken cancellationToken) in /_/src/MySqlConnector/Core/CommandExecutor.cs:line 50 at MySqlConnector.MySqlCommand.ExecuteReaderAsync(CommandBehavior behavior, IOBehavior ioBehavior, CancellationToken cancellationToken) in /_/src/MySqlConnector/MySqlCommand.cs:line 344 at MySqlConnector.MySqlCommand.ExecuteDbDataReader(CommandBehavior behavior) in /_/src/MySqlConnector/MySqlCommand.cs:line 278 at System.Data.Common.DbCommand.ExecuteReader() at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteReader(RelationalCommandParameterObject parameterObject) at Microsoft.EntityFrameworkCore.Query.Internal.SingleQueryingEnumerable1.Enumerator.InitializeReader(Enumerator enumerator) at Microsoft.EntityFrameworkCore.Query.Internal.SingleQueryingEnumerable1.Enumerator.<>c.<MoveNext>b__21_0(DbContext _, Enumerator enumerator) at Pomelo.EntityFrameworkCore.MySql.Storage.Internal.MySqlExecutionStrategy.Execute[TState,TResult](TState state, Func3 operation, Func3 verifySucceeded) at Microsoft.EntityFrameworkCore.Query.Internal.SingleQueryingEnumerable1.Enumerator.MoveNext() at Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.Load[TSource](IQueryable1 source) at Microsoft.EntityFrameworkCore.Internal.EntityFinder1.Load(INavigation navigation, InternalEntityEntry entry) at Microsoft.EntityFrameworkCore.Internal.EntityFinderCollectionLoaderAdapter.Load(InternalEntityEntry entry) at Microsoft.EntityFrameworkCore.ChangeTracking.CollectionEntry.Load() at Microsoft.EntityFrameworkCore.Infrastructure.Internal.LazyLoader.Load(Object entity, String navigationName) at Microsoft.EntityFrameworkCore.Proxies.Internal.LazyLoadingInterceptor.Intercept(IInvocation invocation) at Castle.DynamicProxy.AbstractInvocation.Proceed() at Castle.Proxies.FilmeProxy.get_sessoes() at lambda_method8(Closure , Object , List1 , ResolutionContext ) --- End of inner exception stack trace --- at lambda_method8(Closure , Object , List1 , ResolutionContext ) --- End of inner exception stack trace --- at lambda_method8(Closure , Object , List`1 , ResolutionContext ) at FilmesAPI.Controllers.FilmeController.RecuperaFilmes(Int32 skip, Int32 take) in

4 respostas

Olá, Juari!

Parece que você está enfrentando um problema comum quando estamos trabalhando com Entity Framework e AutoMapper. Isso geralmente ocorre quando tentamos mapear entidades que têm referências circulares.

No seu caso, parece que você adicionou os campos Filme e Cinema no seu DTO (Data Transfer Object) ReadSessaoDto, o que causou a referência circular, pois Filme e Cinema também devem ter referências para Sessao.

Para resolver isso, você pode tentar algumas abordagens:

  1. Ignorar as propriedades que causam a referência circular no AutoMapper. Você pode fazer isso no seu perfil de mapeamento. Aqui está um exemplo de como você pode fazer isso:
public class FilmeProfile : Profile
{
    public FilmeProfile()
    {
        CreateMap<Filme, ReadFilmeDto>()
            .ForMember(dest => dest.Sessoes, opt => opt.Ignore());
    }
}

Neste exemplo, estamos ignorando a propriedade Sessoes ao mapear de Filme para ReadFilmeDto.

  1. Use DTOs diferentes para operações diferentes. Por exemplo, você pode ter um DTO para leitura e outro para gravação. O DTO de leitura pode não incluir as propriedades que causam a referência circular.

  2. Desabilite o carregamento preguiçoso para suas entidades. Isso pode ser feito no DbContext. No entanto, tenha em mente que isso pode afetar o desempenho da sua aplicação, pois todas as entidades relacionadas serão carregadas de uma vez.

Espero que isso resolva seu problema. Lembre-se de que essas são apenas sugestões e pode ser que você precise adaptá-las para o seu caso específico.

Espero ter ajudado e bons estudos!

Obrigado, bom saber que tem como se desviar destas referências circulares no profile de automapper. Também nas propriedades do Dto declarar a variável de classe com seus respectivos Dtos, como ICollection<ReadSessaoDto> ao invés de ICollection<Sessao>.

Isto resolveu no profile do cinema, mas no profile do filme, (porque será?), deu problema no MySQL connector que não suporta a configuração MultipleActiveResultSets=True na string de conexão (https://stackoverflow.com/questions/25953560/mysql-connector-multipleactiveresultsets-issue).

AutoMapper.AutoMapperMappingException: Error mapping types.

Mapping types:
EntityQueryable`1 -> List`1
Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable`1[[FilmesAPI.Models.Filme, FilmesAPI, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]] -> System.Collections.Generic.List`1[[FilmesAPI.Data.Dtos.ReadFilmeDto, FilmesAPI, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]
 ---> AutoMapper.AutoMapperMappingException: Error mapping types.

Mapping types:
Filme -> ReadFilmeDto
FilmesAPI.Models.Filme -> FilmesAPI.Data.Dtos.ReadFilmeDto

Type Map configuration:
Filme -> ReadFilmeDto
FilmesAPI.Models.Filme -> FilmesAPI.Data.Dtos.ReadFilmeDto

Destination Member:
Sessoes

 ---> System.InvalidOperationException: This MySqlConnection is already in use. See https://fl.vu/mysql-conn-reuse
 
   at MySqlConnector.Core.ServerSession.StartQuerying(ICancellableCommand command) in /_/src/MySqlConnector/Core/ServerSession.cs:line 283
   at MySqlConnector.Core.CommandExecutor.ExecuteReaderAsync(IReadOnlyList`1 commands, ICommandPayloadCreator payloadCreator, CommandBehavior behavior, Activity activity, IOBehavior ioBehavior, CancellationToken cancellationToken) in /_/src/MySqlConnector/Core/CommandExecutor.cs:line 50
   at MySqlConnector.MySqlCommand.ExecuteReaderAsync(CommandBehavior behavior, IOBehavior ioBehavior, CancellationToken cancellationToken) in /_/src/MySqlConnector/MySqlCommand.cs:line 344
   at MySqlConnector.MySqlCommand.ExecuteDbDataReader(CommandBehavior behavior) in /_/src/MySqlConnector/MySqlCommand.cs:line 278
   at System.Data.Common.DbCommand.ExecuteReader()
   at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteReader(RelationalCommandParameterObject parameterObject)
   at Microsoft.EntityFrameworkCore.Query.Internal.SingleQueryingEnumerable`1.Enumerator.InitializeReader(Enumerator enumerator)
   at Microsoft.EntityFrameworkCore.Query.Internal.SingleQueryingEnumerable`1.Enumerator.<>c.<MoveNext>b__21_0(DbContext _, Enumerator enumerator)
   at Pomelo.EntityFrameworkCore.MySql.Storage.Internal.MySqlExecutionStrategy.Execute[TState,TResult](TState state, Func`3 operation, Func`3 verifySucceeded)
   at Microsoft.EntityFrameworkCore.Query.Internal.SingleQueryingEnumerable`1.Enumerator.MoveNext()
   at Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.Load[TSource](IQueryable`1 source)
   at Microsoft.EntityFrameworkCore.Internal.EntityFinder`1.Load(INavigation navigation, InternalEntityEntry entry)
   at Microsoft.EntityFrameworkCore.Internal.EntityFinderCollectionLoaderAdapter.Load(InternalEntityEntry entry)
   at Microsoft.EntityFrameworkCore.ChangeTracking.CollectionEntry.Load()
   at Microsoft.EntityFrameworkCore.Infrastructure.Internal.LazyLoader.Load(Object entity, String navigationName)
   at Microsoft.EntityFrameworkCore.Proxies.Internal.LazyLoadingInterceptor.Intercept(IInvocation invocation)
   at Castle.DynamicProxy.AbstractInvocation.Proceed()
   at Castle.Proxies.FilmeProxy.get_Sessoes()
   at lambda_method8(Closure , Object , List`1 , ResolutionContext )
   --- End of inner exception stack trace ---
   at lambda_method8(Closure , Object , List`1 , ResolutionContext )
   --- End of inner exception stack trace ---
   at lambda_method8(Closure , Object , List`1 , ResolutionContext )
   at FilmesAPI.Controllers.FilmeController.RecuperaFilmes(Int32 skip, Int32 take) in O:\5_gdrive\8_proj\1_alura\FilmesAPI\FilmesAPI\Controllers\FilmeController.cs:line 70
   at lambda_method2(Closure , Object , Object[] )
   at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.SyncObjectResultExecutor.Execute(IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeActionMethodAsync()
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeNextActionFilterAsync()
--- End of stack trace from previous location ---

Resolvi mapeando filme dentro de sessões (adicionei uma propriedade ReadFilmeDto dentro de ReadSessaoDto, no FilmeProfile coloquei opt.Ignore() para o campo Sessao conforme resposta do Matheus Brandino). Sessões continou mapeado dentro de cinema. Assim ao listar cinemas, dentro tem sessões, que por sua vez tem filme. Pra terminar o curso vai ser feito relação n-n entre cinemas e filmes, então isto vai ser desfeito, mas tudo bem.

[
    {
        "id": 1,
        "nome": "Miramax",
        "horaDeAcesso": "2023-09-21T16:22:00.535673-03:00",
        "endereco": {
            "id": 1,
            "logradouro": "Rua dos Avatares",
            "numero": 162
        },
        "sessoes": []
    },
    {
        "id": 2,
        "nome": "Alura Cinemas",
        "horaDeAcesso": "2023-09-21T16:22:00.6730718-03:00",
        "endereco": {
            "id": 2,
            "logradouro": "Rua das Amoras",
            "numero": 600
        },
        "sessoes": [
            {
                "id": 1,
                "filme": {
                    "titulo": "Avatar0",
                    "genero": "Aventura",
                    "duracao": 162,
                    "horaDaConsulta": "2023-09-21T16:22:00.7046743-03:00",
                    "sessoes": null
                }
            },
            {
                "id": 2,
                "filme": {
                    "titulo": "Planeta dos Macacos0",
                    "genero": "Ação",
                    "duracao": 120,
                    "horaDaConsulta": "2023-09-21T16:22:00.7067695-03:00",
                    "sessoes": null
                }
            }
        ]
    }
]
solução!

Resolvido seguindo a última parte do curso, relacionamento n:n

Tinha problema no FilmeController, estava retornando queryable ao invés de IEnumerable, resolvido adicionando .ToList() no retorno.

return _mapper.Map<List<ReadFilmeDto>>(_context.Filmes.OrderBy(filme => filme.Id).Skip(skip).Take(take).ToList());

Ao mudar o modelo Sessao, com propriedade Cinema passando a ser nullable que nem a prop Filme, já funcionou as listagens sem referências circulares.

Seguindo no módulo n:n, o modelo Sessao foi convertido para uma tabela de ligação n:n (removido propriedade Id, e as duas propriedades Filme e Cinema passaram a chave primária que também são foreign keys, definido na função OnModelCreating do DbContext FilmeContext).

Terminando assim o curso.