Entity framework core 实体框架-使用派生类型方法有条件地查询相关实体

Entity framework core 实体框架-使用派生类型方法有条件地查询相关实体,entity-framework-core,Entity Framework Core,我有三节课: 公共抽象类对话 { 公共int Id{get;set;} 公共抽象boolhasuser(字符串userId); } 公共课公共对话:对话 { 公共覆盖bool HasUser(字符串userId) { 返回true; } } 公共类私有会话:会话 { 公共ICollection用户{get;set;} 公共覆盖bool HasUser(字符串userId) { 返回Users.Any(t=>t.UserId==UserId); } } 在DbContext中,有以下数据库集:

我有三节课:

公共抽象类对话
{
公共int Id{get;set;}
公共抽象boolhasuser(字符串userId);
}
公共课公共对话:对话
{
公共覆盖bool HasUser(字符串userId)
{
返回true;
}
}
公共类私有会话:会话
{
公共ICollection用户{get;set;}
公共覆盖bool HasUser(字符串userId)
{
返回Users.Any(t=>t.UserId==UserId);
}
}
在DbContext中,有以下数据库集:

公共数据库集会话{get;set;}
公共DbSet PrivateConversations{get;set;}
public DbSet PublicConversations{get;set;}
公共数据库集用户{get;set;}
它生成两个表:
对话
用户

PrivateConversations和PublicConversations保存在表
Conversations

现在,查询发生错误:


//由于某些原因,我只能查询'db.Conversations',无法访问'db.PrivateConversations'`
var conversations=db.conversations
.Include(t=>(t作为PrivateConversation).Users)
.Where(t=>t.HasUser(“某物”))
.ToList();
对于所有的
PublicConversation
,一切正常

告诉我在
PrivateConversation
方法中:
HasUser()
Users.Any()
Users
为空

我很困惑。如何查询与it中用户的所有私人对话

其他信息: 我使用的软件包:


运行时:.NET Core 2.2

Exe
netcoreapp2.2

我复制了您的设置,并得到了相同的错误。如果给PrivateConversation类一个这样的构造函数

public PrivateConversation()
{
    Users = new Collection<User>();
}
var conversations = db.Conversations
                     .Include(a => a.Users)
                     .Where(a => a.Users.Any(t => t.UserId == "something"))
                     .ToList();
正如您所见,这并不理想,因为查询的最后一位是在内存中完成的,而不是在数据库中完成的

注意:我检查了如果不涉及继承,查询是否可以工作。在本例中,查询不能与HasUser方法一起使用。只有当我提出这样的问题时,它才起作用

public PrivateConversation()
{
    Users = new Collection<User>();
}
var conversations = db.Conversations
                     .Include(a => a.Users)
                     .Where(a => a.Users.Any(t => t.UserId == "something"))
                     .ToList();

您不应该在where子句中添加派生类型吗

    var conversations = db.Conversations
      .Include(t => (t as PrivateConversation).Users)
      .Where(t => t is PrivateConversation && ((PrivateConversation) t).HasUser("something"))
      .ToList();
表情

t.HasUser(“某物”)
无法转换为SQL,因为它需要
Conversation
对象并调用
HasUser
方法。只有不需要物化对象的众所周知的方法才能转换为SQL

查询转换器以不同的方式处理不可翻译的表达式。EF6抛出异常。同样的情况也会发生。但是您似乎正在使用的efcore1.x/2.x试图计算这些表达式

问题是,如果条件使用实体原语属性,但导航属性失败,因为在客户端评估时,它们还没有加载,即使稍后将使用
Include
加载它们。您可以通过初始化集合导航属性或添加
null
检查来避免NRE,但无论哪种情况,结果都不正确

这一点,加上隐藏的低效率,是在3.0中删除隐式客户端评估的原因之一

解决此问题有两个选项:

(1) 保留封装并使用显式客户端评估。显式客户端计算意味着您在查询中的某个点显式插入
AsEnumerable()
。之前的所有内容都将由EF Core执行,之后的所有内容都将由LINQ to Entities(EF Core)查询的完全物化结果上的LINQ to对象执行

var conversations=db.conversations
.Include(t=>(t作为PrivateConversation).Users)
.AsEnumerable()//t.HasUser(“某物”))
.ToList();
(2) 打破封装并使用可翻译的结构在原地重新创建表达式。这样,过滤将在服务器端进行:

var conversations=db.conversations
.Include(t=>(t作为PrivateConversation).Users)
.其中(t=>t是PrivateConversation?
((PrivateConversation)t).Users.Any(u=>u.Name==“某物”):
(对)
.ToList();
i、 e.代替

t.HasUser(“某物”)
你会用

t是私人谈话吗?
((PrivateConversation)t).Users.Any(u=>u.Name==“某物”):
真的
或同等品

!(t是私人谈话)
||((PrivateConversation)t).Users.Any(u=>u.Name==“某物”)
它从
HasUser
方法的派生类重写中提取内联逻辑

请注意,EF Core(或任何其他库)无法“查看”类似于C#编译器的方法的实现,因为它没有源代码(除非它尝试反编译编译后的代码,这不是一项简单的任务)


这两种解决方案各有利弊。(1) 从OOP的角度来看,它更好,但效率低,因为加载的数据(和相关数据)可能比它需要的多得多。(2) 相反-性能/内存使用更好,OOP更差(破坏封装,需要更新,以防新的派生类实现有问题的方法等),所以请使用更适合您需要的方法。

您能分享刚才告诉我源为空的错误消息吗?谢谢您的建议。然而,在这个应用程序中,我不能像你那样写。让我们等待其他答案。无论如何谢谢你!不客气。是的,我同意。这是一个有趣的问题,如果能做到这一点,我会很高兴。这对我来说不起作用。查询运行时没有错误。但当我期望得到1个结果时,也给出了0个结果,即来自包含id为“某物”的用户的对话的结果。(我的测试数据与这样一个用户进行了对话)@DaveBarnett抱歉,您可以再试一次,但使用安全转换而不是直接转换
。其中(t=>t是PrivateConversation&(t是PrivateConversation)
。我记得有一次