Non sono sicuro che sia ancora possibile, poiché mi sto semplicemente immergendo in Entity Framework 6. Stiamo migrando da Linq2Sql, ma abbiamo molte query basate sugli utenti nel nostro motore. Compiliamo dinamicamente del codice in base ai requisiti dell'utente e dobbiamo mantenere la compatibilità con le versioni precedenti.
Un esempio di una query dinamica che viene passata dal legacy Linq2Sql potrebbe essere simile a questa (ridotta a zero, questo è solo un esempio):
from item in context.SomeTableOrView
let sub = from sub1 in context.SomeTableOrView where
sub1.DataTimeStamp > item.DataTimeStamp.AddMinutes(60) &&
sub1.DataTimeStamp < item.DataTimeStamp.AddMinutes(300)
select posSub1 where
sub.Take(1).Any()
select item
Ovviamente, Entity Framework non ha alcun tipo di .AddMinutes(x)
ed è necessario utilizzare i nuovi metodi statici di DbFunctions. Non possiamo rompere il codice, quindi dobbiamo aggiornarlo. La prima soluzione che viene in mente, è solo per sostituire il testo dove tutto ciò che ha .AddMinutes(x)
, o .AddSeconds(x)
, o qualunque cosa facciamo intorno a DateTime
, e basta sostituire con le nuove funzioni ed essere fatto con esso . Questo è tutto pre-compilato, quindi tecnicamente funzionerebbe. Faccio solo schifo alle espressioni regolari. Lo accetterei volentieri come una risposta se qualcuno sa come farei comunque.
Ma vorrei capire come funziona EntityFramework riguardo ai metodi di estensione. Dal momento che il ritorno di DateTime.AddMinutes(x)
restituisce un nuovo DateTime
, potrei invece creare un metodo di estensione per restituire un'espressione che faccia l'equivalente di DbFunctions.AddMinutes(time, increment)
o qualcosa di creativo come quello? DbFunctions è statico, quindi non posso restituire Func<DbFunctions>
.
Pensieri / suggerimenti. Grazie!
Aggiornato - Ivan offre un'ottima risposta aggiornata per chi guarda questo. Meno errore incline alla risposta di sotto, e abbastanza liscio IMO. inserisci la descrizione del collegamento qui
Abbiamo scoperto che era più semplice applicare una patch al codice Linq2Sql in entrata. Abbiamo anche notato che DbFunctions.AddMinutes () accetta solo un Int32, dove DateTime.AddMinutes () accetta un doppio. Questo potrebbe potenzialmente rompere il comportamento previsto, quindi lo aggiustiamo anche. Usando alcune espressioni regolari e alcune semplici operazioni sulle stringhe, è risultato in questo codice patchato.
Questo potrebbe non funzionare per tutti, ma se stai passando da Linq2Sql e hai salvato query che seguono Linq2Sql e devi applicare le espressioni DateTime ... funzionerà per AddMintues, AddDays, AddHours, AddMilliseconds
private static string Linq2SqlConvertToEntityFramework(string originalQuery)
{
// Finds expressions with .Add____)
const string dateTimeAdditationPattern = @"(@?[a-z_A-Z]\w*(?:\.@?[a-z_A-Z]\w*)*).Add\s*.+?(?=\))\)";
// Finds all the matches
var matchces = Regex.Matches(originalQuery, dateTimeAdditationPattern);
// Enumerates all the matches, and creates a patch
foreach (Match m in matchces)
{
var inputValue = m.Value;
string valuePassed = inputValue.Between("(", ")").Trim();
string typeOfAddition = inputValue.Between(".Add", "(").Trim();
string dateTimeExpression = inputValue.Before(".Add").Trim();
// because DateTime.AddMinutes() (or any other AddXXX(Y) accepts a double, and
// DbFunctions only accepts an int,
// We must move this to milliseconds so we dont lose any expected behavior
// The input value could be an int or a input variable (such as a sub query)
var mutipler = 1;
switch (typeOfAddition)
{
case "Seconds":
mutipler = 1000;
break;
case "Minutes":
mutipler = 1000*60;
break;
case "Hours":
mutipler = 1000 * 60 * 60;
break;
case "Days":
mutipler = 1000 * 60 * 60 * 24;
break;
}
// new expression to work with Entity Framework
var replacementString = string.Format("DbFunctions.AddMilliseconds({0},(int)({1} * {2}))", dateTimeExpression, valuePassed, mutipler);
// Simple string replace for the input string
originalQuery = originalQuery.Replace(inputValue, replacementString);
}
return originalQuery;
}
/// <summary>
/// Get string value between [first] a and [last] b.
/// Credit Source: http://www.dotnetperls.com/between-before-after
/// </summary>
public static string Between(this string value, string a, string b)
{
int posA = value.IndexOf(a, StringComparison.InvariantCulture);
int posB = value.LastIndexOf(b, StringComparison.InvariantCulture);
if (posA == -1)
{
return "";
}
if (posB == -1)
{
return "";
}
int adjustedPosA = posA + a.Length;
if (adjustedPosA >= posB)
{
return "";
}
return value.Substring(adjustedPosA, posB - adjustedPosA);
}
/// <summary>
/// Get string value after [first] a.
/// Credit Source: http://www.dotnetperls.com/between-before-after
/// </summary>
public static string Before(this string value, string a)
{
int posA = value.IndexOf(a, StringComparison.InvariantCulture);
if (posA == -1)
{
return "";
}
return value.Substring(0, posA);
}
/// <summary>
/// Get string value after [last] a.
/// Credit Source: http://www.dotnetperls.com/between-before-after
/// </summary>
public static string After(this string value, string a)
{
int posA = value.LastIndexOf(a, StringComparison.InvariantCulture);
if (posA == -1)
{
return "";
}
int adjustedPosA = posA + a.Length;
if (adjustedPosA >= value.Length)
{
return "";
}
return value.Substring(adjustedPosA);
}
Entity Framework 6 è stupido in questo caso. Cerca semplicemente di ottenere l'attributo [DbFunction]
associato dal metodo e sostituisce il metodo con DbExpression
utilizzando la classe privata System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.Translator
. E così, non c'è modo di registrare il traduttore personalizzato da un codice esterno. Inoltre, .NET non offre la possibilità di collegare attributi ai costrutti del linguaggio in modo dinamico.
Pertanto, per risolvere questo problema, puoi eseguire una delle seguenti operazioni:
DbFunctions
nel codice sorgente ( ReSharper SSR è utile per questo); ExpressionVisitor
(e possibile IQueryProvider
) che sostituirà le chiamate di metodo con i metodi della classe DbFunctions
. ExpressionVisitor
(e possibile IQueryProvider
) che DbFunctionExpression
espressioni di chiamata del metodo in DbFunctionExpression
.