NHibernate 正在生成带有错误连接的 SQL

发布于 2025-01-06 08:46:43 字数 2000 浏览 3 评论 0原文

我有一个 NHibernate Linq 查询,它没有按照我的预期工作。

问题似乎来自于在 where 子句中使用左连接表中的可空 int 列。这导致连接就像内部连接一样。

var list = this.WorkflowDiaryManager.WorkflowActionRepository.All
    .Fetch(x => x.CaseView)
    .Fetch(x => x.WorkflowActionType)
    .ThenFetchMany(x => x.WorkflowActionPriorityList)
    .Where(x => x.AssignedUser.Id == userId || x.CaseView.MooseUserId == userId)

由此生成的 SQL 看起来像(从连接开始 - 您不需要查看所有选择)

from Kctc.WorkflowAction workflowac0_ 
left outer join Kctc.WorkflowCaseView workflowca1_ on workflowac0_.CaseId=workflowca1_.CaseId 
left outer join Kctc.WorkflowActionType workflowac2_ on workflowac0_.WorkflowActionTypeId=workflowac2_.WorkflowActionTypeId 
left outer join Kctc.WorkflowActionPriority workflowac3_ on workflowac2_.WorkflowActionTypeId=workflowac3_.WorkflowActionTypeId
,Kctc.WorkflowCaseView workflowca4_ 
where workflowac0_.CaseId=workflowca4_.CaseId 
and (workflowac0_.AssignedUser=@p0 or workflowca4_.[MooseUserId]=@p1);
@p0 = 1087 [Type: Int32 (0)],
@p1 = 1087 [Type: Int32 (0)]

因此导致问题的部分是上面代码片段的第 5 行。正如您所看到的,NHibernate 正在尝试在我的 WorkflowCaseView 视图上执行“老式”连接。这会导致查询排除 WorkflowAction 表中没有 CaseId 的其他有效操作。

谁能解释为什么 NHibernate 编写这个 SQL,以及我如何鼓励它生成更好的查询?

谢谢!

WorkflowActionMap 中的重要位

        Table("Kctc.WorkflowAction");
        Id(x => x.Id).GeneratedBy.Identity().Column("WorkflowActionId");
        References(x => x.WorkflowActionType).Column("WorkflowActionTypeId").Unique();
        References(x => x.CompletedBy).Column("CompletedBy");
        References(x => x.CaseView).Column("CaseId").Not.Update().Unique();
        References(x => x.AssignedUser).Column("AssignedUser");

WorkflowCaseViewMap 中的重要位

        Table("Kctc.WorkflowCaseView");
        Id(x => x.Id).Column("CaseId");
        Map(x => x.MooseUserId).Nullable();

看着这个,我想知道我是否应该有一个 HasMany 以其他方式返回...

编辑。似乎没有帮助

I have an NHibernate Linq query which isn't working how I would expect.

The problem seems to come from using a nullable int column from a left joined table in the where clause. This is causing the join to act like an inner join.

var list = this.WorkflowDiaryManager.WorkflowActionRepository.All
    .Fetch(x => x.CaseView)
    .Fetch(x => x.WorkflowActionType)
    .ThenFetchMany(x => x.WorkflowActionPriorityList)
    .Where(x => x.AssignedUser.Id == userId || x.CaseView.MooseUserId == userId)

The SQL produced by this looks like (from the join onwards - you don't need to see all the selects)

from Kctc.WorkflowAction workflowac0_ 
left outer join Kctc.WorkflowCaseView workflowca1_ on workflowac0_.CaseId=workflowca1_.CaseId 
left outer join Kctc.WorkflowActionType workflowac2_ on workflowac0_.WorkflowActionTypeId=workflowac2_.WorkflowActionTypeId 
left outer join Kctc.WorkflowActionPriority workflowac3_ on workflowac2_.WorkflowActionTypeId=workflowac3_.WorkflowActionTypeId
,Kctc.WorkflowCaseView workflowca4_ 
where workflowac0_.CaseId=workflowca4_.CaseId 
and (workflowac0_.AssignedUser=@p0 or workflowca4_.[MooseUserId]=@p1);
@p0 = 1087 [Type: Int32 (0)],
@p1 = 1087 [Type: Int32 (0)]

So the part that is causing the problem is line 5 of the snippet above. As you can see, NHibernate is trying to do an 'old-school' join on my WorkflowCaseView View. This causes the query to exclude otherwise valid actions which do not have a CaseId in the WorkflowAction table.

Could anyone explain why NHibernate is writing this SQL, and how I might encourage it to produce a better query?

Thanks!

Important bits from WorkflowActionMap

        Table("Kctc.WorkflowAction");
        Id(x => x.Id).GeneratedBy.Identity().Column("WorkflowActionId");
        References(x => x.WorkflowActionType).Column("WorkflowActionTypeId").Unique();
        References(x => x.CompletedBy).Column("CompletedBy");
        References(x => x.CaseView).Column("CaseId").Not.Update().Unique();
        References(x => x.AssignedUser).Column("AssignedUser");

Important bits from WorkflowCaseViewMap

        Table("Kctc.WorkflowCaseView");
        Id(x => x.Id).Column("CaseId");
        Map(x => x.MooseUserId).Nullable();

Looking at this, I wonder if I should have a HasMany going back the other way...

EDIT. Doesn't seem to help

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

暮倦 2025-01-13 08:46:43

我认为您需要将 Where 子句更改为:

.Where(x => x.AssignedUser.Id == userId || 
       (x.CaseView != null && x.CaseView.MooseUserId == userId))

使用当前的 Where 子句,您告诉 NHibernate 总会有一个 CaseView,因为您无条件访问其属性。根据此信息,NHibernate 将您的查询从左外连接优化为内连接(即“老式”连接)

I think you need to change your Where clause to this:

.Where(x => x.AssignedUser.Id == userId || 
       (x.CaseView != null && x.CaseView.MooseUserId == userId))

With your current Where clause you tell NHibernate that there always will be a CaseView, because you unconditionally access its properties. Based on this information NHibernate optimizes your query from a left outer join to an inner join (which the "old-school" join is)

百合的盛世恋 2025-01-13 08:46:43

我已经使用存储过程实现了此连接。希望 NHibernate 能够尽快修复这个错误。

I have implemented this join using a stored procedure. Hopefully NHibernate will fix this bug soon.

昵称有卵用 2025-01-13 08:46:43

尝试使用 Fluent NHibernate。类似下面的内容应该让您处于正确的位置:

var List<WorkflowAction> = FluentSessionManager.GetSession().CreateCriteria<WorkflowAction>()
        .SetFetchMode("CaseView", FetchMode.Eager)
        .SetFetchMode("WorkflowActionType", FetchMode.Eager)
        .SetFetchMode("WorkflowActionPriorityList", FetchMode.Eager)
        .CreateAlias("AssignedUser", "au")
        .CreateAlias("CaseView", "cv")
        .Add(Expression.Or(Expression.Eq("au.Id", userId),  Expression.Eq("cv.MooseUserId", userId)))
        .List<WorkflowAction>();

请记住,我有一个扩展 FluentSessionManager.GetSession() 的特殊类,我可以在其中直接使用简单的帮助程序类或逐页调用它。您的 FluentSessionManager 设置可能有很大不同。但最终在“.CreateCriteria()...”你的代码和我的应该匹配。假设“WorkflowAction”是调用查询的表。

Try using Fluent NHibernate. Something like the following should get you in the right ball park:

var List<WorkflowAction> = FluentSessionManager.GetSession().CreateCriteria<WorkflowAction>()
        .SetFetchMode("CaseView", FetchMode.Eager)
        .SetFetchMode("WorkflowActionType", FetchMode.Eager)
        .SetFetchMode("WorkflowActionPriorityList", FetchMode.Eager)
        .CreateAlias("AssignedUser", "au")
        .CreateAlias("CaseView", "cv")
        .Add(Expression.Or(Expression.Eq("au.Id", userId),  Expression.Eq("cv.MooseUserId", userId)))
        .List<WorkflowAction>();

Keep in mind, I have a special class that extends FluentSessionManager.GetSession() where I can call it directly with a simple helper class or on a page by page basis. Your setup of the FluentSessionManager may be considerably different. But ultimately at ".CreateCriteria()..." your code and mine should match. Assuming "WorkflowAction" is the table the query is being called against.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文