ServiceStack 可以通过 System.DateTime 值进行查询吗?
Can ServiceStack do a query by System.DateTime value?
我正在评估 ServiceStack 以确定它是否适用于通用 REST 服务器构建目的,并且我正在尝试扩展 Northwind 演示,我已在本地更新该演示以使用 ServiceStack 4.0.44。我调整过的代码在 Bitbucket here, dumped from git to hg, then with my own changes 添加。
我的代码与上游serviceStack.Examples northwind demo 的区别在于添加了以下内容:
已将 .../date/ 路线添加到订单的 ServiceModel.Operations 对象中:
namespace ServiceStack.Northwind.ServiceModel.Operations
{
...
[Route("/orders")]
[Route("/orders/date/{ByDate}")] // ADDED!
[Route("/orders/page/{Page}")]
[Route("/customers/{CustomerId}/orders")]
public class Orders
{
public int? Page { get; set; }
public DateTime? ByDate { get; set; } // ADDED!
public string CustomerId { get; set; }
}
然后将这种情况的处理程序添加到服务中,该服务在运行时因奇怪的 LINQ 故障而崩溃:variable 'o' of type 'ServiceStack.Northwind.ServiceModel.Types.Order' referenced from scope '', but it is not defined...
Source = System.Core
namespace ServiceStack.Northwind.ServiceInterface
{
public class OrdersService : Service
{
private const int PageCount = 20;
public object Get(Orders request)
{
List<Order> orders = null;
if (request.ByDate.HasValue)
{ // date provided
// broken LINQ #1 -- EXCEPTION HERE AT RUNTIME!
orders = Db.Select<Order>(order => order.Where<Order>(o => o.OrderDate.Value.Date == request.ByDate.Value.Date ));
/*
broken LINQ #2
orders =
Db.Select(
Db.From<Order>().
Where(o => (o != null) && o.OrderDate.HasValue && (request.ByDate.Value.Date == o.OrderDate.Value.Date)
)
);
*/
}
else if (request.CustomerId.IsNullOrEmpty())
{
orders = Db.Select<Order>(order => order.OrderByDescending(o => o.OrderDate))
.Skip((request.Page.GetValueOrDefault(1) - 1) * PageCount)
.Take(PageCount)
.ToList();
}
else
{
orders = Db.Select<Order>(order => order.Where(o => o.CustomerId == request.CustomerId));
}
if (orders.Count == 0)
该异常要么表明我不知道如何编写 LINQ 表达式来按日期查询,要么可能表明 SQLite 的 ORM 存在内部错误,或者整体存在错误框架,或者我做错了什么。
当我尝试按 DateTime 值查询时,Firebird 提供程序也会重现类似的运行时崩溃,因此它可能是 SQLite 和 Firebird ORMLite 提供程序中的错误。
System.InvalidOperationException was unhandled by user code
HResult = -2146233079
Message = variable 'o' of type 'ServiceStack.Northwind.ServiceModel.Types.Order' referenced from scope '', but it is not defined
Source = System.Core
StackTrace:
at System.Linq.Expressions.Compiler.VariableBinder.Reference(ParameterExpression node, VariableStorageKind storage)
at System.Linq.Expressions.Compiler.VariableBinder.VisitParameter(ParameterExpression node)
at System.Linq.Expressions.ParameterExpression.Accept(ExpressionVisitor visitor)
at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
at System.Linq.Expressions.ExpressionVisitor.VisitMember(MemberExpression node)
at System.Linq.Expressions.MemberExpression.Accept(ExpressionVisitor visitor)
at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
at System.Linq.Expressions.ExpressionVisitor.VisitMember(MemberExpression node)
at System.Linq.Expressions.MemberExpression.Accept(ExpressionVisitor visitor)
at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
at System.Linq.Expressions.Compiler.VariableBinder.VisitUnary(UnaryExpression node)
at System.Linq.Expressions.UnaryExpression.Accept(ExpressionVisitor visitor)
at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
at System.Linq.Expressions.ExpressionVisitor.Visit(ReadOnlyCollection`1 nodes)
at System.Linq.Expressions.Compiler.VariableBinder.VisitLambda[T](Expression`1 node)
at System.Linq.Expressions.Expression`1.Accept(ExpressionVisitor visitor)
at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
at System.Linq.Expressions.Compiler.VariableBinder.Bind(LambdaExpression lambda)
at System.Linq.Expressions.Compiler.LambdaCompiler.Compile(LambdaExpression lambda, DebugInfoGenerator debugInfoGenerator)
at ServiceStack.OrmLite.SqlExpression`1.VisitMemberAccess(MemberExpression m)
at ServiceStack.OrmLite.SqlExpression`1.Visit(Expression exp)
at ServiceStack.OrmLite.SqlExpression`1.VisitBinary(BinaryExpression b)
at ServiceStack.OrmLite.SqlExpression`1.Visit(Expression exp)
at ServiceStack.OrmLite.SqlExpression`1.VisitBinary(BinaryExpression b)
at ServiceStack.OrmLite.SqlExpression`1.Visit(Expression exp)
at ServiceStack.OrmLite.SqlExpression`1.VisitLambda(LambdaExpression lambda)
at ServiceStack.OrmLite.SqlExpression`1.Visit(Expression exp)
at ServiceStack.OrmLite.SqlExpression`1.AppendToWhere(String condition, Expression predicate)
at ServiceStack.OrmLite.SqlExpression`1.Where(Expression`1 predicate)
at ServiceStack.Northwind.ServiceInterface.OrdersService.Get(Orders request) in C:\dev\ServiceStack.Examples\src\ServiceStack.Northwind\ServiceStack.Northwind.ServiceInterface\OrdersService.cs:line 19
at lambda_method(Closure, Object, Object)
at ServiceStack.Host.ServiceRunner`1.Execute(IRequest request, Object instance, TRequest requestDto)
InnerException:
解决方法:
如果我自己写一个 SQL where 子句语句,它会起作用:
if (request.ByDate.HasValue)
{ // date provided
string condition = string.Format("OrderDate = '{0}'", request.ByDate.Value.Date.ToString("yyyy-MM-dd") );
orders = Db.Select<Order>(condition);
ServiceStack当然可以按DateTime查询。但是在您的情况下,您试图仅按日期部分查询,而忽略时间(类似于 date(OrderDate) = date(ByDate))。事情是 - 你为此使用的表达式 (o.OrderDate.Value.Date == request.ByDate.Value.Date) 可能看起来很简单,但它实际上是由有时复杂的表达式树表示的。此表达式树由 ORM 探索并转换为原始 sql 查询。现在,表达式可以是任意复杂的,当然并不是所有的表达式都可以转换为 sql 查询。 ORM 的开发者应该明确地考虑哪些表达式可能对实现有用。在这种情况下,很可能是 ORM 的开发人员在您访问 DateTime 实例的 .Date 属性 时没有处理这种情况,或者由于某种原因甚至可能无法处理这种情况。您希望这会被转换为类似 date(OrderDate) sql 的内容,但事实并非如此。其他ORM(例如entity framework)有特殊的功能,你可以在表达式中调用(比如:o => DbFunctions.Date(o.OrderDate)),但ServiceStack没有这样的日期功能- 仅比较,据我所知。
长话短说 - 我会说这不是错误,只是不受支持的功能。当然,他们可能会处理并抛出更好的异常,但事实就是如此。
最后按 DateTime 进行查询,如您的问题所述,只需执行以下操作:
var byDate = request.ByDate.Value.Date;
orders = Db.Select<Order>(order => order.OrderDate == byDate);
我正在评估 ServiceStack 以确定它是否适用于通用 REST 服务器构建目的,并且我正在尝试扩展 Northwind 演示,我已在本地更新该演示以使用 ServiceStack 4.0.44。我调整过的代码在 Bitbucket here, dumped from git to hg, then with my own changes 添加。
我的代码与上游serviceStack.Examples northwind demo 的区别在于添加了以下内容:
已将 .../date/ 路线添加到订单的 ServiceModel.Operations 对象中:
namespace ServiceStack.Northwind.ServiceModel.Operations
{
...
[Route("/orders")]
[Route("/orders/date/{ByDate}")] // ADDED!
[Route("/orders/page/{Page}")]
[Route("/customers/{CustomerId}/orders")]
public class Orders
{
public int? Page { get; set; }
public DateTime? ByDate { get; set; } // ADDED!
public string CustomerId { get; set; }
}
然后将这种情况的处理程序添加到服务中,该服务在运行时因奇怪的 LINQ 故障而崩溃:variable 'o' of type 'ServiceStack.Northwind.ServiceModel.Types.Order' referenced from scope '', but it is not defined...
Source = System.Core
namespace ServiceStack.Northwind.ServiceInterface
{
public class OrdersService : Service
{
private const int PageCount = 20;
public object Get(Orders request)
{
List<Order> orders = null;
if (request.ByDate.HasValue)
{ // date provided
// broken LINQ #1 -- EXCEPTION HERE AT RUNTIME!
orders = Db.Select<Order>(order => order.Where<Order>(o => o.OrderDate.Value.Date == request.ByDate.Value.Date ));
/*
broken LINQ #2
orders =
Db.Select(
Db.From<Order>().
Where(o => (o != null) && o.OrderDate.HasValue && (request.ByDate.Value.Date == o.OrderDate.Value.Date)
)
);
*/
}
else if (request.CustomerId.IsNullOrEmpty())
{
orders = Db.Select<Order>(order => order.OrderByDescending(o => o.OrderDate))
.Skip((request.Page.GetValueOrDefault(1) - 1) * PageCount)
.Take(PageCount)
.ToList();
}
else
{
orders = Db.Select<Order>(order => order.Where(o => o.CustomerId == request.CustomerId));
}
if (orders.Count == 0)
该异常要么表明我不知道如何编写 LINQ 表达式来按日期查询,要么可能表明 SQLite 的 ORM 存在内部错误,或者整体存在错误框架,或者我做错了什么。
当我尝试按 DateTime 值查询时,Firebird 提供程序也会重现类似的运行时崩溃,因此它可能是 SQLite 和 Firebird ORMLite 提供程序中的错误。
System.InvalidOperationException was unhandled by user code
HResult = -2146233079
Message = variable 'o' of type 'ServiceStack.Northwind.ServiceModel.Types.Order' referenced from scope '', but it is not defined
Source = System.Core
StackTrace:
at System.Linq.Expressions.Compiler.VariableBinder.Reference(ParameterExpression node, VariableStorageKind storage)
at System.Linq.Expressions.Compiler.VariableBinder.VisitParameter(ParameterExpression node)
at System.Linq.Expressions.ParameterExpression.Accept(ExpressionVisitor visitor)
at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
at System.Linq.Expressions.ExpressionVisitor.VisitMember(MemberExpression node)
at System.Linq.Expressions.MemberExpression.Accept(ExpressionVisitor visitor)
at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
at System.Linq.Expressions.ExpressionVisitor.VisitMember(MemberExpression node)
at System.Linq.Expressions.MemberExpression.Accept(ExpressionVisitor visitor)
at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
at System.Linq.Expressions.Compiler.VariableBinder.VisitUnary(UnaryExpression node)
at System.Linq.Expressions.UnaryExpression.Accept(ExpressionVisitor visitor)
at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
at System.Linq.Expressions.ExpressionVisitor.Visit(ReadOnlyCollection`1 nodes)
at System.Linq.Expressions.Compiler.VariableBinder.VisitLambda[T](Expression`1 node)
at System.Linq.Expressions.Expression`1.Accept(ExpressionVisitor visitor)
at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
at System.Linq.Expressions.Compiler.VariableBinder.Bind(LambdaExpression lambda)
at System.Linq.Expressions.Compiler.LambdaCompiler.Compile(LambdaExpression lambda, DebugInfoGenerator debugInfoGenerator)
at ServiceStack.OrmLite.SqlExpression`1.VisitMemberAccess(MemberExpression m)
at ServiceStack.OrmLite.SqlExpression`1.Visit(Expression exp)
at ServiceStack.OrmLite.SqlExpression`1.VisitBinary(BinaryExpression b)
at ServiceStack.OrmLite.SqlExpression`1.Visit(Expression exp)
at ServiceStack.OrmLite.SqlExpression`1.VisitBinary(BinaryExpression b)
at ServiceStack.OrmLite.SqlExpression`1.Visit(Expression exp)
at ServiceStack.OrmLite.SqlExpression`1.VisitLambda(LambdaExpression lambda)
at ServiceStack.OrmLite.SqlExpression`1.Visit(Expression exp)
at ServiceStack.OrmLite.SqlExpression`1.AppendToWhere(String condition, Expression predicate)
at ServiceStack.OrmLite.SqlExpression`1.Where(Expression`1 predicate)
at ServiceStack.Northwind.ServiceInterface.OrdersService.Get(Orders request) in C:\dev\ServiceStack.Examples\src\ServiceStack.Northwind\ServiceStack.Northwind.ServiceInterface\OrdersService.cs:line 19
at lambda_method(Closure, Object, Object)
at ServiceStack.Host.ServiceRunner`1.Execute(IRequest request, Object instance, TRequest requestDto)
InnerException:
解决方法:
如果我自己写一个 SQL where 子句语句,它会起作用:
if (request.ByDate.HasValue)
{ // date provided
string condition = string.Format("OrderDate = '{0}'", request.ByDate.Value.Date.ToString("yyyy-MM-dd") );
orders = Db.Select<Order>(condition);
ServiceStack当然可以按DateTime查询。但是在您的情况下,您试图仅按日期部分查询,而忽略时间(类似于 date(OrderDate) = date(ByDate))。事情是 - 你为此使用的表达式 (o.OrderDate.Value.Date == request.ByDate.Value.Date) 可能看起来很简单,但它实际上是由有时复杂的表达式树表示的。此表达式树由 ORM 探索并转换为原始 sql 查询。现在,表达式可以是任意复杂的,当然并不是所有的表达式都可以转换为 sql 查询。 ORM 的开发者应该明确地考虑哪些表达式可能对实现有用。在这种情况下,很可能是 ORM 的开发人员在您访问 DateTime 实例的 .Date 属性 时没有处理这种情况,或者由于某种原因甚至可能无法处理这种情况。您希望这会被转换为类似 date(OrderDate) sql 的内容,但事实并非如此。其他ORM(例如entity framework)有特殊的功能,你可以在表达式中调用(比如:o => DbFunctions.Date(o.OrderDate)),但ServiceStack没有这样的日期功能- 仅比较,据我所知。
长话短说 - 我会说这不是错误,只是不受支持的功能。当然,他们可能会处理并抛出更好的异常,但事实就是如此。
最后按 DateTime 进行查询,如您的问题所述,只需执行以下操作:
var byDate = request.ByDate.Value.Date;
orders = Db.Select<Order>(order => order.OrderDate == byDate);