Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Entity-Framework Join Explanation

I have the following entities

//Active Auction Entity
public class ActiveAuction
{
    public int Id { get; set; }

    public string Title { get; set; }

    public int? FirstAuctionId { get; set; }

    public int? SecondAuctionId { get; set; }

    public int? ThirdAuctionId { get; set; }

    public virtual Auction FirstAuction { get; set; }

    public virtual Auction SecondAuction { get; set; }

    public virtual Auction ThirdAuction { get; set; }
}

// Auction Entity
public class Auction
{
    public int AuctionId { get; set; }

    public AuctionStatus AuctionStatus { get; set; }

    public int? DepartmentId { get; set; }

    public virtual Department Department { get; set; }

}

// Department Entity
public class Department
{
    public int DepartmentId { get; set; }

    public string DepartmentName { get; set; }

    public int? AdminId { get; set; }

    public virtual Admin Admin { get; set; }
}

What I'm trying to do is to get Active Auctions with Auctions loaded and Auction also have Departments loaded I know that I should write Include for every object to be loaded so the generated SQL by EF will have join statement to select there object for me

but this is the existing code

 using (var dc = DataContext())
    {
        await dc.Auction
           .Include("Department.Admin")
           .Where(i => i.Id == id && i.AuctionStatus == AuctionStatus.Accepted).ToListAsync();

        return await dc.ActiveAuction.
           SingleOrDefaultAsync(i => i.Id == id);
    }

I don't know How but this code works and the returned ActiveAuctions include all desired objects

I checked for SQL executed against the database and as expected it generate to separate queries.

I want an explanation to understand how the returned ActiveAcutions loaded with the other mentioned entities!!?

like image 875
Mohamed Badr Avatar asked Sep 15 '15 15:09

Mohamed Badr


People also ask

What is a join entity?

A join defines how one or more entity objects relate. A join can be a self join that creates hierarchical data, a join between two entity objects in the same physical schema, or a join between two entity objects in different physical schemas, known as a cross-schema join.

How do I join two entities in Entity Framework?

Method Syntax Next, use the join extension method and specify the inner table as the first argument to the join method. . Join(db. EmailAddresses, The next two arguments specify the condition on which you want to join the tables.

How do you explain a join?

A join is an SQL operation performed to establish a connection between two or more database tables based on matching columns, thereby creating a relationship between the tables.


1 Answers

The reason is simple. As you most likely know, Entity Framework tracks entities you fetched from database, mostly to detect changes to them and apply those changes to database when you call SaveChanges. However, that means EF context has kind of cache of entities fetched from database so far.

EDIT: As correctly pointed in comments by @GertArnold - my explanation with dynamic proxies was completely wrong - it works this way even if ProxyCreationEnabled is false. The real reason is relationship fix up which is performed by entity framework when DetectChanges is called (it is called implicitly on various events, like attaching entity to context, or executing query over DbSet). During that relationship fix up, EF synchronizes navigation properties and foreign keys which in your case causes behavior you observe. More about relationship fix up in MSDN

To verify this you may use this simple code:

using (var ctx = new TestEntities()) {
     ctx.Configuration.LazyLoadingEnabled = false;                
     ctx.Configuration.ProxyCreationEnabled = false;                
     var code = ctx.Codes.First();                
     var error = ctx.Errors.First();
     Debug.Assert(Object.ReferenceEquals(error.Code, code));                                
}

Here I first fetch some entity (Code), then I fetch another entity (Error) which has navigation property Code. You see that lazy loading is disabled. The following assert will pass, because error.Code and code is the same .NET object, which confirms it was fetched from context cache.

like image 188
Evk Avatar answered Sep 23 '22 10:09

Evk