How to get all records from a mixed table generated by class inheritance when using Entityframework 6?

Asked

Viewed 1,261 times

8

I have a class structure in the following form:

public class Pessoa
{
    public int Id {get; set;}
    public string Nome {get; set;}
}

public class Membro : Pessoa
{
    public int CargoId {get; set;}
    public Cargo Cargo {get; set;}
}

public class MembroTemp : Pessoa { }

My database has the table Pessoas aggregating the data related to the two classes and with this has the field Discriminator.

Como ficou a base

In a query screen need to bring the two types of data, class Membro and MembroTemp. And for the records like Membro need to get the Cargo and then demonstrate in this consultation.

Then I thought of something like:

var items = repository.Pessoas.Include(x => x.Cargo).OrderBy(x => x.Nome);

But of course it does not, nor does it compile because the class Pessoa does not own the property Cargo, she is from Membro. Not even if I force it with a Include("Cargo") it won’t work, of course.

I tried a unified search instead of doing it separately:

var membros = repository.Membros.Include(x => x.Cargo).OrderBy(x => x.Nome);  
var membrosTemp = repository.MembrosTemp.OrderBy(x => x.Nome);  

Because in my Action realization filters and field ordination. In the case of ordination I do not know how to unify the records but respect the ordination.

If I make separate requests I will end up getting a result like:

+-----------------------------+
|ID | Nome     | Discriminator|
+-----------------------------+
| 1 | Fulano 1 | Membro       |
| 2 | Fulano 2 | Membro       |
| 3 | Fulano 1 | MembroTemp   |
| 4 | Fulano 2 | MembroTemp   |
+-----------------------------+

And that’s not a perfect naming ordination.

How can I handle this situation?

  • Can show how the tables defined in BD?

  • 1

    @ramaral, presenting only the table Pessoas. I will present a demonstrated image!

  • 1

    @ramaral, sorry for the delay, and the bank image was added!

  • LazyLoading is enabled or disabled?

  • 1

    @ramaral, I didn’t set anything up about LazyLoading.

2 answers

6


If an inheritance has remained between these entities it is with OfType the solution, and the Dbset will only contain the Super Class Gente. Only reinforcing that in Heritage Oftype is indispensable.

Example: 1 Table Only (Discriminator)

Classes:

public class Gente
{
    public int Id { get; set; }
    public String Nome { get; set; }
}    
public class GenteFisica: Gente
{
    public string Cpf { get; set; }

    public int CargoId {get;set;}        
    [ForeignKey("CargoId")]
    public virtual Cargo Cargo { get; set; }
}    
public class GenteJuridica: Gente
{
    public string Cnpj { get; set; }
    public String Ie { get; set; }
}
[Table("Cargo")]
public class Cargo
{
    public int Id { get; set; }
    public String Nome { get; set; }
}
public class Db : System.Data.Entity.DbContext
{
    public Db()
        : base("Data Source=.\\SqlExpress;initial catalog=generics2;Integrated Security=False;User ID=sa;Password=senha;Connect Timeout=15;Encrypt=False;TrustServerCertificate=False")
    {

    }
    public System.Data.Entity.DbSet<Gente> Gente {get; set;}
    public System.Data.Entity.DbSet<Cargo> Cargo { get; set; }
}

Code:

namespace ConsoleApplication2
{
    class Program
    {
        static void Main(string[] args)
        {
            Db db = new Db();

            Cargo cargo = new Cargo();
            cargo.Nome = "Administrador";

            GenteFisica gentefisica = new GenteFisica();
            gentefisica.Nome = "Fulano Fisica 1";
            gentefisica.Cpf = "12345645600";
            gentefisica.Cargo = cargo;
            gentefisica.CargoId = cargo.Id

            GenteJuridica gentejuridica = new GenteJuridica();
            gentejuridica.Nome = "Fulano Fisica 1";
            gentejuridica.Cnpj = "12345645600-00";
            gentejuridica.Ie = "102030405060";

            db.Cargo.Add(cargo);
            db.Gente.Add(gentefisica);
            db.Gente.Add(gentejuridica);

            db.SaveChanges();

            GenteFisica gentefi = db.Gente.OfType<GenteFisica>().Where(x => x.Id == 1).FirstOrDefault();
            GenteJuridica genteju = db.Gente.OfType<GenteJuridica>().Where(x => x.Id == 2).FirstOrDefault();
            db.Dispose();

        }
    }
}

Generated Base:

inserir a descrição da imagem aqui

Upshot:

inserir a descrição da imagem aqui


Example: 3 tables

Add the Table of System.ComponentModel.Dataannotations.Schem that way

[Table("Gente")]
public class Gente
{
    public int Id { get; set; }
    public String Nome { get; set; }
}    
[Table("GenteFisica")]
public class GenteFisica: Gente
{
    public string Cpf { get; set; }

    public int CargoId {get;set;}        
    [ForeignKey("CargoId")]
    public virtual Cargo Cargo { get; set; }
}    
[Table("GenteJuridica")]
public class GenteJuridica: Gente
{
    public string Cnpj { get; set; }
    public String Ie { get; set; }
}    
[Table("Cargo")]
public class Cargo
{
    public int Id { get; set; }
    public String Nome { get; set; }
}    
public class Db : System.Data.Entity.DbContext
{
    public Db()
        : base("Data Source=.\\SqlExpress;initial catalog=generics2;Integrated Security=False;User ID=sa;Password=senha;Connect Timeout=15;Encrypt=False;TrustServerCertificate=False")
    {

    }
    public System.Data.Entity.DbSet<Gente> Gente {get; set;}
    public System.Data.Entity.DbSet<Cargo> Cargo { get; set; }
}

It will create your base with 4 tables:

inserir a descrição da imagem aqui

Note: the encoding is equal to 1 table (Add, Update, Delete and Select) with the DbSet<Gente> for GenteFisica and GenteJuridica

SQL com Order By Nome - Concat - UNION ALL

        **Concat**       
        //Sem o Tipo
        var resultado = db.Gente.OfType<GenteFisica>().Select(x => new
        {                
            x.Id,
            x.Nome
        }).Concat(db.Gente.OfType<GenteJuridica>().Select(g => new
        {                
            g.Id,
            g.Nome                
        })).AsQueryable();

        var resultOrdemNome = resultado.OrderBy(x => x.Nome).ToList();

        **Concat**
        //Com o Tipo 
        var resultado = db.Gente.OfType<GenteFisica>().Select(x => new
        {                
            x.Id,
            x.Nome, 
            Gente = "GenteFisica"
        }).Concat(db.Gente.OfType<GenteJuridica>().Select(g => new
        {                
            g.Id,
            g.Nome,
            Gente = "GenteJuridica" 
        })).AsQueryable();
        var resultOrdemNome = resultado.OrderBy(x => x.Nome).ToList();

        **Concat**
        //Com tipo e com Cargo quando tem 
        var resultado = db.Gente.OfType<GenteFisica>().Select(x => new
        {                
            x.Id,
            x.Nome, 
            Gente = "GenteFisica",
            Cargo = x.Cargo.Nome
        }).Concat(db.Gente.OfType<GenteJuridica>().Select(g => new
        {                
            g.Id,
            g.Nome,
            Gente = "GenteJuridica",
            Cargo = ""
        })).AsQueryable();
        var resultOrdemNome = resultado.OrderBy(x => x.Nome).ToList();
  • I’ll just test here if I can maintain ordination.

  • @Tiagopsilva I’m tidying up now I need to understand maybe what you want let me put the post already I tell you

  • 1

    The Post is not so important. The Post is only a differential as CPF and the CNPJ. The issue is to maintain an order by the field requested. Take my example of ordination, where the idea would be an ordination by Name. Finally, how to unite, according to your example, gentefi and genteju and order by Nome?

  • @James takes a look at the last item joining the two

  • 2

    Resolved and accepted. + 1 for the broad explanation. Very good! Thank you very much!

2

If LazyLoading is enabled, which is the case by default.

var items = repository.Pessoas.OrderBy(x => x.Nome);  

Returns all table records Pessoas ordered by name.

The only thing needed to gain access to the office is to make a cast for Membro.

Edited according to the Fccdias comment.

var pessoas = items.ToList().Select(i =>
            {
                var membro = i as Membro;
                if(membro != null)
                {
                    return new
                    {
                        membro.Id,
                        membro.Nome,
                        Cargo = membro.Cargo
                    };
                }
                else
                {
                    return new
                    {
                        i.id,
                        i.Nome,
                        Cargo = default(Cargo)
                    };
                }
            });
  • 2

    sorry, but, it will only work if you give before a Tolist() or Toarray() (items.Tolist().Select), and in this mode would be slow, because, if you do in Linq Objects (which is not cool if this structure brings many records). Obs: what I’m talking about was tested, and I’ve been in Entity since first version, when I saw your code I went to VS test but, as you had to use Tolist(). Select, it wouldn’t be very cool to use, it works, but as I said to bring in lots of data it will get slow. In my example was Linq To SQL, ie in SQL itself is made the conversions and sent directly to the variable

  • 2

    It’s true, you’re right. The Select of a IQueryable accepts a Expression Tree, not a lambda like I’m doing. Thanks for the comment.

  • 1

    I really liked what you did, congratulations is always good to share various knowledge. @ramaral

Browser other questions tagged

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