NHibernate Fluent (QueryOver) 使用相关子查询替换 HQL

发布于 2024-10-25 04:29:54 字数 2327 浏览 0 评论 0原文

背景,使用 FluentNHibernate,最新的开发版本与 NHibernate 3.0 一起使用。

以下是 WorkIncident 的类型声明:

// Enumeration used in class below.
public enum TicketStatus
{
    Open = 1,
    Closed = 10,
    Hold = 20
}

// Ticket class.
public class WorkIncident
{
    public virtual int EntryId { get; set; }
    public virtual int TicketNumber { get; set; }
    public virtual string ModifierNtId { get; set; }
    public virtual DateTime ModifiedDate { get; set; }
    public virtual TicketStatus Status { get; set; }
    public virtual int Version { get; set; }
    public virtual string Title { get; set; }
    public virtual string Details { get; set; }
}

// FluentNHibernate mapping
public class WorkIncidentMap : ClassMap<WorkIncident>
{
    public WorkIncidentMap()
    {
        Table("incident_details");
        Id( wi => wi.EntryId, "wiid");
        Map(wi => wi.TicketNumber, "workitem_number");
        Map(wi => wi.Title, "workitem_title");
        Map(wi => wi.Details, "workitem_comment");
        Map(wi => wi.ModifiedDate, "workitem_modified_on");
        Map(wi => wi.ModifierNtId, "modified_by_worker_nt_id");
        Map(wi => wi.Status, "workitem_status_lookup_id").CustomType<EnumType<Status>>();
        Map(wi => wi.Version, "workitem_version");
    }
}

映射工作正常,我可以毫无问题地执行如下查询:

session.QueryOver<AltirisIncident>()
    .Where(ai => ai.ModifierNtId == worker.Name.Replace("\\", @"\"))
    .AndRestrictionOn(ai => ai.ModifiedDate)
    .IsBetween(DateTime.Today)
    .And(DateTime.Today.AddDays(1))
    .List<WorkIncident>();

这为我提供了当前日期特定用户接触的所有工作项目(基本上是帮助台故障单)。

但是,我在将以下 HQL 翻译为流畅的声明时遇到了麻烦:

from    WorkIncident as t1
where   t1.ModifierNtId = :ntid
and     t1.ModifiedDate between :startdate and :enddate
and     t1.Status = :status
and     (t1.Version = 1
or      t1.TicketNumber in (
    select    t2.TicketNumber
    from      WorkIncident as t2
    where     t2.Status != t1.Status
    and       t2.TicketNumber = t1.TicketNumber
    and       t2.Version = t1.Version - 1))

此查询为我提供了由工作人员置于关闭状态的所有工作项的列表。考虑到工单在数据库中的存储方式(每张工单都有多个记录(每次更新一条),并且主管通常会在工作人员关闭工单后向工单添加注释,导致我无法只查看工单的情况具有关闭状态的最后一个版本号可以可靠地告诉我谁关闭了票证,

我们将不胜感激,因为我希望尽可能远离 HQL 和魔术字符串。

Background, using FluentNHibernate, lastest dev build working with NHibernate 3.0.

Here is the type declarations for WorkIncident:

// Enumeration used in class below.
public enum TicketStatus
{
    Open = 1,
    Closed = 10,
    Hold = 20
}

// Ticket class.
public class WorkIncident
{
    public virtual int EntryId { get; set; }
    public virtual int TicketNumber { get; set; }
    public virtual string ModifierNtId { get; set; }
    public virtual DateTime ModifiedDate { get; set; }
    public virtual TicketStatus Status { get; set; }
    public virtual int Version { get; set; }
    public virtual string Title { get; set; }
    public virtual string Details { get; set; }
}

// FluentNHibernate mapping
public class WorkIncidentMap : ClassMap<WorkIncident>
{
    public WorkIncidentMap()
    {
        Table("incident_details");
        Id( wi => wi.EntryId, "wiid");
        Map(wi => wi.TicketNumber, "workitem_number");
        Map(wi => wi.Title, "workitem_title");
        Map(wi => wi.Details, "workitem_comment");
        Map(wi => wi.ModifiedDate, "workitem_modified_on");
        Map(wi => wi.ModifierNtId, "modified_by_worker_nt_id");
        Map(wi => wi.Status, "workitem_status_lookup_id").CustomType<EnumType<Status>>();
        Map(wi => wi.Version, "workitem_version");
    }
}

The mapping works fine, and I can do queries like the following with no problems:

session.QueryOver<AltirisIncident>()
    .Where(ai => ai.ModifierNtId == worker.Name.Replace("\\", @"\"))
    .AndRestrictionOn(ai => ai.ModifiedDate)
    .IsBetween(DateTime.Today)
    .And(DateTime.Today.AddDays(1))
    .List<WorkIncident>();

This gives me all of the work items (basically help desk trouble tickets) touched by a specific user on the current date.

However, I have been having trouble translating the following HQL into a fluent declaration:

from    WorkIncident as t1
where   t1.ModifierNtId = :ntid
and     t1.ModifiedDate between :startdate and :enddate
and     t1.Status = :status
and     (t1.Version = 1
or      t1.TicketNumber in (
    select    t2.TicketNumber
    from      WorkIncident as t2
    where     t2.Status != t1.Status
    and       t2.TicketNumber = t1.TicketNumber
    and       t2.Version = t1.Version - 1))

This query gives me the list of all work items that were placed in a closed status by a worker. Given the way the tickets are stored in the database (each ticket has multiple records (one for each update) and supervisors will often add notes to a ticket after a worker has closed it, leads to situations where I can't just look at the last version number with a closed status to reliably tell me who closed a ticket.

Any help would be greatly appreciated, as I would prefer to move away from HQL and magic strings as much as possible.

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

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

发布评论

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

评论(1

无人问我粥可暖 2024-11-01 04:29:54

我认为这应该可以解决问题。困难的部分实际上是处理那里的数学运算。你必须进入 SQLFunction 投影

session.QueryOver<WorkIncident>(() => t1Alias)
                .Where(w => w.ModifierNtId == "test")
                .And(w => w.ModifiedDate < DateTime.Now && w.ModifiedDate > DateTime.Now)
                .And(w => w.Status == TicketStatus.Open)
                .And(Restrictions.Disjunction()
                    .Add(Restrictions.Where<WorkIncident>(w => w.TicketNumber == 1))
                    .Add(Subqueries.WhereProperty(() => t1Alias.TicketNumber).In(
                        QueryOver.Of<WorkIncident>(() => t2Alias)
                                .Where(() => t2Alias.Status != t1Alias.Status)
                                .And(() => t2Alias.TicketNumber == t1Alias.TicketNumber)
                                .And(Restrictions.EqProperty(
                                    Projections.Property<WorkIncident>(w=> w.Version), 
                                    Projections.SqlFunction(
                                        new VarArgsSQLFunction("(","-",")"),
                                        NHibernateUtil.Int32,
                                        Projections.Property(()=> t1Alias.Version),
                                        Projections.Constant(1)
                                    )))
                                .Select(w => w.TicketNumber)))
                ).List();

在我的测试中,这生成了以下 SQL

 SELECT <snip...>
 FROM incident_details this_ 
 WHERE this_.modified_by_worker_nt_id = @p0 
    and (this_.workitem_modified_on < @p1 and this_.workitem_modified_on > @p2) 
    and this_.workitem_status_lookup_id = @p3 
    and (this_.workitem_number = @p4 
    or this_.workitem_number in 
        (SELECT this_0_.workitem_number as y0_ 
         FROM incident_details this_0_ 
         WHERE not (this_0_.workitem_status_lookup_id = this_.workitem_status_lookup_id) 
         and this_0_.workitem_number = this_.workitem_number 
         and this_0_.workitem_version = (this_.workitem_version-@p5)));
         @p0 = 'test' [Type: String (0)], @p1 = 10/26/2012 11:26:24 PM [Type: DateTime (0)], @p2 = 10/26/2012 11:26:24 PM [Type: DateTime (0)], @p3 = 1 [Type: Int32 (0)], @p4 = 1 [Type: Int32 (0)], @p5 = 1 [Type: Int32 (0)]

I think this should do the trick. The tough part is really handling that mathematical operation you've got there. You have to get into SQLFunction projections

session.QueryOver<WorkIncident>(() => t1Alias)
                .Where(w => w.ModifierNtId == "test")
                .And(w => w.ModifiedDate < DateTime.Now && w.ModifiedDate > DateTime.Now)
                .And(w => w.Status == TicketStatus.Open)
                .And(Restrictions.Disjunction()
                    .Add(Restrictions.Where<WorkIncident>(w => w.TicketNumber == 1))
                    .Add(Subqueries.WhereProperty(() => t1Alias.TicketNumber).In(
                        QueryOver.Of<WorkIncident>(() => t2Alias)
                                .Where(() => t2Alias.Status != t1Alias.Status)
                                .And(() => t2Alias.TicketNumber == t1Alias.TicketNumber)
                                .And(Restrictions.EqProperty(
                                    Projections.Property<WorkIncident>(w=> w.Version), 
                                    Projections.SqlFunction(
                                        new VarArgsSQLFunction("(","-",")"),
                                        NHibernateUtil.Int32,
                                        Projections.Property(()=> t1Alias.Version),
                                        Projections.Constant(1)
                                    )))
                                .Select(w => w.TicketNumber)))
                ).List();

In my test, this generated the following SQL

 SELECT <snip...>
 FROM incident_details this_ 
 WHERE this_.modified_by_worker_nt_id = @p0 
    and (this_.workitem_modified_on < @p1 and this_.workitem_modified_on > @p2) 
    and this_.workitem_status_lookup_id = @p3 
    and (this_.workitem_number = @p4 
    or this_.workitem_number in 
        (SELECT this_0_.workitem_number as y0_ 
         FROM incident_details this_0_ 
         WHERE not (this_0_.workitem_status_lookup_id = this_.workitem_status_lookup_id) 
         and this_0_.workitem_number = this_.workitem_number 
         and this_0_.workitem_version = (this_.workitem_version-@p5)));
         @p0 = 'test' [Type: String (0)], @p1 = 10/26/2012 11:26:24 PM [Type: DateTime (0)], @p2 = 10/26/2012 11:26:24 PM [Type: DateTime (0)], @p3 = 1 [Type: Int32 (0)], @p4 = 1 [Type: Int32 (0)], @p5 = 1 [Type: Int32 (0)]
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文