C# 带有LoadAsync()的Query()不返回实体,尽管它应该返回

C# 带有LoadAsync()的Query()不返回实体,尽管它应该返回,c#,entity-framework,entity-framework-6.1,C#,Entity Framework,Entity Framework 6.1,SchoolclassCodes集合为空,但必须有一个schoolclassCode,因为在底部LoadAsync方法中是我在此查询的Id为的schoolclassCode 在瞳孔.SchoolclassCodes集合中用一个schoolclassCode填充瞳孔 为什么底部LoadAsync有效,而顶部LoadAsync无效 更新 很抱歉混淆了[NotMappedAttribute]中的瞳孔.SchoolclassCodeId 这就是关系 学生N有M个班级代码 每个实体都有另一个实体的集合 最

SchoolclassCodes集合为空,但必须有一个schoolclassCode,因为在底部LoadAsync方法中是我在此查询的Id为的schoolclassCode

在瞳孔.SchoolclassCodes集合中用一个schoolclassCode填充瞳孔

为什么底部LoadAsync有效,而顶部LoadAsync无效

更新

很抱歉混淆了[NotMappedAttribute]中的瞳孔.SchoolclassCodeId

这就是关系

学生N有M个班级代码

每个实体都有另一个实体的集合

最后一个LoadAsync可以工作,正如我所说的,关系设置中没有问题

问题是第一个LoadAsync不能像上面描述的那样工作

延迟加载已完全禁用!我在任何地方都不用虚拟道具

更新2

await context.Entry(pupil).Collection(p => p.SchoolclassCodes).LoadAsync();
public class学校代码
{
公立学校班级代码()
{
瞳孔=新HashSet();
}
公共int Id{get;set;}
公共ISet学生{get;set;}
}
公办班学生
{
公立学生()
{
SchoolclassCodes=newHashSet();
}
公共int Id{get;set;}
公共ISet学校类代码{get;set;}
[未映射]
public int SchoolclassCodeId{get;set;}
}

这个字段
student.SchoolclassCodeId
显然没有用于这个问题,所以我们还是算了吧

您的第二个问题:

public class SchoolclassCode
{
    public SchoolclassCode()
    {
        Pupils = new HashSet<Pupil>();
    }

    public int Id { get; set; }
    public ISet<Pupil> Pupils { get; set; }

}

public class Pupil
{
    public Pupil()
    {
        SchoolclassCodes = new HashSet<SchoolclassCode>();
    }

    public int Id { get; set; }
    public ISet<SchoolclassCode> SchoolclassCodes { get; set; }

    [NotMapped]
    public int SchoolclassCodeId { get; set; }
}
一切正常。我们可以使用以下代码进行验证:

await context.Entry(pupil).Collection(p => p.SchoolclassCodes).LoadAsync();
假设
学生
在其
学校类代码
中有三个元素,则
IsLoaded
将为
true
,并且
foreach
循环将显示三个ID

然后是您的第一个查询:

await context.Entry(pupil).Collection(p => p.SchoolclassCodes).LoadAsync();
Console.WriteLine("IsLoaded = " + context.Entry(pupil).Collection(p => p.SchoolclassCodes).IsLoaded);
foreach (var code in pupil.SchoolclassCodes)
  Console.WriteLine("  " + code.Id);
让我们测试一下:

await context.Entry(pupil).Collection(p => p.SchoolclassCodes).Query().Where(s => s.Id == schoolclassCodeId).LoadAsync();
假设确实存在一个
SchoolclassCode
,它的
Id
1
,那么
AsyncLoad
应该只将一个
SchoolclassCode
加载到内存中。但是在输出中,您可以看到
IsLoaded=false
,而
foreach
完全没有给出任何信息!为什么?

首先,
AsyncLoad
不是应用于
集合(p=>p.SchoolclassCodes)
,而是从它派生的
IQueryable
,因此
IsLoaded
应该是
false
,这是可以理解的

但是有一个
SchoolclassCode
确实加载到了上下文中:

var pupil = context.Pupils.First();
var schoolclassCodeId = 1;
await context.Entry(pupil).Collection(p => p.SchoolclassCodes).Query().Where(s => s.Id == schoolclassCodeId).LoadAsync();
Console.WriteLine("IsLoaded = " + context.Entry(pupil).Collection(p => p.SchoolclassCodes).IsLoaded);
foreach (var code in pupil.SchoolclassCodes)
  Console.WriteLine("  " + code.Id);
foreach
输出单个
1
。那么,为什么我们不能在
小学生.SchoolclassCodes
中找到
学校班级代码呢

答案是:
SchoolclassCode
school
之间的关系是多对多的。在这种情况下,实体框架不会进行关系修复,即自动将
SchoolclassCode
添加到
studio.SchoolclassCodes
,因此您不会在那里看到它。如果你真的想修复关系,你必须手动完成

更新1 引自:

Query
方法提供对实体框架在加载相关实体时将使用的基础查询的访问。然后,您可以使用LINQ对查询应用过滤器,然后通过调用LINQ扩展方法执行查询,例如
ToList
Load
,等。
Query
方法可与引用和集合导航属性一起使用,但对于仅可用于加载集合一部分的集合最为有用

这有点令人困惑。这似乎与我的论点相矛盾,但事实并非如此。实际上,在上面的引用中,“加载”一词的意思是“加载到上下文中”,而不是“加载到导航属性中”,因此MSDN和我的答案都是正确的。为了证明我的说法,让我们从几个实验开始,然后深入研究源代码

模型 出于演示目的,我们将另一个类添加到模型中:

foreach (var code in context.SchoolclassCodes.Local)
  Console.WriteLine("  " + code.Id);
数据 我们在数据库中有以下条目:

public class SchoolEntities: DbContext
{
  public SchoolEntities()
    : base("name=SchoolEntities")
  {
  }

  public DbSet<Pupil> Pupils { get; set; }
  public DbSet<Book> Books { get; set; }
  public DbSet<SchoolclassCode> SchoolclassCodes { get; set; }
}
实验1:直接加载 我们直接将相关数据加载到导航属性中。为简单起见,我们使用
Load
方法代替
LoadAsync
。它们的作用完全相同,只是前者是同步的,后者是异步的。守则:

Pupil  (Id = 1)
  the Books property contains:
    Book  (Id = 1)
    Book  (Id = 2)
  the SchoolclassCodes property contains:
    SchoolclassCode  (Id = 1)
    SchoolclassCode  (Id = 2)
    SchoolclassCode  (Id = 3)
以及输出:

using (var context = new SchoolEntities()) {
  Console.WriteLine("Books direct load");
  var pupil = context.Pupils.First();
  context.Entry(pupil).Collection(p => p.Books).Load();
  Console.WriteLine("  IsLoaded = " + context.Entry(pupil).Collection(p => p.Books).IsLoaded);
  Console.WriteLine("  Items in the pupil:");
  foreach (var item in pupil.Books)
    Console.WriteLine("    " + item.Id);
  Console.WriteLine("  Items in the context:");
  foreach (var item in context.Books.Local)
    Console.WriteLine("    " + item.Id);
}
using (var context = new SchoolEntities()) {
  Console.WriteLine("SchoolclassCodes direct load");
  var pupil = context.Pupils.First();
  context.Entry(pupil).Collection(p => p.SchoolclassCodes).Load();
  Console.WriteLine("  IsLoaded = " + context.Entry(pupil).Collection(p => p.SchoolclassCodes).IsLoaded);
  Console.WriteLine("  Items in the pupil:");
  foreach (var item in pupil.SchoolclassCodes)
    Console.WriteLine("    " + item.Id);
  Console.WriteLine("  Items in the context:");
  foreach (var item in context.SchoolclassCodes.Local)
    Console.WriteLine("    " + item.Id);
}
using (var context = new SchoolEntities()) {
  Console.WriteLine("Books partial query load");
  var pupil = context.Pupils.First();
  context.Entry(pupil).Collection(p => p.Books).Query().Where(s => s.Id == 1).Load();
  Console.WriteLine("  IsLoaded = " + context.Entry(pupil).Collection(p => p.Books).IsLoaded);
  Console.WriteLine("  Items in the pupil:");
  foreach (var item in pupil.Books)
    Console.WriteLine("    " + item.Id);
  Console.WriteLine("  Items in the context:");
  foreach (var item in context.Books.Local)
    Console.WriteLine("    " + item.Id);
}
using (var context = new SchoolEntities()) {
  Console.WriteLine("SchoolclassCodes partial query load");
  var pupil = context.Pupils.First();
  context.Entry(pupil).Collection(p => p.SchoolclassCodes).Query().Where(s => s.Id == 1).Load();
  Console.WriteLine("  IsLoaded = " + context.Entry(pupil).Collection(p => p.SchoolclassCodes).IsLoaded);
  Console.WriteLine("  Items in the pupil:");
  foreach (var item in pupil.SchoolclassCodes)
    Console.WriteLine("    " + item.Id);
  Console.WriteLine("  Items in the context:");
  foreach (var item in context.SchoolclassCodes.Local)
    Console.WriteLine("    " + item.Id);
}
using (var context = new SchoolEntities()) {
  Console.WriteLine("Books full query load");
  var pupil = context.Pupils.First();
  context.Entry(pupil).Collection(p => p.Books).Query().Load();
  // output statements omitted...
}
using (var context = new SchoolEntities()) {
  Console.WriteLine("SchoolclassCodes full query load");
  var pupil = context.Pupils.First();
  context.Entry(pupil).Collection(p => p.SchoolclassCodes).Query().Load();
  // output statements omitted...
}
using (var context = new SchoolEntities()) {
  Console.WriteLine("Books side load");
  var pupil = context.Pupils.First();
  context.Books.Where(s => s.Pupil.Id == pupil.Id).Load();
  // output statements omitted...
}
using (var context = new SchoolEntities()) {
  Console.WriteLine("SchoolclassCodes side load");
  var pupil = context.Pupils.First();
  context.SchoolclassCodes.Where(s => s.Pupils.Select(t => t.Id).Contains(pupil.Id)).Load();
  // output statements omitted...
}
实验分为两部分,一部分用于
书籍
,另一部分用于
学校课程代码
。两个上下文用于确保这两个部分不会相互干扰。我们使用集合的
Load
方法将相关数据直接加载到导航属性中。结果表明:

  • 集合的
    IsLoaded
    属性设置为
    true
  • 加载的数据可以在导航属性中找到(即
    瞳孔.Books
    瞳孔.SchoolclassCodes
  • 加载的数据也可以在上下文中找到(即
    context.Books.Local
    context.SchoolclassCodes.Local
  • 实验2:查询部分加载 我们使用
    查询
    方法加载部分相关数据,然后使用
    方法,其中

    Books direct load
      IsLoaded = True
      Items in the pupil:
        1
        2
      Items in the context:
        1
        2
    SchoolclassCodes direct load
      IsLoaded = True
      Items in the pupil:
        1
        2
        3
      Items in the context:
        1
        2
        3
    
    大部分代码与实验1相同;请注意以
    context.Entry(瞳孔)…
    开头的行。输出:

    using (var context = new SchoolEntities()) {
      Console.WriteLine("Books direct load");
      var pupil = context.Pupils.First();
      context.Entry(pupil).Collection(p => p.Books).Load();
      Console.WriteLine("  IsLoaded = " + context.Entry(pupil).Collection(p => p.Books).IsLoaded);
      Console.WriteLine("  Items in the pupil:");
      foreach (var item in pupil.Books)
        Console.WriteLine("    " + item.Id);
      Console.WriteLine("  Items in the context:");
      foreach (var item in context.Books.Local)
        Console.WriteLine("    " + item.Id);
    }
    using (var context = new SchoolEntities()) {
      Console.WriteLine("SchoolclassCodes direct load");
      var pupil = context.Pupils.First();
      context.Entry(pupil).Collection(p => p.SchoolclassCodes).Load();
      Console.WriteLine("  IsLoaded = " + context.Entry(pupil).Collection(p => p.SchoolclassCodes).IsLoaded);
      Console.WriteLine("  Items in the pupil:");
      foreach (var item in pupil.SchoolclassCodes)
        Console.WriteLine("    " + item.Id);
      Console.WriteLine("  Items in the context:");
      foreach (var item in context.SchoolclassCodes.Local)
        Console.WriteLine("    " + item.Id);
    }
    
    using (var context = new SchoolEntities()) {
      Console.WriteLine("Books partial query load");
      var pupil = context.Pupils.First();
      context.Entry(pupil).Collection(p => p.Books).Query().Where(s => s.Id == 1).Load();
      Console.WriteLine("  IsLoaded = " + context.Entry(pupil).Collection(p => p.Books).IsLoaded);
      Console.WriteLine("  Items in the pupil:");
      foreach (var item in pupil.Books)
        Console.WriteLine("    " + item.Id);
      Console.WriteLine("  Items in the context:");
      foreach (var item in context.Books.Local)
        Console.WriteLine("    " + item.Id);
    }
    using (var context = new SchoolEntities()) {
      Console.WriteLine("SchoolclassCodes partial query load");
      var pupil = context.Pupils.First();
      context.Entry(pupil).Collection(p => p.SchoolclassCodes).Query().Where(s => s.Id == 1).Load();
      Console.WriteLine("  IsLoaded = " + context.Entry(pupil).Collection(p => p.SchoolclassCodes).IsLoaded);
      Console.WriteLine("  Items in the pupil:");
      foreach (var item in pupil.SchoolclassCodes)
        Console.WriteLine("    " + item.Id);
      Console.WriteLine("  Items in the context:");
      foreach (var item in context.SchoolclassCodes.Local)
        Console.WriteLine("    " + item.Id);
    }
    
    using (var context = new SchoolEntities()) {
      Console.WriteLine("Books full query load");
      var pupil = context.Pupils.First();
      context.Entry(pupil).Collection(p => p.Books).Query().Load();
      // output statements omitted...
    }
    using (var context = new SchoolEntities()) {
      Console.WriteLine("SchoolclassCodes full query load");
      var pupil = context.Pupils.First();
      context.Entry(pupil).Collection(p => p.SchoolclassCodes).Query().Load();
      // output statements omitted...
    }
    
    using (var context = new SchoolEntities()) {
      Console.WriteLine("Books side load");
      var pupil = context.Pupils.First();
      context.Books.Where(s => s.Pupil.Id == pupil.Id).Load();
      // output statements omitted...
    }
    using (var context = new SchoolEntities()) {
      Console.WriteLine("SchoolclassCodes side load");
      var pupil = context.Pupils.First();
      context.SchoolclassCodes.Where(s => s.Pupils.Select(t => t.Id).Contains(pupil.Id)).Load();
      // output statements omitted...
    }
    
    看到区别了吗

  • IsLoaded
    现在在这两种情况下都是
    false
  • 加载的数据仍然进入上下文
  • 但是,加载的数据不会进入
    SchoolclassCodes
    案例中的导航属性,而会进入
    Books
    案例中
  • 这种差异是由关系类型造成的:
    书籍
    是一对多,而
    学校班级代码
    是多对多。实体框架以不同的方式对待这两种类型

    实验3:带查询的满载 那么,如果我们使用
    Query
    wi呢
    context.Entry(pupil).Collection(p => p.Books).Query()
    
    string sourceQuery = GenerateQueryText();
    var query = new ObjectQuery<TEntity>(sourceQuery, _context, mergeOption);
    AddQueryParameters(query);
    return query;
    
    SELECT VALUE [TargetEntity]
    FROM (SELECT VALUE x
          FROM [SchoolEntities].[Pupil_Books] AS x
          WHERE Key(x.[Pupil_Books_Source]) = ROW(@EntityKeyValue1 AS EntityKeyValue1)) AS [AssociationEntry]
    INNER JOIN [SchoolEntities].[Books] AS [TargetEntity]
      ON Key([AssociationEntry].[Pupil_Books_Target]) = Key(Ref([TargetEntity]))
    
    context.Books.Where(s => s.Pupil.Id == pupil.Id)
    
    context.Entry(pupil).Collection(p => p.Book).Load()
    
    var sourceQuery = CreateSourceQuery<TEntity>(mergeOption, out hasResults);
    IEnumerable<TEntity> refreshedValues;
    refreshedValues = sourceQuery.Execute(sourceQuery.MergeOption);
    Merge(refreshedValues, mergeOption, true /*setIsLoaded*/);
    
    context.Entry(pupil).Collection(p => p.Book).Query().Load()
    
    public static void Load(this IQueryable source)
    {
        Check.NotNull(source, "source");
    
        var enumerator = source.GetEnumerator();
        try
        {
            while (enumerator.MoveNext())
            {
            }
        }
        finally
        {
            var asDisposable = enumerator as IDisposable;
            if (asDisposable != null)
            {
                asDisposable.Dispose();
            }
        }
    }
    
    using (var context = new SchoolEntities()) {
      Console.WriteLine("Books side load");
      var pupil = context.Pupils.First();
      context.Books.Where(s => s.Pupil.Id == pupil.Id).Load();
      // output statements omitted...
    }
    using (var context = new SchoolEntities()) {
      Console.WriteLine("SchoolclassCodes side load");
      var pupil = context.Pupils.First();
      context.SchoolclassCodes.Where(s => s.Pupils.Select(t => t.Id).Contains(pupil.Id)).Load();
      // output statements omitted...
    }
    
    Books side load
      IsLoaded = False
      Items in the pupil:
        1
        2
      Items in the context:
        1
        2
    SchoolclassCodes side load
      IsLoaded = False
      Items in the pupil:
      Items in the context:
        1
        2
        3
    
    context.Entry(pupil).Collection(p => p.SchoolclassCodes).Load();
    var code = pupil.SchoolclassCodes.Where(...).First();
    pupil.SchoolclassCodes.Remove(code);
    context.SaveChanges();
    
    var code = context.Entry(pupil).Collection(p => p.SchoolclassCodes).Query().Where(...).First();
    var objectStateManager = ((IObjectContextAdapter)context).ObjectContext.ObjectStateManager;
    objectStateManager.ChangeRelationshipState(pupil, code, p => p.SchoolclassCodes, EntityState.Deleted);
    context.SaveChanges();
    
    var code = new SchoolclassCode { Id = 1 };
    context.SchoolclassCodes.Attach(code);
    var objectStateManager = ((IObjectContextAdapter)context).ObjectContext.ObjectStateManager;
    objectStateManager.ChangeRelationshipState(pupil, code, p => p.SchoolclassCodes, EntityState.Deleted);
    context.SaveChanges();