Solucionado (ver solução)
Solucionado
(ver solução)
1
resposta

Boa prática na inicialização de propriedades que são listas, e método de conversã não evita duplicidade

Na aula 04 item 04, minuto 5.40 o instrutor mostra como fazer uma condicional ternária para, se receber uma lista de gêneros, atribuir a lista à coleção chamando um método, ou então, se não receber, atribuir uma lista vazia à esta coleção.

Entendo a utilidade do teste para evirar processamento desnecessário chamando o método de conversão sem uma lista de gêneros recebida.

Mas a segunda parte, de atribuir a lista vazia, eu faria de outra maneira.

É uma boa prática, até para não ter erro de null pointer exception ou propriedade não inicializada, que em nossas classes já inicializemos os atributos que são listas com uma lista vazia.

Assim por exemplo em Generos.cs a lista de músicas ficaria assim:

public virtual ICollection<Musica> Musicas { get; set; } = new List<Musica>();

Da mesma forma em Musicas.cs:

public virtual ICollection<Genero> Generos { get; set; } = new List<Genero>();

Desta forma sempre que um objeto Genero ou Musica forem instanciados, listas correspondentes já terão sido inicializadas com uma lista vazia.

Voltando ainda à questão da condicional ternária, eu não faria uma condicional na atribuição da propriedade, e sim dentro do método de conversão, eu colocaria um if testando se a lista está vazia e então retornaria a lista vazia de dentro do método, evitando ter que por condicional em diversos lugares se este método for usado em mais que um lugar.

Outro ponto, foi feita uma verificação para checar se o genero já existe, não incluindo duas vezes. Mas da forma que está o método ainda permite duplicidade, eu explico: se você passar no swagger duas ou três vezes um gênero que não existe, o método vai ver que não tem no banco de daos, e vai incluir duas ou três vezes na lista.

Como alternativa sugiro usar um HashSet, que não permite duplicidade, assim pode passar 10 vezes o mesmo gênero que não existe, que a lista só terá este gênero uma vez.

Pra isso é importante definir Equals e GetHashCode no genero, exemplo:

//genero.cs

namespace ScreenSound.Modelos;

public class Genero
{
    public int Id { get; set; }

    public string Nome { get; set; } = string.Empty;

    public string? Descricao { get; set; } = string.Empty;

    public virtual ICollection<Musica> Musicas { get; set; } = new List<Musica>();

    public override string ToString()
    {
        return $"Nome: {Nome} - Descrição: {Descricao}";
    }

    public override bool Equals(object obj)
    {
        if (obj == null || GetType() != obj.GetType())
            return false;

        Genero genero = (Genero)obj;
        return Nome.Equals(genero.Nome, StringComparison.OrdinalIgnoreCase);
    }

    public override int GetHashCode()
    {
        return Nome != null ? Nome.GetHashCode(StringComparison.OrdinalIgnoreCase) : 0;
    }
}

Método de conversão que evita duplicidade:

        private static ICollection<Genero> PostMusicaGeneroListCompose(ICollection<GeneroRequestPost> generosRequest, DAL<Genero> dalGeneros)
        {
            if (generosRequest == null || generosRequest.Count == 0)
            {
                return new List<Genero>(); // Retorna uma lista vazia se não houver gêneros para processar
            }

            //um ICollection nao impede que entrem dois generos iguais, se passar Rock e Rock, duas vezes, e não tiver no banco, cadastra dois...
            //ICollection<Genero> generos = new List<Genero>();

            HashSet<Genero> generos = new HashSet<Genero>(); // usar HashSet para impedir duplicidade, por isso definir equals e hashcode em Genero pelo nome

            foreach (var genero in generosRequest)
            {
                var generoEntity = dalGeneros.RecuperarListaPor(g => g.Nome!.Equals(genero.Nome, StringComparison.OrdinalIgnoreCase)).FirstOrDefault();

                generos.Add(generoEntity ?? new Genero() { Nome = genero.Nome, Descricao = genero.Descricao });
            }

            //return generos;

            return generos.ToList();
        }
1 resposta
solução!

Olá Angelo! Tudo bem?

Você levantou pontos muito importantes sobre boas práticas na inicialização de propriedades que são listas e na prevenção de duplicidades ao lidar com gêneros musicais. Vamos abordar cada um deles.

Inicialização de Propriedades que são Listas

Inicializar as propriedades que são listas diretamente na declaração da propriedade é uma excelente prática. Isso evita problemas com null e facilita a manutenção do código. O exemplo que você deu está perfeito:

public virtual ICollection<Musica> Musicas { get; set; } = new List<Musica>();
public virtual ICollection<Genero> Generos { get; set; } = new List<Genero>();

Essa abordagem garante que, ao instanciar um objeto Genero ou Musica, as listas estarão sempre inicializadas, evitando NullReferenceException.

Método de Conversão e Prevenção de Duplicidade

Você também mencionou a questão de evitar duplicidade ao adicionar gêneros. A sua sugestão de usar um HashSet é ótima, pois ele impede a inserção de elementos duplicados. Aqui está um exemplo de como você pode implementar isso:

private static ICollection<Genero> PostMusicaGeneroListCompose(ICollection<GeneroRequestPost> generosRequest, DAL<Genero> dalGeneros)
{
    if (generosRequest == null || generosRequest.Count == 0)
    {
        return new List<Genero>(); // Retorna uma lista vazia se não houver gêneros para processar
    }

    HashSet<Genero> generos = new HashSet<Genero>(); // usar HashSet para impedir duplicidade, por isso definir equals e hashcode em Genero pelo nome

    foreach (var genero in generosRequest)
    {
        var generoEntity = dalGeneros.RecuperarListaPor(g => g.Nome!.Equals(genero.Nome, StringComparison.OrdinalIgnoreCase)).FirstOrDefault();

        generos.Add(generoEntity ?? new Genero() { Nome = genero.Nome, Descricao = genero.Descricao });
    }

    return generos.ToList();
}
Implementação de Equals e GetHashCode

Para que o HashSet funcione corretamente, é necessário sobrescrever os métodos Equals e GetHashCode na classe Genero. O seu exemplo está correto:

public override bool Equals(object obj)
{
    if (obj == null || GetType() != obj.GetType())
        return false;

    Genero genero = (Genero)obj;
    return Nome.Equals(genero.Nome, StringComparison.OrdinalIgnoreCase);
}

public override int GetHashCode()
{
    return Nome != null ? Nome.GetHashCode(StringComparison.OrdinalIgnoreCase) : 0;
}

A sua abordagem de inicializar listas na declaração das propriedades e usar HashSet para evitar duplicidade é muito eficiente e segue boas práticas de programação. Além disso, colocar a lógica de verificação de lista vazia dentro do método de conversão ajuda a manter o código mais limpo e reutilizável.

Bons estudos!