带有JoinAlias的Nhibernate QueryOver上的TargetInvocationException

时间:2016-08-07 18:01:50

标签: nhibernate queryover

Product productAlias = null;
Session sessionAlias = null;
Slot slotAlias = null;
Price priceAlias = null;

var queryOver = session.QueryOver<Slot>(() => slotAlias)
    .JoinAlias(() => slotAlias.Session, () => sessionAlias)
    .JoinAlias(() => sessionAlias.Product, () => productAlias);

if (productGuid.HasValue)
{
    var productEntity = session.Query<Product>().FirstOrDefault(x => x.Guid == productGuid.Value);
    queryOver = queryOver.Where(() => productAlias.Id == productEntity.Id);
}

if (onlyAvailable)
{
    queryOver = queryOver.Where(() => slotAlias.StartDate >= DateTimeOffset.UtcNow.AddMinutes(productAlias.Duration));
}

queryOver.List();

当我运行此查询时,我得到TargetInvocationException。在内部消息中,它在slotAlias.StartDate处是NullReferenceException(第18行,在onlyAvailable if子句内)。

使用if子句和多个Where子句这样的别名是否有问题?

堆栈跟踪:

System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> System.NullReferenceException: Object reference not set to an instance of an object.
   at lambda_method(Closure )
   --- End of inner exception stack trace ---
   at System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor)
   at System.Reflection.RuntimeMethodInfo.UnsafeInvokeInternal(Object obj, Object[] parameters, Object[] arguments)
   at System.Delegate.DynamicInvokeImpl(Object[] args)
   at NHibernate.Impl.ExpressionProcessor.FindValue(Expression expression)
   at NHibernate.Impl.ExpressionProcessor.ProcessSimpleExpression(Expression left, Expression right, ExpressionType nodeType)
   at NHibernate.Impl.ExpressionProcessor.ProcessSimpleExpression(BinaryExpression be)
   at NHibernate.Impl.ExpressionProcessor.ProcessBinaryExpression(BinaryExpression expression)
   at NHibernate.Impl.ExpressionProcessor.ProcessExpression(Expression expression)
   at NHibernate.Criterion.QueryOver`2.NHibernate.IQueryOver<TRoot,TSubType>.Where(Expression`1 expression)
   at Fullseats.Database.Repositories.Repository.GetSlots(Int32 limit, Int32 offset, Nullable`1 operatorGuid, Nullable`1 productGuid, Nullable`1 from, Nullable`1 to, Boolean onlyAvailable) in C:\Users\erkin\Desktop\FullSeats\Fullseats.Api\Fullseats.Database\Repositories\Repository.cs:line 455
   at Fullseats.Server.Core.Services.ProductService.GetSlots(Guid productGuid, PaginationQuery paginationQuery) in C:\Users\erkin\Desktop\FullSeats\Fullseats.Api\Fullseats.Server\Core\Services\ProductService.cs:line 63
   at Fullseats.Server.Modules.ProductModule.GetSlotsForProduct(Object arg) in C:\Users\erkin\Desktop\FullSeats\Fullseats.Api\Fullseats.Server\Modules\ProductModule.cs:line 224
   at CallSite.Target(Closure , CallSite , Func`2 , Object )
   at System.Dynamic.UpdateDelegates.UpdateAndExecute2[T0,T1,TRet](CallSite site, T0 arg0, T1 arg1)
   at Nancy.Routing.Route.<>c__DisplayClass4.<Wrap>b__3(Object parameters, CancellationToken context)

1 个答案:

答案 0 :(得分:1)

这一行:

queryOver.Where(
    () => slotAlias.StartDate >= DateTimeOffset.UtcNow.AddMinutes(productAlias.Duration));

您实际上是将应用程序端逻辑与数据库端逻辑混合在一起。在将查询发送到数据库之前,执行DateTimeOffset.UtcNow.AddMinutes。这就是传递常数值而不是productAlias.Duration的原因。

由于您没有将预定义值作为参数发送,因此您需要在数据库中执行日期操作。

这可能需要几个步骤,具体取决于您的方言。我将假设SQL Server 2012。

  1. 使用定义addminutes函数的函数创建自定义方言:

    public class MyDialect : MsSql2012Dialect
    {
        public MyDialect()
        {
            this.RegisterFunction(
                "addminutes",
                new SQLFunctionTemplate(NHibernateUtil.DateTimeOffset, "dateadd(n, ?1, ?2)"));
        }
    }
    
  2. 在查询中使用新注册的功能:

    queryOver = queryOver.Where(
        Restrictions.GeProperty(
            Projections.Property(() => slotAlias.StartDate),
            Projections.SqlFunction(
                "addminutes",
                NHibernateUtil.DateTimeOffset,
                Projections.Property(() => productAlias.Duration),
                Projections.Constant(DateTimeOffset.UtcNow))))
    
  3. 这会生成一个如下所示的SQL代码段:

    WHERE
        slotAlia_1.StartDate >= dateadd(minute, this_.Duration, '8/9/2016 2:22:48 AM +00:00');
    

    C#可能有点难以阅读,您可以重构SqlFunction部分:

    var addMinutesFunction = Projections.SqlFunction(
        "addMinutes",
        NHibernateUtil.DateTimeOffset,
        Projections.Property(() => productAlias.Duration),   
        Projections.Constant(DateTimeOffset.UtcNow))
    
    queryOver = queryOver.Where(
        Restrictions.GeProperty(
            Projections.Property(() => slotAlias.StartDate), addMinutesFunction))