如何比较在 ICompositeUserType 的 NHibernate ICriteria 查询中使用 .NET 类型?

发布于 2024-08-28 16:55:36 字数 3340 浏览 6 评论 0原文

我有一个已回答的 StackOverflow 问题,涉及如何将旧版 CHAR 数据库日期和时间字段合并到 POCO 中的一个 .NET DateTime 属性中 这里(非常感谢Berryl!)。现在我试图获取一个自定义 ICritera 查询来针对该 DateTime 属性进行操作,但无济于事。这是我的查询:

ICriteria criteria =
    Session.CreateCriteria<InputFileLog>()
    .Add(Expression.Gt(MembersOf<InputFileLog>.GetName(x => x.FileCreationDateTime), DateTime.Now.AddDays(-14)))
    .AddOrder(Order.Desc(Projections.Id()))
    .CreateCriteria(typeof(InputFile).Name)
        .Add(Expression.Eq(MembersOf<InputFile>.GetName(x => x.Id), inputFileName));

IList<InputFileLog> list = criteria.List<InputFileLog>();

这是它生成的查询:

SELECT this_.input_file_token as input1_9_2_,
    this_.file_creation_date as file2_9_2_,
    this_.file_creation_time as file3_9_2_,
    this_.approval_ind as approval4_9_2_,
    this_.file_id as file5_9_2_,
    this_.process_name as process6_9_2_,
    this_.process_status as process7_9_2_,
    this_.input_file_name as input8_9_2_,
    gonogo3_.input_file_token as input1_6_0_,
    gonogo3_.go_nogo_ind as go2_6_0_,
    inputfile1_.input_file_name as input1_3_1_,
    inputfile1_.src_code as src2_3_1_,
    inputfile1_.process_cat_code as process3_3_1_
FROM input_file_log this_
    left outer join go_nogo gonogo3_ on this_.input_file_token=gonogo3_.input_file_token
    inner join input_file inputfile1_ on this_.input_file_name=inputfile1_.input_file_name
WHERE this_.file_creation_date > :p0 and
    this_.file_creation_time > :p1 and
    inputfile1_.input_file_name = :p2
ORDER BY this_.input_file_token desc;
:p0 = '20100401',
:p1 = '15:15:27',
:p2 = 'LMCONV_JR'

实际上,该查询正是我所期望的,只是它实际上并没有给我想要的东西(过去两周的所有行),因为在数据库中它正在执行大于使用 CHAR 而不是 DATE 的比较。我不知道如何让查询将 CHAR 值转换为查询中的 DATE 而不执行 CreateSQLQuery(),这是我想避免的。有人知道该怎么做吗?

更新: 我一直在尝试使用 Projections.SqlFunction() 或公式来完成此任务,但到目前为止没有任何效果。这是我使用 SqlFunction() 的代码,但我得到一个 NHibernate.QueryException : property does not map to a single columns: FileCreationDateTime 错误:

DateTime twoWeeksAgo = DateTime.Now.AddDays(-14);
ICriteria criteria =
    Session.CreateCriteria<InputFileLog>()
    .Add(Restrictions.Gt(Projections.SqlFunction("to_date", NHibernateUtil.DateTime, Projections.Property(MembersOf<InputFileLog>.GetName(x => x.FileCreationDateTime))), twoWeeksAgo))
    //.Add(Expression.Gt(MembersOf<InputFileLog>.GetName(x => x.FileCreationDateTime), DateTime.Now.AddDays(-14)))
    .AddOrder(Order.Desc(Projections.Id()))
     .CreateCriteria(typeof(InputFile).Name)
        .Add(Expression.Eq(MembersOf<InputFile>.GetName(x => x.Id), inputFileName));

我确信我'我在这里做了一些错误的事情,但它仍然不喜欢它,因为 FileCreationDateTime 使用自定义 ICompositeUserType 它将 .NET DateTime 属性分成两个Oracle SQL CHAR 列(请参阅此 StackOverflow 问题了解详细信息)。

I have an answered StackOverflow question about how to combine to legacy CHAR database date and time fields into one .NET DateTime property in my POCO
here (thanks much Berryl!). Now i am trying to get a custom ICritera query to work against that very DateTime property to no avail. here's my query:

ICriteria criteria =
    Session.CreateCriteria<InputFileLog>()
    .Add(Expression.Gt(MembersOf<InputFileLog>.GetName(x => x.FileCreationDateTime), DateTime.Now.AddDays(-14)))
    .AddOrder(Order.Desc(Projections.Id()))
    .CreateCriteria(typeof(InputFile).Name)
        .Add(Expression.Eq(MembersOf<InputFile>.GetName(x => x.Id), inputFileName));

IList<InputFileLog> list = criteria.List<InputFileLog>();

And here's the query it's generating:

SELECT this_.input_file_token as input1_9_2_,
    this_.file_creation_date as file2_9_2_,
    this_.file_creation_time as file3_9_2_,
    this_.approval_ind as approval4_9_2_,
    this_.file_id as file5_9_2_,
    this_.process_name as process6_9_2_,
    this_.process_status as process7_9_2_,
    this_.input_file_name as input8_9_2_,
    gonogo3_.input_file_token as input1_6_0_,
    gonogo3_.go_nogo_ind as go2_6_0_,
    inputfile1_.input_file_name as input1_3_1_,
    inputfile1_.src_code as src2_3_1_,
    inputfile1_.process_cat_code as process3_3_1_
FROM input_file_log this_
    left outer join go_nogo gonogo3_ on this_.input_file_token=gonogo3_.input_file_token
    inner join input_file inputfile1_ on this_.input_file_name=inputfile1_.input_file_name
WHERE this_.file_creation_date > :p0 and
    this_.file_creation_time > :p1 and
    inputfile1_.input_file_name = :p2
ORDER BY this_.input_file_token desc;
:p0 = '20100401',
:p1 = '15:15:27',
:p2 = 'LMCONV_JR'

The query is exactly what i would expect, actually, except it doesn't actually give me what i want (all the rows in the last 2 weeks) because in the DB it's doing a greater than comparison using CHARs instead of DATEs. I have no idea how to get the query to convert the CHAR values into a DATE in the query without doing a CreateSQLQuery(), which I would like to avoid. Anyone know how to do this?

UPDATE:
I've been looking into trying to use Projections.SqlFunction() or formulas to accomplish this, but to no avail so far. Here's the code i have using SqlFunction(), but i get an NHibernate.QueryException : property does not map to a single column: FileCreationDateTime error:

DateTime twoWeeksAgo = DateTime.Now.AddDays(-14);
ICriteria criteria =
    Session.CreateCriteria<InputFileLog>()
    .Add(Restrictions.Gt(Projections.SqlFunction("to_date", NHibernateUtil.DateTime, Projections.Property(MembersOf<InputFileLog>.GetName(x => x.FileCreationDateTime))), twoWeeksAgo))
    //.Add(Expression.Gt(MembersOf<InputFileLog>.GetName(x => x.FileCreationDateTime), DateTime.Now.AddDays(-14)))
    .AddOrder(Order.Desc(Projections.Id()))
     .CreateCriteria(typeof(InputFile).Name)
        .Add(Expression.Eq(MembersOf<InputFile>.GetName(x => x.Id), inputFileName));

I'm sure i'm doing something wrong here and it doesn't like it still anyway because FileCreationDateTime uses a custom ICompositeUserType which splits the .NET DateTime property into two Oracle SQL CHAR columns (see this StackOverflow question for details).

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

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

发布评论

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

评论(1

十六岁半 2024-09-04 16:55:37

我终于弄清楚了!这是代码(出于某种原因,StackOverflow 将第一个代码片段中的一些方法名称设置为类型的语法颜色):

    IList<InputFileLog> list = null;
    DateTime twoWeeksAgo = DateTime.Now.AddDays(-14);

    IProjection datePropProj =
        DefaultStringFileCreationDateTimeType.GetFileCreationDateToDateSQLProjection();
    IProjection timePropProj =
        DefaultStringFileCreationDateTimeType.GetFileCreationTimeToDateSQLProjection();

    IProjection dateConstProj =
        DefaultStringFileCreationDateTimeType.GetFileCreationDateToDateSQLFunction(twoWeeksAgo);
    IProjection timeConstProj =
        DefaultStringFileCreationDateTimeType.GetFileCreationTimeToDateSQLFunction(twoWeeksAgo);

    ICriteria criteria =
        Session.CreateCriteria<InputFileLog>()
        .Add(Restrictions.Or(Restrictions.GtProperty(datePropProj, dateConstProj),
            Restrictions.And(Restrictions.EqProperty(datePropProj, dateConstProj),
                Restrictions.GeProperty(timePropProj, timeConstProj))))
        .AddOrder(Order.Desc(Projections.Id()))
        .CreateCriteria(typeof(InputFile).Name)
            .Add(Expression.Eq(MembersOf<InputFile>.GetName(x => x.Id), inputFileName));

    list = criteria.List<InputFileLog>();

这是我用来创建 SQLProjections的方法SQLFunctions。我将它们放入我的 ICompositeUserType (DefaultStringFileCreationDateTime) 中,用于 FileCreationDateTime 属性上的自定义类型映射。

public class DefaultStringFileCreationDateTime : ICompositeUserType
{
    .
    .
    .
    public const string DotNetDateFormat = "yyyyMMdd";

    public const string DotNetTimeFormat = "HH:mm:ss";

    public const string DbDateFormat = "YYYYMMDD";

    public const string DbTimeFormat = "HH24:MI:SS";

    private const string _nullDateRepresentationInDb = "00000000";

    public struct DatabaseFieldNames
    {
        /// <summary>
        /// File creation date column name.
        /// </summary>
        public const string FileCreationDate = "file_creation_date";

        /// <summary>
        /// File creation time column name.
        /// </summary>
        public const string FileCreationTime = "file_creation_time";
    }

    public static IProjection GetFileCreationDateToDateSQLProjection()
    {
        return ProjectionUtil.GetToDateSQLProjection(DatabaseFieldNames.FileCreationDate, DbDateFormat, NHibernateUtil.DateTime);
    }

    public static IProjection GetFileCreationTimeToDateSQLProjection()
    {
        return ProjectionUtil.GetToDateSQLProjection(DatabaseFieldNames.FileCreationTime, DbTimeFormat, NHibernateUtil.DateTime);
    }

    public static IProjection GetFileCreationDateToDateSQLFunction(DateTime dt)
    {
        return ProjectionUtil.GetToDateSQLFunction(dt, DotNetDateFormat, DbDateFormat);
    }

    public static IProjection GetFileCreationTimeToDateSQLFunction(DateTime dt)
    {
        return ProjectionUtil.GetToDateSQLFunction(dt, DotNetTimeFormat, DbTimeFormat);
    }
}

我已经在使用 consts DatabaseFieldNames struct 来实现 PropertyNames 成员,因此我能够重用这些硬体我也需要的 Projections 的编码列名称。

这是 Projection 实用程序类,其中包含通用 to_date 方法:

public class ProjectionUtil
{
    public static IProjection GetToDateSQLProjection(
        string columnName, string dbToDateFormat, IType returnType)
    {
        return Projections.SqlProjection(
            string.Format("to_date({0}, '{1}') as {0}", columnName, dbToDateFormat),
            new string[] { columnName },
            new IType[] { returnType });
    }

    public static IProjection GetToDateSQLFunction(
        DateTime dt, string dotNetFormatString, string dbFormatString)
    {
        return Projections.SqlFunction(
            "to_date",
            NHibernateUtil.DateTime,
            Projections.Constant(dt.ToString(dotNetFormatString)),
            Projections.Constant(dbFormatString));
    }
}

最后,这是 NHibernate 生成的 Oracle SQL:

SELECT
    this_.input_file_token as input1_9_2_,
    this_.file_creation_date as file2_9_2_,
    this_.file_creation_time as file3_9_2_,
    this_.approval_ind as approval4_9_2_,
    this_.file_id as file5_9_2_,
    this_.process_name as process6_9_2_,
    this_.process_status as process7_9_2_,
    this_.input_file_name as input8_9_2_,
    gonogo3_.input_file_token as input1_6_0_,
    gonogo3_.go_nogo_ind as go2_6_0_,
    inputfile1_.input_file_name as input1_3_1_,
    inputfile1_.src_code as src2_3_1_,
    inputfile1_.process_cat_code as process3_3_1_
FROM
    input_file_log this_
    left outer join go_nogo gonogo3_ on this_.input_file_token=gonogo3_.input_file_token
    inner join input_file inputfile1_ on this_.input_file_name=inputfile1_.input_file_name
WHERE
    (
        to_date(file_creation_date, 'YYYYMMDD') > to_date(:p0, :p1) or 
        (
            to_date(file_creation_date, 'YYYYMMDD') = to_date(:p2, :p3) and
            to_date(file_creation_time, 'HH24:MI:SS') >= to_date(:p4, :p5)
        )
    ) and
    inputfile1_.input_file_name = :p6
ORDER BY this_.input_file_token desc;
:p0 = '20100415',
:p1 = 'YYYYMMDD',
:p2 = '20100415',
:p3 = 'YYYYMMDD',
:p4 = '18:48:48',
:p5 = 'HH24:MI:SS',
:p6 = 'LMCONV_JR'

不敢相信我得到了这个!我想我肯定必须求助于 ISQLQuery

I finally figure this out! here's the code (for some reason StackOverflow is making some of the methods names in the this first code snippet the syntax color of a type):

    IList<InputFileLog> list = null;
    DateTime twoWeeksAgo = DateTime.Now.AddDays(-14);

    IProjection datePropProj =
        DefaultStringFileCreationDateTimeType.GetFileCreationDateToDateSQLProjection();
    IProjection timePropProj =
        DefaultStringFileCreationDateTimeType.GetFileCreationTimeToDateSQLProjection();

    IProjection dateConstProj =
        DefaultStringFileCreationDateTimeType.GetFileCreationDateToDateSQLFunction(twoWeeksAgo);
    IProjection timeConstProj =
        DefaultStringFileCreationDateTimeType.GetFileCreationTimeToDateSQLFunction(twoWeeksAgo);

    ICriteria criteria =
        Session.CreateCriteria<InputFileLog>()
        .Add(Restrictions.Or(Restrictions.GtProperty(datePropProj, dateConstProj),
            Restrictions.And(Restrictions.EqProperty(datePropProj, dateConstProj),
                Restrictions.GeProperty(timePropProj, timeConstProj))))
        .AddOrder(Order.Desc(Projections.Id()))
        .CreateCriteria(typeof(InputFile).Name)
            .Add(Expression.Eq(MembersOf<InputFile>.GetName(x => x.Id), inputFileName));

    list = criteria.List<InputFileLog>();

And here's the methods i used to create the SQLProjections and SQLFunctions. i put them in my ICompositeUserType (DefaultStringFileCreationDateTime) that i used for the custom type mapping on the FileCreationDateTime property.

public class DefaultStringFileCreationDateTime : ICompositeUserType
{
    .
    .
    .
    public const string DotNetDateFormat = "yyyyMMdd";

    public const string DotNetTimeFormat = "HH:mm:ss";

    public const string DbDateFormat = "YYYYMMDD";

    public const string DbTimeFormat = "HH24:MI:SS";

    private const string _nullDateRepresentationInDb = "00000000";

    public struct DatabaseFieldNames
    {
        /// <summary>
        /// File creation date column name.
        /// </summary>
        public const string FileCreationDate = "file_creation_date";

        /// <summary>
        /// File creation time column name.
        /// </summary>
        public const string FileCreationTime = "file_creation_time";
    }

    public static IProjection GetFileCreationDateToDateSQLProjection()
    {
        return ProjectionUtil.GetToDateSQLProjection(DatabaseFieldNames.FileCreationDate, DbDateFormat, NHibernateUtil.DateTime);
    }

    public static IProjection GetFileCreationTimeToDateSQLProjection()
    {
        return ProjectionUtil.GetToDateSQLProjection(DatabaseFieldNames.FileCreationTime, DbTimeFormat, NHibernateUtil.DateTime);
    }

    public static IProjection GetFileCreationDateToDateSQLFunction(DateTime dt)
    {
        return ProjectionUtil.GetToDateSQLFunction(dt, DotNetDateFormat, DbDateFormat);
    }

    public static IProjection GetFileCreationTimeToDateSQLFunction(DateTime dt)
    {
        return ProjectionUtil.GetToDateSQLFunction(dt, DotNetTimeFormat, DbTimeFormat);
    }
}

I was already using the consts DatabaseFieldNames struct for the PropertyNames member implementation, so I was able to reuse these hard-coded column names for the Projections i needed as well.

Here's the Projection utility class where the generic to_date methods live:

public class ProjectionUtil
{
    public static IProjection GetToDateSQLProjection(
        string columnName, string dbToDateFormat, IType returnType)
    {
        return Projections.SqlProjection(
            string.Format("to_date({0}, '{1}') as {0}", columnName, dbToDateFormat),
            new string[] { columnName },
            new IType[] { returnType });
    }

    public static IProjection GetToDateSQLFunction(
        DateTime dt, string dotNetFormatString, string dbFormatString)
    {
        return Projections.SqlFunction(
            "to_date",
            NHibernateUtil.DateTime,
            Projections.Constant(dt.ToString(dotNetFormatString)),
            Projections.Constant(dbFormatString));
    }
}

Finally, here's the Oracle SQL that NHibernate generates:

SELECT
    this_.input_file_token as input1_9_2_,
    this_.file_creation_date as file2_9_2_,
    this_.file_creation_time as file3_9_2_,
    this_.approval_ind as approval4_9_2_,
    this_.file_id as file5_9_2_,
    this_.process_name as process6_9_2_,
    this_.process_status as process7_9_2_,
    this_.input_file_name as input8_9_2_,
    gonogo3_.input_file_token as input1_6_0_,
    gonogo3_.go_nogo_ind as go2_6_0_,
    inputfile1_.input_file_name as input1_3_1_,
    inputfile1_.src_code as src2_3_1_,
    inputfile1_.process_cat_code as process3_3_1_
FROM
    input_file_log this_
    left outer join go_nogo gonogo3_ on this_.input_file_token=gonogo3_.input_file_token
    inner join input_file inputfile1_ on this_.input_file_name=inputfile1_.input_file_name
WHERE
    (
        to_date(file_creation_date, 'YYYYMMDD') > to_date(:p0, :p1) or 
        (
            to_date(file_creation_date, 'YYYYMMDD') = to_date(:p2, :p3) and
            to_date(file_creation_time, 'HH24:MI:SS') >= to_date(:p4, :p5)
        )
    ) and
    inputfile1_.input_file_name = :p6
ORDER BY this_.input_file_token desc;
:p0 = '20100415',
:p1 = 'YYYYMMDD',
:p2 = '20100415',
:p3 = 'YYYYMMDD',
:p4 = '18:48:48',
:p5 = 'HH24:MI:SS',
:p6 = 'LMCONV_JR'

can't believe i got this one! i thought i was going to have to resort to an ISQLQuery for sure!

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