Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/entity-framework/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# EF加载所有子项,即使第一个或默认值在何处_C#_Entity Framework_Linq To Sql - Fatal编程技术网

C# EF加载所有子项,即使第一个或默认值在何处

C# EF加载所有子项,即使第一个或默认值在何处,c#,entity-framework,linq-to-sql,C#,Entity Framework,Linq To Sql,我有以下代码: using (var context = new MyDbContext(connectionString)) { context.Configuration.LazyLoadingEnabled = true; context.Configuration.ProxyCreationEnabled = true; context.Database.Log = logValue => File.AppendAllText(logFilePath, lo

我有以下代码:

using (var context = new MyDbContext(connectionString))
{
    context.Configuration.LazyLoadingEnabled = true;
    context.Configuration.ProxyCreationEnabled = true;
    context.Database.Log = logValue => File.AppendAllText(logFilePath, logValue);

    var testItem1 = context.ParentTable
                      .FirstOrDefault(parent => parent.Id == 1)
                      .ChildEntities
                      .FirstOrDefault(child => child.ChildId == 2000);
}
在执行此代码并检查EF 6的日志文件(logFilePath)时,我看到为Id=1的整个ParentTable记录加载了子实体,同时启用了LazyLoading并指定了子表的条件(child.ChildId==2000)

EF不应该只加载相关的子级,还是先执行读取项,然后再执行内存中的data FirstOrDefault

因为如果某个父实体有许多子实体,这样,在加载带有条件的子实体时,会显著降低性能

我想解决方法是分别加载子实体

这是上述代码的完整日志文件(为了便于阅读,排除了某些行):

注意:添加了相关类:

public class MyDbContext : DbContext
{
    public DbSet<ParentTable> ParentTable { get; set; }

    public DbSet<ChildTable> ChildTable { get; set; }

    static MyDbContext()
    {
        Database.SetInitializer<MyDbContext>(null);
    }

    public MyDbContext(string connStr)
        : base(connStr)
    {

    }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity<ParentTable>()
            .HasMany(t => t.ChildEntities);
    }
}

[Table("ParentTable", Schema = "dbo")]
public class ParentTable
{
    public int Id { get; set; }

    public virtual ICollection<ChildTable> ChildEntities { get; set; }
}

[Table("ChildTable", Schema = "dbo")]
public class ChildTable
{
    public int ChildId { get; set; }

    public int ParentId { get; set; }

    [ForeignKey("ParentId")]
    public virtual ParentTable Parent { get; set; }
}
公共类MyDbContext:DbContext
{
公共数据库集父表{get;set;}
公共DbSet子表{get;set;}
静态MyDbContext()
{
Database.SetInitializer(null);
}
公共MyDbContext(字符串connStr)
:base(connStr)
{
}
模型创建时受保护的覆盖无效(DbModelBuilder modelBuilder)
{
modelBuilder.Entity()
.HasMany(t=>t.ChildEntities);
}
}
[表(“父表”,Schema=“dbo”)]
公共类父表
{
公共int Id{get;set;}
公共虚拟ICollection子实体{get;set;}
}
[Table(“ChildTable”,Schema=“dbo”)]
公共类子表
{
public int ChildId{get;set;}
public int ParentId{get;set;}
[ForeignKey(“ParentId”)]
公共虚拟父表父{get;set;}
}
使用此查询:

var testItem1 = context.ChildTables
                      .Include(p=>p.ParentTable)
                      .Where(ch => ch.ChildId == 2000)
                      .FirstOrDefault();

您的问题与延迟加载无关。这是因为在LINQ方法序列中过早地使用了
FirstOrDefault

我将首先编写适当的查询,然后解释为什么该查询更好

var result = dbContext.ParentTable
    .Where(parent => parent.Id == 1)
    .SelectMany(parent => parent.ChildEntities.Where(child => child.ChildId == 2000))
    .FirstOrDefault();
如果仔细观察LINQ方法,您会发现有两种类型:返回
IQueryable
的类型和其他类型。第一组的LINQ方法使用延迟执行,也称为延迟执行。这意味着这些语句不会执行查询。它们只会更改IQueryable中的
表达式。尚未查询数据库

后一组中的LINQ语句将深入调用
GetEnumerator()
,大多数情况下会重复调用
MoveNext()/Current
。这将把
IQueryable.Expression
发送给
IQueryable.Provider
,后者将尝试将表达式转换为SQL并执行查询以从数据库中获取数据(确切地说:转换不一定总是SQL,这取决于提供程序)。获取的数据显示为一个
IEnumerator
,您可以调用其中的
MoveNext()/Current

您的第一个
FirstOrDefault
将已经执行查询。除此之外,它执行得太早,可能会获取比您想要的更多的数据,还可能存在返回
null
的问题

正确的方法是使用
选择
。只有最后一条语句应该包含不可查询的方法,如
FirstOrDefault

我使用SelectMany而不是Select,因为您只对父对象的子实体感兴趣,而对任何父对象属性都不感兴趣

var result = dbContext.ParentTable
    .Where(parent => parent.Id == 1)
    .SelectMany(parent => parent.ChildEntities.Where(child => child.ChildId == 2000))
    .FirstOrDefault();
虽然这解决了您的问题,但这将获取比您实际计划使用的更多的数据。例如,每个子级都有父级的外键。您知道父项的主键值等于1,因此子项的外键值也将为1。为什么要转移它

在这种情况下,我希望只有一个孩子,所以问题不是太大。但在其他情况下,您可能会经常发送相同的值

使用实体框架时,请始终使用“选择”,并仅选择计划使用的属性。如果计划更新获取的项目,则仅获取整行或使用Include


如果不使用Select,另一件会减慢处理速度的事情是,当您获取完整的行时,原始获取的数据及其副本将放在
DbContext.ChangeTracker
中。这样做是为了在调用
SaveChanges
时检测必须保存的值。如果您不打算更新提取的数据,请不要浪费处理能力将提取的数据放入更改跟踪器。

请显示ParentTable和ChildTable类好吗?添加在问题末尾谢谢!不完全是我想要的,但它可以。仍然不知道为什么EF不能识别并进行适当的查询:)
var result = dbContext.ParentTable
    .Where(parent => parent.Id == 1)
    .SelectMany(parent => parent.ChildEntities.Where(child => child.ChildId == 2000))
    .FirstOrDefault();