C# 带有LoadAsync()的Query()不返回实体,尽管它应该返回
SchoolclassCodes集合为空,但必须有一个schoolclassCode,因为在底部LoadAsync方法中是我在此查询的Id为的schoolclassCode 在瞳孔.SchoolclassCodes集合中用一个schoolclassCode填充瞳孔 为什么底部LoadAsync有效,而顶部LoadAsync无效 更新 很抱歉混淆了[NotMappedAttribute]中的瞳孔.SchoolclassCodeId 这就是关系 学生N有M个班级代码 每个实体都有另一个实体的集合 最后一个LoadAsync可以工作,正如我所说的,关系设置中没有问题 问题是第一个LoadAsync不能像上面描述的那样工作 延迟加载已完全禁用!我在任何地方都不用虚拟道具 更新2C# 带有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个班级代码 每个实体都有另一个实体的集合 最
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();