带有 JoinAlias 的 Nhibernate QueryOver 上的 TargetInvocationException
TargetInvocationException on Nhibernate QueryOver with JoinAlias
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)
这一行:
queryOver.Where(
() => slotAlias.StartDate >= DateTimeOffset.UtcNow.AddMinutes(productAlias.Duration));
您实际上是在将应用程序端逻辑与数据库端逻辑混合在一起。 DateTimeOffset.UtcNow.AddMinutes
在 之前执行 查询被发送到数据库。这就是为什么传递常量值而不是 productAlias.Duration
起作用的原因。
由于您没有将预定义值作为参数发送,因此您需要在数据库中执行日期操作。
执行此操作可能需要几个步骤,具体取决于您的方言。我将假设 SQL Server 2012。
使用定义 addminutes
函数的函数创建自定义方言:
public class MyDialect : MsSql2012Dialect
{
public MyDialect()
{
this.RegisterFunction(
"addminutes",
new SQLFunctionTemplate(NHibernateUtil.DateTimeOffset, "dateadd(n, ?1, ?2)"));
}
}
在您的查询中使用新注册的函数:
queryOver = queryOver.Where(
Restrictions.GeProperty(
Projections.Property(() => slotAlias.StartDate),
Projections.SqlFunction(
"addminutes",
NHibernateUtil.DateTimeOffset,
Projections.Property(() => productAlias.Duration),
Projections.Constant(DateTimeOffset.UtcNow))))
这会生成一个 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))
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)
这一行:
queryOver.Where(
() => slotAlias.StartDate >= DateTimeOffset.UtcNow.AddMinutes(productAlias.Duration));
您实际上是在将应用程序端逻辑与数据库端逻辑混合在一起。 DateTimeOffset.UtcNow.AddMinutes
在 之前执行 查询被发送到数据库。这就是为什么传递常量值而不是 productAlias.Duration
起作用的原因。
由于您没有将预定义值作为参数发送,因此您需要在数据库中执行日期操作。
执行此操作可能需要几个步骤,具体取决于您的方言。我将假设 SQL Server 2012。
使用定义
addminutes
函数的函数创建自定义方言:public class MyDialect : MsSql2012Dialect { public MyDialect() { this.RegisterFunction( "addminutes", new SQLFunctionTemplate(NHibernateUtil.DateTimeOffset, "dateadd(n, ?1, ?2)")); } }
在您的查询中使用新注册的函数:
queryOver = queryOver.Where( Restrictions.GeProperty( Projections.Property(() => slotAlias.StartDate), Projections.SqlFunction( "addminutes", NHibernateUtil.DateTimeOffset, Projections.Property(() => productAlias.Duration), Projections.Constant(DateTimeOffset.UtcNow))))
这会生成一个 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))