Come fai a confrontare utilizzando tipi .NET in una query NHibernate ICriteria per un ICompositeUserType?

StackOverflow https://stackoverflow.com/questions/2649099

Domanda

Ho una domanda StackOverflow risposto su come combinare a data e ora di database legacy campi CHAR in proprietà DateTime uno NET nella mia POCO qui (grazie molto Berryl !). Ora sto cercando di ottenere una query ICritera personalizzata per il lavoro contro quella proprietà molto DateTime inutilmente. ecco la mia domanda:

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>();

Ed ecco la domanda sta generando:

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'

La query è esattamente quello che mi aspetterei, in realtà, tranne che in realtà non mi dà quello che voglio (tutte le righe negli ultimi 2 settimane) perché nel DB sta facendo un maggiore confronto con CHARs invece di DATEs . Non ho idea di come raggiungere la query per convertire i valori CHAR in una DATE nella query senza fare un createSQLQuery (), , che vorrei evitare di . Qualcuno sa come fare questo?

UPDATE: Ho cercato per cercare di utilizzare Projections.SqlFunction() o formule per raggiungere questo obiettivo, ma senza alcun risultato finora. Ecco il codice che ho con SqlFunction(), ma ottengo un errore NHibernate.QueryException : property does not map to a single column: 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));

Sono sicuro che sto facendo qualcosa di sbagliato qui e non lo fa come se fosse ancora comunque perché FileCreationDateTime utilizza un ICompositeUserType personalizzato che divide la proprietà .NET DateTime in due colonne CHAR Oracle SQL (si veda questo StackOverflow question per i dettagli).

È stato utile?

Soluzione

Alla fine ho questo numero! ecco il codice (per qualche motivo StackOverflow sta facendo alcuni dei nomi metodi nelle questo primo frammento di codice il colore della sintassi di un tipo):

    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>();

Ed ecco i metodi che ho usato per creare il SQLProjections e SQLFunctions. Li ho messi nel mio ICompositeUserType (DefaultStringFileCreationDateTime), che ho usato per la mappatura tipo personalizzato sulla proprietà 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);
    }
}

ero già usando il consts DatabaseFieldNames struct per l'attuazione membro PropertyNames, quindi ero in grado di riutilizzare questi i nomi delle colonne hardcoded per il Projections mi serviva pure.

Ecco la classe di utilità Projection in cui i metodi to_date generici vivono:

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));
    }
}

Infine, ecco l'Oracle SQL che NHibernate genera:

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'

Non posso credere che ho ottenuto questo uno! ho pensato che stavo per avere a ricorrere ad un ISQLQuery di sicuro!

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top