简体   繁体   中英

How to write this C# statement in linq

I'm trying to get related tables Adress and PlzOrt into an object User. The relationships are as follows:

User 1:1 Adress

Adress n:1 PlzOrt

Entities Scaffolded from the DB

public partial class User
{
    //No hash sets for Adress Scaffolded (probably unnecessary since the is 
    //only ever one address per user

    public int IdUser { get; set; }
    public int? FidAdresse { get; set; }
    public string Email { get; set; }
    public string Vorname { get; set; }
    public string Nachmname { get; set; }

    [ForeignKey("FidAdresse")]
    [InverseProperty("User")]
    public virtual Adresse FidAdresseNavigation { get; set; }
}

public partial class Adresse
{
    public Adresse()
    {
        User = new HashSet<User>();
    }

    public int IdAdresse { get; set; }
    public int FidPlzOrt { get; set; }
    public string Strasse { get; set; }
    public string Hausnr { get; set; }

    [ForeignKey("FidPlzOrt")]
    [InverseProperty("Adresse")]
    public virtual PlzOrt FidPlzOrtNavigation { get; set; }
    [InverseProperty("FidAdresseNavigation")]
    public virtual ICollection<User> User { get; set; }
}

public partial class PlzOrt
{
    public PlzOrt()
    {
        Adresse = new HashSet<Adresse>();
    }

    public int IdPlzOrt { get; set; }
    public string Plz { get; set; }
    public string Ort { get; set; }

    [InverseProperty("FidPlzOrtNavigation")]
    public virtual ICollection<Adresse> Adresse { get; set; }
}

Here´s the linq that does not work.

return _context.User
            .Include(u => u.FidPermissionNavigation)
            .Include(c => c.FidAdresseNavigation)
                .ThenInclude(c => c.FidPlzOrtNavigation)
            .FirstOrDefault(c => c.IdUser == id);

The linq work when I don´t include the "ThenInclude(c => c.FidPlzOrtNavigation)" statement, but I want this information in my object.

Here´s the C# that gives me the expected results:

 public User GetUser(int id)
    {

        foreach (User user in _context.User)
        {
            if (user.IdUser == id)
            {
                foreach (Adresse adresse in _context.Adresse)
                {
                    if (adresse.IdAdresse == user.FidAdresse)
                    {
                        user.FidAdresseNavigation = adresse;

                        foreach (PlzOrt plzOrt in _context.PlzOrt)
                        {
                            if (plzOrt.IdPlzOrt == adresse.FidPlzOrt)
                            {
                                user.FidAdresseNavigation.FidPlzOrtNavigation = plzOrt;
                                break;
                            }
                        }

                        break;
                    }
                }

                return user;
            }
        }

        return null;
 }

Translating this linq statement would be of great help. Thanks in advance.

Generated db_context code in case you are interested or this helps

modelBuilder.Entity<User>(entity =>
        {
            entity.HasKey(e => e.IdUser)
                .HasName("PRIMARY");

            entity.HasIndex(e => e.FidAdresse)
                .HasName("fk_User_Adresse1_idx");

            entity.HasOne(d => d.FidAdresseNavigation)
                .WithMany(p => p.User)
                .HasForeignKey(d => d.FidAdresse)
                .HasConstraintName("fk_User_Adresse1");
        });

modelBuilder.Entity<Adresse>(entity =>
        {
            entity.HasKey(e => e.IdAdresse)
                .HasName("PRIMARY");

            entity.HasIndex(e => e.FidPlzOrt)
                .HasName("fk_Adresse_plz_ort1_idx");

            entity.HasOne(d => d.FidPlzOrtNavigation)
                .WithMany(p => p.Adresse)
                .HasForeignKey(d => d.FidPlzOrt)
                .OnDelete(DeleteBehavior.ClientSetNull)
                .HasConstraintName("fk_Adresse_plz_ort1");
        });

modelBuilder.Entity<PlzOrt>(entity =>
        {
            entity.HasKey(e => e.IdPlzOrt)
                .HasName("PRIMARY");
        });

So you have an id , and you want the one and only user that has this id as primary key, together with his Address and his PlzOrt.

Whenever you query, use Select to fetch the data. Only use Include if you want to update the fetched data.

The reason for Select is that you have greater freedom of what you select. Besides you can limit the fetched data: if you query Schools with their Students, you know that every Student of School 10 will have a foreign key SchoolId equal to 10. So why fetch this foreign key for every of the Schools 1000 Students?

I'm not familiar with the possibilities of ef-core. Does it know that if you Select one of the virtual properties that a (group-)join is needed? In that case it is easier to use Select.

If you'll have to do your joins yourself:

var requestedUserWithAddresAndPlz = dbContext.Users.

    // keep only the user with id
    .Where(user => user.IdUser == id)

    // every User has exactly one Address: use a normal join
    .Join(dbContext.Addresses,
    user => user.IdUser,              // from every User take IdUser
    address => addres.IdAddress,      // from every Address take IdAddress

    // result selector: take the user with its address to make one new
    (user, address) => new
    {
        // Select only the User properties you plan to use
        Id = user.IdUser,
        Name = user.Name,
        ...

        Address = new
        {
            // again: select only the properties you plan to use
            // no need to select IdAddress, you know the value!
            Street = address.Street,
            HouseNumber = address.HouseNumber,

            // to fetch the Plz: fetch the one-and-only PlzOrt with address.FidPlzOrt
            Plz = dbContext.PlzOrts
                  .Where(plzOrt => plzOrt.PlzOrdIt == address.FidPlzOrt)
                  .FirstOrDefault(),
    })
    .FirstOrDefault();

Note: I used anonymous types to have greater freedom in selecting only the properties I actually plan to use. I can also give my properties the names that I want.

Disadvantage: you can't use anonymous types as return values. If you really need this outside your function, use create a class that contains your data, and use new SomeClass(). Advantage: if your database changes, SomeClass doesn't have to change, and thus your callers won't notice the change in your database.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM