2017-11-06 109 views
9

używam struktury encji 6 z oracle i Sql. Typ danych Timespan nie działa z Oracle. więc zmieniłem typ danych na datetime. teraz chcę porównać tylko czas w datetime z zapytaniem Linq. dawny.Porównaj tylko czas od datetime w ramce 6 środowiska z odp.net Oracle 12c

var db0010016 = _idb0010016Rep.GetAll().Where(e => e.ExecutionTime.TimeOfDay == viewmodel.ExecutionTime).FirstOrDefault(); 

w powyższym przykładzie e.ExecutionTime jest DateTime viewmodel.ExecutionTime jest skumulowanie. Używam timeofday funkcję, aby przekształcić go w wybranym przedziale dat

Powyższe zapytanie udało się wykonać tak użyłem DbFunctions.CreateTime funkcję

var db0010016 = _idb0010016Rep.FindBy(e => DbFunctions.CreateTime(e.ExecutionTime.Hour, e.ExecutionTime.Minute, e.ExecutionTime.Second) == exetime).FirstOrDefault(); 

powyżej ex exetime() jest timespan.still otrzymuję poniżej błędu

{"Invalid parameter binding\r\nParameter name: ParameterName"} 
+1

Jaki błąd pojawia się po uruchomieniu pierwszego kodu? –

+0

Wygląda na duplikat https://stackoverflow.com/questions/39822588/how-to-convert-datetime-to-timespan-in-entity-framework-query?rq=1. Sprawdź tę odpowiedź, ponieważ jest lepsza niż konwersja rzeczy na ciągi! – user2612030

Odpowiedz

1

powodu Data-i czasowych problemów z wyroczni, robimy to tylko ciąg:

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())); 
} 

W źródle można dodać DateTime-parsowania-PROPERTY:

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"); 
     } 
    } 
} 

Nie najpiękniejsza wersja, ale działa.

Jest to problem oparty na rozwiązaniu Oracle w funkcjach DbFunctions. Jeśli aktywujesz dziennik sql-log, zobaczysz, że używana jest funkcja "CREATETIME()", która jest nieznana.

Aktywacja sql-log: ctx.Database.Log = Console.WriteLine;

Rejestr będzie wyglądać następująco:

SELECT * 
    FROM "ExecutionTimes" "Extent1" 
WHERE ((((CREATETIME (EXTRACT (HOUR FROM (CAST (
[...] 
0

Czy można przekształcić zarówno okres czasu oracle, jak i SQL Data w łańcuchu, a następnie porównać go. Jak:

var db0010016 = _idb0010016Rep.GetAll().Where(e => e.ExecutionTime.TimeOfDay.ToString() == viewmodel.ExecutionTime.ToString()).FirstOrDefault() 
+0

Możesz używać równań do porównywania ciągów znaków – ceferrari

+0

Używanie porównywania ciągów na datach nie działa jednak ... –

1

jeśli chcesz porównać tylko czas można użyć obsługiwane EDM funkcje kanoniczne z DbFunctions (patrz here). Niestety DbFunction.CreateTime nie jest obsługiwany.

Mimo to, jeśli jesteś zainteresowany w porównaniu na przykład na poziomie sekund, można zrobić coś takiego:

 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(); 
     } 

Tu wziąć różnicę w kilka sekund za pomocą DiffSeconds funkcji EDM i uczynić moduł o liczbie sekund w ciągu dnia.

Kwerenda, która zostanie wykonana jest:

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))) 

Jak widać to przekłada się poprawnie do funkcji Oracle po stronie serwera.

Mam nadzieję, że to pomaga,

Nicola

1

zapytania EF6 tłumacz nie obsługuje DateTime.TimeOfDay i dostawca Oracle nie obsługuje DbFunctions.CreateTime i TimeSpan parametry/stałe.

Nadal istnieje kilka opcji przed przełączeniem pamięci z DateTime na string zgodnie z sugestią innej odpowiedzi.

Po pierwsze, dla kontroli równości można porównać komponenty momencie przez każdą wydobywania ich w oddzielnych zmiennych (parametry zapytań):

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(); 

lub do fałszywej DateTime zmiennym (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(); 

Po drugie, możesz pracować z czasem konwertowanym na sekundy. Pozwala to również wykonać dowolne porównanie:

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(); 

Ale robienie tego ręcznie jest dość denerwujące i podatne na błędy. Co mogę zaoferować jest s mały klasy narzędzie metoda dostarczaniu niestandardowych rozszerzeń:

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; 
    } 
} 

Wykorzystuje dwa małe zwyczaj ExpressionVisitor klas do konwersji DateTime.TimeOfDay własności i TimeSpan klasy właściwości podobne do viewModel.ExecutionTime.

Teraz powinieneś używać oryginalnego kwerendy tak:

var db0010016 = _idb0010016Rep.GetAll() 
    .Where(e => e.ExecutionTime.TimeOfDay == viewmodel.ExecutionTime) 
    .ConvertTimeStamps() // the magic happens here 
    .FirstOrDefault(); 

W przypadku, gdy chcesz użyć milisekund zamiast sekund, co potrzebne jest, aby zmienić pierwsze dwa oświadczenia w TimeSpanConverter klasy następująco:

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;