C# 如何将数据从LINQ流式传输到实体查询?
我想知道如何使用EF6从SQL server进行数据流传输 假设有这些类C# 如何将数据从LINQ流式传输到实体查询?,c#,entity-framework-6,C#,Entity Framework 6,我想知道如何使用EF6从SQL server进行数据流传输 假设有这些类 个人知识库 EFPerson(EF模型) DomainPerson(域模型) 人格阶层 假设PersonUsingClass依赖于获取一组DomainPersons。 假设业务规则规定不允许EFPerson离开PersonRepository 通常,我会有一个如下所示的存储库方法: public IEnumerable<DomainPerson> GetPeople() {
- 个人知识库
- EFPerson(EF模型)
- DomainPerson(域模型)
- 人格阶层
public IEnumerable<DomainPerson> GetPeople()
{
using (var db = new efContext())
{
IQueryable efPeople = db.Person.Where(someCriteria);
foreach (var person in efPeople)
{
yield return person.ToDomainPerson();
}
}
}
public IEnumerable GetPeople()
{
使用(var db=new efContext())
{
IQueryable efPeople=db.Person.Where(someCriteria);
foreach(efPeople中的var person)
{
让出返回人。ToDomainPerson();
}
}
}
使用我这里的代码,在执行foreach时,所有内容都将加载到内存中。我可以通过将IQueryable返回给PersonUsingClass来实现流化,但这会将EF模型暴露给该类,这是一个不需要的场景
是否真的不可能同时隐藏EF模型和流式数据?或者有什么我不知道的吗?您创建的方法会迭代EF创建的
IQueryable
对象
IQueryable
变量已延迟执行,因此在内部,EF仅在迭代IQueryable
时(即首次调用.MoveNext()
时)才会调用数据库
此外,如果您曾经使用SqlDataReader
手动滚动数据库调用,您将看到可以逐个.Read()
查询结果,您不需要将所有记录加载到内存中。EF可能以这种方式传输记录(这是我的假设,可能取决于您的特定EF设置)
您的方法正在返回一个IEnumerable
对象,该对象也会受到延迟执行的影响。通过调用GetPeople()
创建此实例不会导致数据库调用
当方法的结果在上迭代时,您将触发对内部IQueryable
对象的迭代,并逐个转换结果
因此:
在该方法中没有记录被加载到内存中(除非EF在内部进行缓存)。如果您迭代该方法的结果,那么您将逐个迭代每个记录
如果对该方法的结果调用
.ToList()
或.ToArray()
,则记录将加载到内存中。实体框架查询用于缓冲,并可通过关联扩展方法进行流式处理。但是流式传输一直是默认的,扩展方法仍然存在,但现在已经过时了(在EF6中)。这是一个
但别忘了英孚的变化跟踪系统。默认情况下,EF在其变更跟踪程序(即身份缓存)中缓存其具体化的所有实体。因此,即使查询是流式的,为了防止内存消耗,也必须防止EF跟踪实体。这正是您的代码中缺少的内容
foreach
循环的每次迭代都会将一个Person
实例附加到变更跟踪器
可以通过两种方式防止缓存实体
只需使用db.Person.AsNoTracking()
立即计划。投影创建EF不跟踪的类型的对象
第二种方法如下所示:
var people = db.Person.Where(someCriteria).Select(p => p.ToDomainPerson());
当然,ToDomainPerson()
不能翻译成SQL。相反,您应该做如下操作:
db.Person.Where(someCriteria)。选择(p=>newdomainPerson
{
名称=p.名称,
...
}
);
或者,更好的方法是使用这个方法,它可以让您的代码像这个ToDomainPerson
方法一样保持干燥
立即投影的优点是,您只需从数据库中提取所需的字段,之后不会触发延迟加载。延迟加载可能是序列化问题或异常的来源,因为触发延迟加载时会释放上下文。在该方法中没有记录加载到内存中(除非EF在内部进行缓存)。如果您对该方法的结果进行foreach
,那么您将逐个迭代每个记录。如果对该方法的结果调用.ToList()、ToArray()
,则记录将加载到内存中。太棒了!一位团队成员让我确信我错了,因为我们可以用这种方式传输数据。您可以在EF online的文档中找到这样的信息:“LINQ查询总是在迭代查询变量时执行,而不是在创建查询变量时执行”。因此,最后我们计算出,一旦foreach被命中,所有内容都将被加载到内存中,然后产生的返回将不会产生任何有意义的影响,除了推迟映射“到域内人”。请创建您的评论作为答案,然后我可以接受。除非EF在内部进行一些缓存——好吧,它是这样做的,并且有两种方法可以防止这种情况发生(参见另一个答案)。@GertArnold您完全正确。我对这个问题的解释导致了我对迭代方面的关注。我把EF缓存这个主题留得很模糊,因为有很多变量在起作用,这个主题甚至可能需要一个额外的问题。例如,可以在上下文级别禁用跟踪,您不必使用.AsNoTracking()
。但是,只能在ef core中在上下文级别禁用跟踪。