简体   繁体   English

实体框架联接说明

[英]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 我想做的是在加载了Auctions获得Active Auctions ,并且Auction也加载了Departments我知道我应该为要加载的每个对象编写Include ,以便EF生成的SQL将具有join语句为我选择那里的对象

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 我不知道如何操作,但是这段代码有效,返回的ActiveAuctions包含所有所需的对象

I checked for SQL executed against the database and as expected it generate to separate queries. 我检查了针对数据库执行的SQL,并按预期生成了单独的查询。

I want an explanation to understand how the returned ActiveAcutions loaded with the other mentioned entities!!? 我想要一个解释,以了解返回的ActiveAcutions如何与其他提到的实体一起加载!!

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 . 您很可能知道,Entity Framework会跟踪您从数据库中获取的实体,主要是检测对它们的更改并将这些更改应用于数据库,当您调用SaveChanges However, that means EF context has kind of cache of entities fetched from database so far. 但是,这意味着到目前为止,EF上下文具有某种类型的从数据库中获取的实体缓存。

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 . 编辑 :正如@GertArnold在评论中正确指出的那样-我对动态代理的解释是完全错误的-即使ProxyCreationEnabledfalse它也可以通过这种方式工作。 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 ). 真正的原因是关系修复,它由实体框架在DetectChanges时执行(在各种事件上隐式调用,例如将实体附加到上下文或通过DbSet执行查询)。 During that relationship fix up, EF synchronizes navigation properties and foreign keys which in your case causes behavior you observe. 在修复关系期间,EF会同步导航属性和外键,这会导致您观察到的行为。 More about relationship fix up in MSDN 有关在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. 在这里,我首先获取某个实体(代码),然后获取另一个具有导航属性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. 下面的断言将通过,因为error.Code和code是相同的.NET对象,该对象确认它是从上下文缓存中获取的。

The behavior you are seeing is because EF internally maintains object cache per each DB context instance. 您看到的行为是因为EF在每个数据库上下文实例内部维护对象缓存。 Every time when query is executed, EF first checks internal cache to see if it contains neccessary entity. 每次执行查询时,EF都会首先检查内部缓存以查看其是否包含必要的实体。 If entity is found, then it is returned from cache without even querying database. 如果找到了实体,那么它将从缓存中返回,甚至不查询数据库。

In your sample here it what happens: 在您的示例中,发生了什么:

  • First query gets some Auction entities and add them to cache; 第一个查询获取一些Auction实体并将其添加到缓存;
  • Second query gets filtered ActiveAuction entities and add them to cache; 第二个查询获取过滤的ActiveAuction实体并将其添加到缓存中;
  • When you inspect results of second query you see that properties are already filled because they were found in cache by key; 当您检查第二个查询的结果时,您会看到属性已被填充,因为它们是通过键在缓存中找到的;

You can change this behavior by changing MergeOption setting on database context instance. 您可以通过更改数据库上下文实例上的MergeOption设置来更改此行为。 This setting changes how EF merges results from database to cache. 此设置更改了EF将结果从数据库合并到缓存的方式。

Possible values are as follows: 可能的值如下:

AppendOnly (default value) 仅附加(默认值)

Objects that do not exist in the object context are attached to the context. 对象上下文中不存在的对象将附加到上下文。 If an object is already in the context, the current and original values of object's properties in the entry are not overwritten with data source values. 如果上下文中已有对象,则条目中对象属性的当前值和原始值不会被数据源值覆盖。 The state of the object's entry and state of properties of the object in the entry do not change. 对象条目的状态和条目中对象的属性状态不变。 AppendOnly is the default merge option. AppendOnly是默认的合并选项。

NoTracking 无追踪

Objects are maintained in a Detached state and are not tracked in the ObjectStateManager. 对象保持在分离状态,并且不在ObjectStateManager中跟踪。 However, Entity Framework-generated entities and POCO entities with proxies maintain a reference to the object context to facilitate loading of related objects. 但是,实体框架生成的实体和带有代理的POCO实体维护对对象上下文的引用,以方便相关对象的加载。

OverwriteChanges 覆盖更改

Objects that do not exist in the object context are attached to the context. 对象上下文中不存在的对象将附加到上下文。 If an object is already in the context, the current and original values of object's properties in the entry are overwritten with data source values. 如果上下文中已有对象,则条目中对象属性的当前值和原始值将被数据源值覆盖。 The state of the object's entry is set to Unchanged, no properties are marked as modified. 对象条目的状态设置为“不变”,没有属性标记为已修改。

PreserveChanges 保留更改

Objects that do not exist in the object context are attached to the context. 对象上下文中不存在的对象将附加到上下文。

( https://msdn.microsoft.com/en-us/library/system.data.objects.mergeoption(v=vs.110).aspx ) https://msdn.microsoft.com/zh-cn/library/system.data.objects.mergeoption(v=vs.110).aspx

And here is a good article that describes all the options in more details and contains some code samples as well. 这篇好文章更详细地描述了所有选项,并且还包含一些代码示例。

http://www.develop1.net/public/post/Do-you-understand-MergeOptions.aspx http://www.develop1.net/public/post/Do-you-understand-MergeOptions.aspx

EDIT: Just wanted to add that default behavior can lead to strange results if the first query does projection of entity (for example, selects only some columns). 编辑:只是想补充一下,如果第一个查询进行实体投影(例如,仅选择一些列),则默认行为会导致奇怪的结果。 In this case entity will still be added to the cache even if it is only half loaded. 在这种情况下,即使实体仅被加载一半,实体仍将被添加到缓存中。 This means that second query will return half loaded entity as well. 这意味着第二个查询也将返回一半加载的实体。

You can't use .Include("Department.Admin") since EF don't load inner inclusions like yours, say, Admin . 您不能使用.Include("Department.Admin")因为EF不会加载像Admin这样的内含物。

Note that , when you work with EF code first, It's better to determine the real relationships among tables, whether in classes or using fluent API, to meet a good design without anomaly. 请注意 ,当您首先使用EF代码时,最好确定表之间的真实关系,无论是在类中还是在使用流利的API,以满足没有异常的良好设计。 So, using three Auction s in ActiveAuction without any clarification may disturb you. 因此,在ActiveAuction使用三个Auction而不作任何澄清可能会打扰您。

Another thing is that, It seems you're using ActiveAuction as foreign key in each Auction without any declaration! 另一件事是,似乎您在每次Auction都使用ActiveAuction作为外键,而没有任何声明! So change your Auction class to this: 因此,将您的Auction类别更改为:

public class Auction
{
    [ForeignKey("ActiveOne")]
    public int AuctionId { get; set; }

    public AuctionStatus AuctionStatus { get; set; }

    public int? DepartmentId { get; set; }

    public virtual Department Department { get; set; }

    [Required]
    public virtual ActiveAuction ActiveOne { get; set; } //match this name

}

With this in mind, I believe that there are some ambiguities in your models. 考虑到这一点,我相信您的模型中存在一些歧义。 Let's speak about the query. 让我们谈谈查询。 As you said : 如你所说 :

What I'm trying to do is to get ActiveAuctions with Auctions loaded and Auction also have Departments loaded 我想做的是在ActiveAuctions上加载Auctions并且Auction也加载了Departments

So you can use something like this query: 因此,您可以使用类似以下查询的内容:

await context.Auctions.Include("Department").Include("ActiveOne")
.Where(i => i.id == id && i.AuctionStatus == AuctionStatus.Accepted)
.Select(i => i.ActiveOne).ToListAsync();

当EF加载任何实体时,它将它们与缓存中的现有实体联系起来,从而使模型完整,指向所有已加载对象的所有引用。

"Lazy loading is the process whereby an entity or collection of entities is automatically loaded from the database the first time that a property referring to the entity/entities is accessed." “延迟加载是这样的过程,在该过程中,首次访问引用一个或多个实体的属性时,将从数据库自动加载一个实体或实体集合。” See this article: https://msdn.microsoft.com/en-us/data/jj574232.aspx 看到这篇文章: https : //msdn.microsoft.com/en-us/data/jj574232.aspx

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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