简体   繁体   English

NHibernate:拦截和修改查询参数?

[英]NHibernate: intercept and modify query parameters?

tldr: Can I intercept query parameters when a select query is being constructed, and modify them? tldr:我可以在构造 select 查询时拦截查询参数并修改它们吗? I can't find any event listener or interceptor that seems to be able to do this.我找不到任何似乎能够做到这一点的事件侦听器或拦截器。 In my case I'd like to inspect all query parameters before they are bound, looking for specific DateTime query parameters with Kind=Unspecified, and set their Kind to Local.就我而言,我想在绑定之前检查所有查询参数,查找具有 Kind=Unspecified 的特定 DateTime 查询参数,并将它们的 Kind 设置为 Local。

edit: solved with IUserType, see below编辑:用 IUserType 解决,见下文


After a BIG version upgrade (3.4 to 5.3), I ran into a problem querying an entity by a DateTime property mapped as LocalDateTimeType.在 BIG 版本升级(3.4 到 5.3)之后,我遇到了通过映射为 LocalDateTimeType 的 DateTime 属性查询实体的问题。 A simplified example of the entity mapping, in part, and a use case for querying by the DateTime property:实体映射的简化示例,部分和通过 DateTime 属性查询的用例:

public class MyEntity {
    ...
    public virtual DateTime EntityDate { get; set; }
    ...
}
public class MyEntityMap : ClassMap<MyEntity> {
    public MyEntityMap() {
        ...
        Map(x => x.EntityDate).Column("entitydate").Not.Nullable().CustomType<LocalDateTimeType>();
        ...
    }
}
...
public IEnumerable<MyEntity> FindAllBeforeDate(DateTime date) {
    return session.QueryOver<MyEntity>().Where(x => x.EntityDate < date).List();
}

The codebase has lots of other use cases for querying on this property, implemented using QueryOver, Criteria queries, and HQL.代码库有许多其他用于查询此属性的用例,使用 QueryOver、Criteria 查询和 HQL 实现。 In every case, post-upgrade these queries throw an exception:在任何情况下,升级后这些查询都会引发异常:

LocalDateTime expect date kind Local but it is Unspecified\r\nParameter name: value
at NHibernate.Type.AbstractDateTimeType.Set(DbCommand st, Object value, Int32 index, ISessionImplementor session)
   at NHibernate.Param.NamedParameterSpecification.Bind(DbCommand command, IList`1 multiSqlQueryParametersList, Int32 singleSqlParametersOffset, IList`1 sqlQueryParametersList, QueryParameters queryParameters, ISessionImplementor session)
   at NHibernate.Param.NamedParameterSpecification.Bind(DbCommand command, IList`1 sqlQueryParametersList, QueryParameters queryParameters, ISessionImplementor session)
   at NHibernate.SqlCommand.SqlCommandImpl.Bind(DbCommand command, ISessionImplementor session)
   at NHibernate.Loader.Loader.PrepareQueryCommand(QueryParameters queryParameters, Boolean scroll, ISessionImplementor session)
   at NHibernate.Loader.Loader.DoQuery(ISessionImplementor session, QueryParameters queryParameters, Boolean returnProxies, IResultTransformer forcedResultTransformer, QueryCacheResultBuilder queryCacheResultBuilder)
   at NHibernate.Loader.Loader.DoQueryAndInitializeNonLazyCollections(ISessionImplementor session, QueryParameters queryParameters, Boolean returnProxies, IResultTransformer forcedResultTransformer, QueryCacheResultBuilder queryCacheResultBuilder)
   at NHibernate.Loader.Loader.DoList(ISessionImplementor session, QueryParameters queryParameters, IResultTransformer forcedResultTransformer, QueryCacheResultBuilder queryCacheResultBuilder)

In other words, I'm trying to query by EntityDate with a parameter DateTime value that has DateTimeKind=Unspecified.换句话说,我试图通过 EntityDate 使用具有 DateTimeKind=Unspecified 的参数 DateTime 值进行查询。 nHibernate now enforces that the parameter value MUST have DateTimeKind=Local for any LocalDateTimeType properties; nHibernate 现在强制参数值必须具有 DateTimeKind=Local 对于任何 LocalDateTimeType 属性; so the query builder won't bind my parameter value.所以查询生成器不会绑定我的参数值。

I could go through every single place in the application that queries on this property (there are many) and fix each one individually.我可以 go 通过应用程序中查询此属性(有很多)的每个位置并单独修复每个位置。 But that sucks, in my opinion.但这很糟糕,在我看来。 How can I intercept or listen to an event that's positioned before the parameter binding process, look for instances of querying on "entitydate" with a bad parameter value, and fix the parameter value to have the proper DateTimeKind?如何拦截或侦听在参数绑定过程之前定位的事件,查找使用错误参数值查询“entitydate”的实例,并修复参数值以具有正确的 DateTimeKind? I can't find any interceptor or event listener that seems to be able to do this.我找不到任何似乎能够做到这一点的拦截器或事件监听器。

Turns out I was looking at the wrong part of the API, and this was solved using a custom user type instead of event listener or interceptor.原来我正在查看 API 的错误部分,这是使用自定义用户类型而不是事件侦听器或拦截器解决的。 The custom type is an implementation of IUserType that wraps a DateTime and forcibly converts any parameter of a DbCommand mapped to the type, to have DateTimeKind.Local.自定义类型是 IUserType 的实现,它包装 DateTime 并强制将映射到该类型的 DbCommand 的任何参数转换为具有 DateTimeKind.Local。

[Serializable]
public class LocalKindDateTimeType : IUserType
{
    public SqlType[] SqlTypes => new SqlType[]
    {
        new SqlType(DbType.DateTime)
    };

    public Type ReturnedType => typeof(DateTime);

    public bool IsMutable => false;

    public object Assemble(object cached, object owner)
    {
        return DeepCopy(cached);
    }

    public object DeepCopy(object value)
    {
        return value;
    }

    public object Disassemble(object value)
    {
        return DeepCopy(value);
    }

    public new bool Equals(object x, object y)
    {
        if (ReferenceEquals(x, y))
        {
            return true;
        }

        if (x == null || y == null)
        {
            return false;
        }

        return x.Equals(y);
    }

    public int GetHashCode(object x)
    {
        return x.GetHashCode();
    }

    public object NullSafeGet(DbDataReader rs, string[] names, ISessionImplementor session, object owner)
    {
        if (owner == null)
            return null;
        return DateTime.SpecifyKind((DateTime)NHibernateUtil.DateTime.NullSafeGet(rs, names, session), DateTimeKind.Local);
    }

    public void NullSafeSet(DbCommand cmd, object value, int index, ISessionImplementor session)
    {
        if (value is DateTime date)
            NHibernateUtil.DateTime.NullSafeSet(cmd, DateTime.SpecifyKind(date, DateTimeKind.Local), index, session);
        else
            NHibernateUtil.DateTime.NullSafeSet(cmd, value, index, session);
    }

    public object Replace(object original, object target, object owner)
    {
        return original;
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM