How to use Groupby according to parameter?

Asked

Viewed 127 times

5

I have my Model account as below:

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

    public int ClienteId {get;set;}
    public Cliente Cliente {get;set;}

    public int ContaBancariaId {get;set;}
    public ContaBancaria ContaBancaria {get;set;}

    public int PlanoId {get;set;}
    public Plano Plano {get;set;}

    public string Descricao {get;set;}
    public DateTime Data {get;set;}
    public decimal Valor {get;set;}
}

For my filter and result I have the following models:

public class ContaBusca
{
    public AgruparPor AgruparPor {get;set;}

}
public enum AgruparPor 
{
   Cliente,
   ContaBancaria,
   Plano
}

And here’s my comeback:

public class ContaResultadoBusca
{
    public string Nome {get;set;} //Pode ser o nome do Cliente, Conta Bancaria ou Plano
    public List<ContaResultadoBuscaItem> Contas {get;set;}
}

public class ContaResultadoBuscaItem 
{
    public string Descricao {get;set;}
    public DateTime Data {get;set;}
    public decimal Valor {get;set;}
}

And in my service I receive the parameter and return as follows

   public class ContaServico
    {
        public ContaResultadoBusca AgruparPor(AgruparPor agrupador)
        {
            if (agrupador == AgruparPor.Cliente)
            {
                return dbo.Contas.GroupBy(conta => conta.Cliente).Select(conta => new ContaResultadoBusca { Nome = conta.Nome })..
            }
            if(agrupador == AgruparPor.ContaBancaria)
            {
                return dbo.Contas.GroupBy(conta => conta.ContaBancaria).Select(conta => new ContaResultadoBusca { Nome = conta.Nome })..
            }


    }
    }

How can I group according to the past parameter without having to do so many if? In the example there are only 2, but could be more...

  • In this case that you exemplified, I don’t know any other way than using a switch instead of if. What one can also do is to have a method to manufacture the Expression of the grouping, more in any way will have a control structure to identify the grouping

  • @Julioborges I could use ternary operation within groupBy... But if you have too many groupers it gets very bad maintenance and code reading...

  • It sure would look very bad, the best even will be to replace the if for a switch.

  • The nice thing about Switch is that if you fail to implement an Enum option, the compiler warns you and makes it easier to avoid errors due to lack of implementation

  • Yeah, but I still have the duplicate of my Select... That’s what I’m not liking... I tried with Func too

  • 2

    the only way you can do that is if you use polymorphism by having your entity count as abstract and the rest inherit from it. in Account you would have the data as id and name.

  • @Marconciliosouza, good option too

  • If the column matches the Enum string, you can do it with a Linq Query

  • @Leandroangelo in particular I do not like syntax of Innumerable query rsrsrs But answer there... If it is the only option...

Show 4 more comments

2 answers

3

Your model should reflect the following hierarchy:

Hierarquia de models

Once done, change the context of the comic book as follows:

public virtual DbSet<Conta> Contas { get; set; }

At the point where the context of the BD is consulted, do as follows:

public ContaResultadoBusca AgruparPor(Type type)
{
    IEnumerable<Conta> query;

    query = dbo.Contas
        .Where(where => where.GetType() == type);
    ...
    return new ContaResultadoBusca(nome, MapearResultado(query));
}

....

At the point where there is consumption of models, do:

public IActionResult ConsumirServico(ContaBusca contaBusca)
{
    ContaResultadoBusca model = null;

    if (ModelState.IsValid)
    {
        AgruparPor agruparPor = contaBusca.AgruparPor;

        Type type = Type.GetType($"MeuNamespace.Models.{agruparPor}");

        model = Servico.AgruparPor(type);
        ...
     }

     ...
     return View(model);
}

1

A little dynamic but functional way is to add static properties of the type Expression<Func<Conta, TKey>>(this is the type of data that function GroupBy of IEnumerable use to interpret and generate SQL) and use them to pass as parameter in the method Agrupar of the service:

The class counts:

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

    public int ClienteId { get; set; }
    public Cliente Cliente { get; set; }

    public int ContaBancariaId { get; set; }
    public ContaBancaria ContaBancaria { get; set; }

    public int PlanoId { get; set; }
    public Plano Plano { get; set; }

    public string Descricao { get; set; }
    public DateTime Data { get; set; }
    public decimal Valor { get; set; }

    public static Expression<Func<Conta, Cliente>> AgrupamentoPorCliente
        => c => c.Cliente;

    public static Expression<Func<Conta, ContaBancaria>> AgrupamentoPorContaBancaria
        => c => c.ContaBancaria;

    public static Expression<Func<Conta, Plano>> AgrupamentoPorPlano
        => c => c.Plano;
}

The method of grouping the ContaServico:

public ContaResultadoBusca Agrupar<TType, TKey>(Expression<Func<TType, TKey>> exp)
    where TType : Conta
    where TKey : class
{
    return dbo.Contas.GroupBy(exp).Select(conta => new ContaResultadoBusca { Nome = conta.Nome });
}

Browser other questions tagged

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