仅比较 entity framework 6 中日期时间的时间与 odp.net Oracle 12c
Compare only time from datetime in entity framework 6 with odp.net Oracle 12c
我正在使用 entity framework 6 与 oracle 和 Sql。 Timespan 数据类型不适用于 oracle。所以我将数据类型更改为日期时间。现在我只想将日期时间中的时间与 Linq 查询进行比较。前任。
var db0010016 = _idb0010016Rep.GetAll().Where(e => e.ExecutionTime.TimeOfDay == viewmodel.ExecutionTime).FirstOrDefault();
在上面的示例中,e.ExecutionTime 是日期时间,viewmodel.ExecutionTime 是时间跨度。我正在使用 timeofday 函数将其转换为时间跨度
上面的查询执行失败所以我使用了 DbFunctions.CreateTime() 函数
var db0010016 = _idb0010016Rep.FindBy(e => DbFunctions.CreateTime(e.ExecutionTime.Hour, e.ExecutionTime.Minute, e.ExecutionTime.Second) == exetime).FirstOrDefault();
上面的 ex exetime 是 timespan.still 我得到低于错误
{"Invalid parameter binding\r\nParameter name: ParameterName"}
能否将 oracle timespan 和 SQL datetime 转换为字符串,然后进行比较。喜欢:
var db0010016 = _idb0010016Rep.GetAll().Where(e => e.ExecutionTime.TimeOfDay.ToString() == viewmodel.ExecutionTime.ToString()).FirstOrDefault()
由于 oracle 的日期和时间问题,我们只使用字符串:
using(MyDbContext ctx = new MyDbContext())
{
TimeSpan myTime = new TimeSpan(12, 00, 00);
string myTimeString = myTime.ToString("hh':'mm':'ss");
List<ExecutionObjects> tmp = ctx.ExecutionObjects.Where(a => a.ExecutionTime.EndsWith(myTimeString)).ToList();
// Access field in source with seperated DateTime-property.
tmp.ForEach(e => Console.WriteLine(e.ExecutionTimeDateTime.ToShortDateString()));
}
在源代码中你可以添加一个 DateTime-parsing-属性:
public class ExecutionObject
{
[Column("ColExecutionTime")]
public string ExecutionTime { get; set; }
[NotMapped]
public DateTime ExecutionTimeDateTime {
get
{
return DateTime.ParseExact(this.ExecutionTime, "yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture);
}
set
{
this.ExecutionTime = value.ToString("yyyy-MM-dd HH:mm:ss");
}
}
}
不是最漂亮的版本,但可以用。
这是 DbFunctions 中基于 Oracle 的问题。如果您激活 sql-log,您会看到使用了一个未知的函数 "CREATETIME()"。
激活sql-日志:
ctx.Database.Log = Console.WriteLine;
日志将如下所示:
SELECT *
FROM "ExecutionTimes" "Extent1"
WHERE ((((CREATETIME (EXTRACT (HOUR FROM (CAST (
[...]
如果您只想比较可以使用 DbFunctions 中受支持的 EDM 规范函数的时间(请参阅 here)。
很遗憾 DbFunction.CreateTime 不受支持。
不过,如果您对以秒为单位的比较感兴趣,您可以这样做:
var refTime = new DateTime(2017, 12, 13, 09, 30, 31);
using (this.ctx = new MyContext())
{
var results = this.ctx.Groupings.Where(e => DbFunctions.DiffSeconds(e.EndDate, refTime) % 86400 == 0).ToList();
}
在这里,您使用 EDM 函数 DiffSeconds 以秒为单位求差,并用一天中的秒数取模。
执行的查询是:
select
"Extent1"."GROUP_TYPE" as "GROUP_TYPE",
"Extent1"."GROUP_ENTITY_ID" as "GROUP_ENTITY_ID",
"Extent1"."ITEM_ENTITY_ID" AS "ITEM_ENTITY_ID",
"Extent1"."DATE_START" as "DATE_START",
"Extent1"."DATE_END" AS "DATE_END"
from "MYSCHEMA"."ENTITY_GROUP_REL" "Extent1" where (0 = (mod( extract( day from ( cast(:p__linq__0 as timestamp(9)) - cast("Extent1"."DATE_END" as timestamp(9))))*24*60*60 + extract( hour from( cast(:p__linq__0 as timestamp(9)) - cast("Extent1"."DATE_END" as timestamp(9))))*60*60 + extract( minute from ( cast(:p__linq__0 as timestamp(9)) - cast("Extent1"."DATE_END" as timestamp(9))))*60 + extract( second from ( cast(:p__linq__0 as timestamp(9)) - cast("Extent1"."DATE_END" as timestamp(9)))) ,86400)))
如您所见,它可以正确转换为服务器端的 oracle 函数。
希望对你有所帮助,
尼古拉
EF6 查询转换器不支持 DateTime.TimeOfDay
,Oracle 提供程序不支持 DbFunctions.CreateTime
和 TimeSpan
parameters/constants.
按照另一个答案的建议,在将存储从 DateTime
切换到 string
之前还有一些选项。
首先,对于相等性检查,您可以通过将时间分量提取到单独的变量(查询参数)中来比较时间分量:
var hour = viewmodel.ExecutionTime.Hours;
var minute = viewmodel.ExecutionTime.Minutes;
var second = viewmodel.ExecutionTime.Seconds;
var db0010016 = _idb0010016Rep.FindBy(e =>
e.ExecutionTime.Hour == hour && e.ExecutionTime.Minute == minute && e.ExecutionTime.Second == second)
.FirstOrDefault();
或进入假DateTime
变量(queryParameter):
var executionTime = DateTime.Today + viewmodel.ExecutionTime;
var db0010016 = _idb0010016Rep.FindBy(e =>
e.ExecutionTime.Hour == executionTime.Hour && e.ExecutionTime.Minute == executionTime.Minute && e.ExecutionTime.Second == executionTime.Second)
.FirstOrDefault();
其次,您可以将时间转换为秒。这还允许您执行任何比较:
var executionTime = (int)viewmodel.ExecutionTime.TotalSeconds;
var db0010016 = _idb0010016Rep.FindBy(e =>
60 * 60 * e.ExecutionTime.Hour + 60 * e.ExecutionTime.Minute + e.ExecutionTime.Second == executionTime)
.FirstOrDefault();
但是手动完成所有这些操作非常烦人且容易出错。我可以提供的是提供自定义扩展方法的小实用程序 class:
public static partial class QueryableExtensions
{
public static IQueryable<T> ConvertTimeSpans<T>(this IQueryable<T> source)
{
var expr = new TimeSpanConverter().Visit(source.Expression);
return source == expr ? source : source.Provider.CreateQuery<T>(expr);
}
class TimeSpanConverter : ExpressionVisitor
{
static readonly Expression<Func<DateTime, int>> ConvertTimeOfDay = dt =>
60 * (60 * dt.Hour + dt.Minute) + dt.Second;
static int ConvertTimespan(TimeSpan ts) =>
60 * (60 * ts.Hours + ts.Minutes) + ts.Seconds;
protected override Expression VisitMember(MemberExpression node)
{
if (node.Type == typeof(TimeSpan))
{
if (node.Member.DeclaringType == typeof(DateTime) && node.Member.Name == nameof(DateTime.TimeOfDay))
return ConvertTimeOfDay.ReplaceParameter(0, base.Visit(node.Expression));
// Evaluate the TimeSpan value, convert and wrap it into closure (to keep non const semantics)
return ConvertTimespan(base.VisitMember(node).Evaluate<TimeSpan>()).ToClosure().Body;
}
return base.VisitMember(node);
}
protected override Expression VisitBinary(BinaryExpression node)
{
if (node.Left.Type == typeof(TimeSpan))
return Expression.MakeBinary(node.NodeType, Visit(node.Left), Visit(node.Right));
return base.VisitBinary(node);
}
}
static T Evaluate<T>(this Expression source) => Expression.Lambda<Func<T>>(source).Compile().Invoke();
static Expression<Func<T>> ToClosure<T>(this T value) => () => value;
static Expression ReplaceParameter(this LambdaExpression source, int index, Expression target) =>
new ParameterReplacer { Source = source.Parameters[index], Target = target }.Visit(source.Body);
class ParameterReplacer : ExpressionVisitor
{
public ParameterExpression Source;
public Expression Target;
protected override Expression VisitParameter(ParameterExpression node) => node == Source ? Target : node;
}
}
它使用两个小的自定义 ExpressionVisitor
classes 来转换 DateTime.TimeOfDay
属性 和 TimeSpan
class 类似于您的 viewModel.ExecutionTime
.
现在您应该像这样使用您的原始查询:
var db0010016 = _idb0010016Rep.GetAll()
.Where(e => e.ExecutionTime.TimeOfDay == viewmodel.ExecutionTime)
.ConvertTimeStamps() // the magic happens here
.FirstOrDefault();
如果您想使用毫秒而不是秒,只需将 TimeSpanConverter
class 中的前两个语句更改如下:
static readonly Expression<Func<DateTime, int>> ConvertTimeOfDay = dt =>
1000 * (60 * (60 * dt.Hour + dt.Minute) + dt.Second) + dt.Millisecond;
static int ConvertTimespan(TimeSpan ts) =>
1000 * (60 * (60 * ts.Hours + ts.Minutes) + ts.Seconds) + ts.Milliseconds;
我正在使用 entity framework 6 与 oracle 和 Sql。 Timespan 数据类型不适用于 oracle。所以我将数据类型更改为日期时间。现在我只想将日期时间中的时间与 Linq 查询进行比较。前任。
var db0010016 = _idb0010016Rep.GetAll().Where(e => e.ExecutionTime.TimeOfDay == viewmodel.ExecutionTime).FirstOrDefault();
在上面的示例中,e.ExecutionTime 是日期时间,viewmodel.ExecutionTime 是时间跨度。我正在使用 timeofday 函数将其转换为时间跨度
上面的查询执行失败所以我使用了 DbFunctions.CreateTime() 函数
var db0010016 = _idb0010016Rep.FindBy(e => DbFunctions.CreateTime(e.ExecutionTime.Hour, e.ExecutionTime.Minute, e.ExecutionTime.Second) == exetime).FirstOrDefault();
上面的 ex exetime 是 timespan.still 我得到低于错误
{"Invalid parameter binding\r\nParameter name: ParameterName"}
能否将 oracle timespan 和 SQL datetime 转换为字符串,然后进行比较。喜欢:
var db0010016 = _idb0010016Rep.GetAll().Where(e => e.ExecutionTime.TimeOfDay.ToString() == viewmodel.ExecutionTime.ToString()).FirstOrDefault()
由于 oracle 的日期和时间问题,我们只使用字符串:
using(MyDbContext ctx = new MyDbContext())
{
TimeSpan myTime = new TimeSpan(12, 00, 00);
string myTimeString = myTime.ToString("hh':'mm':'ss");
List<ExecutionObjects> tmp = ctx.ExecutionObjects.Where(a => a.ExecutionTime.EndsWith(myTimeString)).ToList();
// Access field in source with seperated DateTime-property.
tmp.ForEach(e => Console.WriteLine(e.ExecutionTimeDateTime.ToShortDateString()));
}
在源代码中你可以添加一个 DateTime-parsing-属性:
public class ExecutionObject
{
[Column("ColExecutionTime")]
public string ExecutionTime { get; set; }
[NotMapped]
public DateTime ExecutionTimeDateTime {
get
{
return DateTime.ParseExact(this.ExecutionTime, "yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture);
}
set
{
this.ExecutionTime = value.ToString("yyyy-MM-dd HH:mm:ss");
}
}
}
不是最漂亮的版本,但可以用。
这是 DbFunctions 中基于 Oracle 的问题。如果您激活 sql-log,您会看到使用了一个未知的函数 "CREATETIME()"。
激活sql-日志: ctx.Database.Log = Console.WriteLine;
日志将如下所示:
SELECT *
FROM "ExecutionTimes" "Extent1"
WHERE ((((CREATETIME (EXTRACT (HOUR FROM (CAST (
[...]
如果您只想比较可以使用 DbFunctions 中受支持的 EDM 规范函数的时间(请参阅 here)。 很遗憾 DbFunction.CreateTime 不受支持。
不过,如果您对以秒为单位的比较感兴趣,您可以这样做:
var refTime = new DateTime(2017, 12, 13, 09, 30, 31);
using (this.ctx = new MyContext())
{
var results = this.ctx.Groupings.Where(e => DbFunctions.DiffSeconds(e.EndDate, refTime) % 86400 == 0).ToList();
}
在这里,您使用 EDM 函数 DiffSeconds 以秒为单位求差,并用一天中的秒数取模。
执行的查询是:
select
"Extent1"."GROUP_TYPE" as "GROUP_TYPE",
"Extent1"."GROUP_ENTITY_ID" as "GROUP_ENTITY_ID",
"Extent1"."ITEM_ENTITY_ID" AS "ITEM_ENTITY_ID",
"Extent1"."DATE_START" as "DATE_START",
"Extent1"."DATE_END" AS "DATE_END"
from "MYSCHEMA"."ENTITY_GROUP_REL" "Extent1" where (0 = (mod( extract( day from ( cast(:p__linq__0 as timestamp(9)) - cast("Extent1"."DATE_END" as timestamp(9))))*24*60*60 + extract( hour from( cast(:p__linq__0 as timestamp(9)) - cast("Extent1"."DATE_END" as timestamp(9))))*60*60 + extract( minute from ( cast(:p__linq__0 as timestamp(9)) - cast("Extent1"."DATE_END" as timestamp(9))))*60 + extract( second from ( cast(:p__linq__0 as timestamp(9)) - cast("Extent1"."DATE_END" as timestamp(9)))) ,86400)))
如您所见,它可以正确转换为服务器端的 oracle 函数。
希望对你有所帮助,
尼古拉
EF6 查询转换器不支持 DateTime.TimeOfDay
,Oracle 提供程序不支持 DbFunctions.CreateTime
和 TimeSpan
parameters/constants.
按照另一个答案的建议,在将存储从 DateTime
切换到 string
之前还有一些选项。
首先,对于相等性检查,您可以通过将时间分量提取到单独的变量(查询参数)中来比较时间分量:
var hour = viewmodel.ExecutionTime.Hours;
var minute = viewmodel.ExecutionTime.Minutes;
var second = viewmodel.ExecutionTime.Seconds;
var db0010016 = _idb0010016Rep.FindBy(e =>
e.ExecutionTime.Hour == hour && e.ExecutionTime.Minute == minute && e.ExecutionTime.Second == second)
.FirstOrDefault();
或进入假DateTime
变量(queryParameter):
var executionTime = DateTime.Today + viewmodel.ExecutionTime;
var db0010016 = _idb0010016Rep.FindBy(e =>
e.ExecutionTime.Hour == executionTime.Hour && e.ExecutionTime.Minute == executionTime.Minute && e.ExecutionTime.Second == executionTime.Second)
.FirstOrDefault();
其次,您可以将时间转换为秒。这还允许您执行任何比较:
var executionTime = (int)viewmodel.ExecutionTime.TotalSeconds;
var db0010016 = _idb0010016Rep.FindBy(e =>
60 * 60 * e.ExecutionTime.Hour + 60 * e.ExecutionTime.Minute + e.ExecutionTime.Second == executionTime)
.FirstOrDefault();
但是手动完成所有这些操作非常烦人且容易出错。我可以提供的是提供自定义扩展方法的小实用程序 class:
public static partial class QueryableExtensions
{
public static IQueryable<T> ConvertTimeSpans<T>(this IQueryable<T> source)
{
var expr = new TimeSpanConverter().Visit(source.Expression);
return source == expr ? source : source.Provider.CreateQuery<T>(expr);
}
class TimeSpanConverter : ExpressionVisitor
{
static readonly Expression<Func<DateTime, int>> ConvertTimeOfDay = dt =>
60 * (60 * dt.Hour + dt.Minute) + dt.Second;
static int ConvertTimespan(TimeSpan ts) =>
60 * (60 * ts.Hours + ts.Minutes) + ts.Seconds;
protected override Expression VisitMember(MemberExpression node)
{
if (node.Type == typeof(TimeSpan))
{
if (node.Member.DeclaringType == typeof(DateTime) && node.Member.Name == nameof(DateTime.TimeOfDay))
return ConvertTimeOfDay.ReplaceParameter(0, base.Visit(node.Expression));
// Evaluate the TimeSpan value, convert and wrap it into closure (to keep non const semantics)
return ConvertTimespan(base.VisitMember(node).Evaluate<TimeSpan>()).ToClosure().Body;
}
return base.VisitMember(node);
}
protected override Expression VisitBinary(BinaryExpression node)
{
if (node.Left.Type == typeof(TimeSpan))
return Expression.MakeBinary(node.NodeType, Visit(node.Left), Visit(node.Right));
return base.VisitBinary(node);
}
}
static T Evaluate<T>(this Expression source) => Expression.Lambda<Func<T>>(source).Compile().Invoke();
static Expression<Func<T>> ToClosure<T>(this T value) => () => value;
static Expression ReplaceParameter(this LambdaExpression source, int index, Expression target) =>
new ParameterReplacer { Source = source.Parameters[index], Target = target }.Visit(source.Body);
class ParameterReplacer : ExpressionVisitor
{
public ParameterExpression Source;
public Expression Target;
protected override Expression VisitParameter(ParameterExpression node) => node == Source ? Target : node;
}
}
它使用两个小的自定义 ExpressionVisitor
classes 来转换 DateTime.TimeOfDay
属性 和 TimeSpan
class 类似于您的 viewModel.ExecutionTime
.
现在您应该像这样使用您的原始查询:
var db0010016 = _idb0010016Rep.GetAll()
.Where(e => e.ExecutionTime.TimeOfDay == viewmodel.ExecutionTime)
.ConvertTimeStamps() // the magic happens here
.FirstOrDefault();
如果您想使用毫秒而不是秒,只需将 TimeSpanConverter
class 中的前两个语句更改如下:
static readonly Expression<Func<DateTime, int>> ConvertTimeOfDay = dt =>
1000 * (60 * (60 * dt.Hour + dt.Minute) + dt.Second) + dt.Millisecond;
static int ConvertTimespan(TimeSpan ts) =>
1000 * (60 * (60 * ts.Hours + ts.Minutes) + ts.Seconds) + ts.Milliseconds;