2009-02-13 6 views
7

La mia tabella ha una colonna timestamp denominata "RowVer" che LINQ esegue il mapping per digitare System.Data.Linq.Binary. Questo tipo di dati sembra inutile perché (a meno che non mi manca qualcosa) Non riesco a fare le cose in questo modo:Interrogazione di una colonna timestamp da LINQ a SQL

// Select all records that changed since the last time we inserted/updated. 
IEnumerable<UserSession> rows = db.UserSessions.Where 
(usr => usr.RowVer > ????); 

Così, una delle soluzioni che sto guardando è quello di aggiungere un nuovo "calcolata colonna" chiamato RowTrack che è definito in SQL come questo:

CREATE TABLE UserSession 
(
RowVer timestamp NOT NULL, 
RowTrack AS (convert(bigint,[RowVer])), 
-- ... other columns ... 
) 

questo mi permette di interrogare il database come voglio:

// Select all records that changed since the last time we inserted/updated. 
IEnumerable<UserSession> rows = db.UserSessions.Where 
(usr => usr.RowTrack > 123456); 

E 'un brutto modo di fare le cose? Quanto performante sta interrogando su una colonna calcolata? C'è una soluzione migliore?

Inoltre, sto sviluppando contro Sql Server 2000 per la massima compatibilità con le versioni precedenti, ma posso dire al capo di fare del 2005 il minimo comune denominatore.

risposta

4

// Seleziona tutti i record modificati dall'ultima volta che abbiamo inserito/aggiornato.

C'è una soluzione migliore?

Perché non hanno due colonne, una per createddate un altro per LastModifiedDate. Direi che è il modo più tradizionale per gestire questo scenario.

+0

Grazie per il tuo post. Mi ha fatto riflettere sul problema più per rendermi conto che un datetime è molto meglio perché rappresenta un'informazione più utile. Ora riesco a rilevare _come_ stantio un record è, non solo se è stantio. –

+0

non dimenticare di aggiungere un indice per loro –

6

Il "timestamp" di SQL Server è solo un indicatore del fatto che il record è stato modificato, non è in realtà una rappresentazione di data/ora. (Anche se si suppone di incrementare ogni volta che viene modificato un record nel DB,

Attenzione che verrà ripristinato a zero (non molto spesso, ammettiamolo), quindi l'unico test di sicurezza è se il valore è cambiato, non se è maggiore di qualche valore precedente arbitrario

È possibile passare il valore della colonna TimeStamp a un modulo Web e quindi, quando viene inviato, vedere se TimeStamp dal modulo è diverso dal valore nel record corrente, se è diverso qualcun altro è cambiato & salvato il record nel frattempo.

+0

se hai solo bisogno di verificare se il valore è cambiato, puoi confrontare le istanze binarie con "a == b" – Lucas

+0

Grazie per il tuo post. Ora ho una migliore comprensione del perché non dovrei provare a ri-utilizzare la mia colonna RowVer. –

+1

In realtà la colonna timestamp è abbastanza pratica per questo scopo. Non devi preoccuparti del fatto che si esegua il wrapping a meno che non stai apportando più di 2^64 modifiche a una singola tabella. Per riferimento, ci vorrebbero quasi 600.000 anni a 1.000.000 di scritture al secondo. – Josh

8

AS Diego Frata contorni in this post c'è un hack t hat consente di interrogare i timestamp da LINQ.

Il trucco è quello di definire un Confronta metodo che accetta due System.Data.Linq.Binary parametri

public static class BinaryComparer 
{ 
public static int Compare(this Binary b1, Binary b2) 
{ 
throw new NotImplementedException(); 
} 
} 

Si noti che la funzione non ha bisogno di essere attuato, solo il suo nome (Confronta) è importante.

e la query sarà simile:

Binary lastTimestamp = GetTimeStamp(); 
var result = from job in c.GetTable<tblJobs> 
      where BinaryComparer.Compare(job.TimeStamp, lastTimestamp)>0 
      select job; 

(Questo in caso di lavoro.Timestamp> lastTimestamp)

EDIT: Vedere Rory MacLeod's answer per un'implementazione del metodo, se avete bisogno di lavorare fuori di SQL.

+0

Anche in Entity Framework (4.3.1), utilizzare Byte [] invece di Binary per il comparatore. –

+0

I collegamenti al profilo e al blog di Diego non funzionano più. –

2

A seguito di jaraics' answer, si potrebbe anche fornire un'implementazione per il metodo Compare che consenta di lavorare al di fuori di una query:

public static class BinaryExtensions 
{ 
    public static int Compare(this Binary b1, Binary b2) 
    { 
     if (b1 == null) 
      return b2 == null ? 0 : -1; 

     if (b2 == null) 
      return 1; 

     byte[] bytes1 = b1.ToArray(); 
     byte[] bytes2 = b2.ToArray(); 
     int len = Math.Min(bytes1.Length, bytes2.Length); 
     int result = memcmp(bytes1, bytes2, len); 

     if (result == 0 && bytes1.Length != bytes2.Length) 
     { 
      return bytes1.Length > bytes2.Length ? 1 : -1; 
     } 

     return result; 
    } 

    [DllImport("msvcrt.dll")] 
    private static extern int memcmp(byte[] arr1, byte[] arr2, int cnt); 
} 

L'uso di memcmp è stato preso da this answer a una domanda sul confronto array di byte. Se gli array non hanno la stessa lunghezza, ma l'array più lungo inizia con gli stessi byte dell'array più corto, l'array più lungo è considerato maggiore di quello più corto, anche se i byte aggiuntivi sono tutti zero.