2017-04-11 1 views
3

In diesem Unittest werde ich überprüfen, dass das MD5 der Content-Byte-Spalte berechnet, persistent ist und korrekt abgerufen wird.Entity Framework Kontext 6.1.3 nicht aktualisiert/zerstört?

Es scheint jedoch, dass der Entity Framework (6.1.3) -Kontext nicht aktualisiert/zerstört wird, da nach dem rohen SQL-UPDATE eindeutig, aber nicht beim Abrufen der Zeile mit einem neuen Kontext wirksam wird.

namespace UnitTests 
{ 
    [TestClass] 
    public class TestDataPacketServiceDebug 
    { 
     [TestInitialize] 
     public void Setup() 
     { 
      CommonMethods.ResetDatabase(); 
      try 
      { 
       CommonMethods.ResetDataPacketDirectory(); 
      } 
      catch (DirectoryNotFoundException) 
      { 
      } 
     } 

     [TestCategory("DataPacketService"), TestMethod] 
     public void TestGetLocalFilePathDebug() 
     { 
      // Persist a DataPacket 
      int dataPacketId; 
      using (var testDBContext = new TestDBContext()) 
      { 
       DataPacket dataPacket = new DataPacket 
       { 
        Content = File.ReadAllBytes(@"Resources\SampleResources.zip"), 
        Description = "DataPacketSample consist of some random found .DLL files on disk", 
        Name = "SampleResources", 
        Version = "1" 
       }; 
       testDBContext.DataPackets.Add(dataPacket); 
       testDBContext.SaveChanges(); 
       dataPacketId = dataPacket.DataPacketId; 
      } 

      // Verify file path extraction 
      using (var testDBContext = new TestDBContext()) 
      { 
       DataPacket dataPacket = DataPacketService.GetByNameAndVersion("SampleResources", "1", 
        testDBContext); 

       string extractedFilePath = DataPacketService.GetLocalFilePath(testDBContext, 
        dataPacket, "EntityFramework.dll"); 

       string validDestinationPath = String.Format(@"{0}\DataPackets\{1}_v{2}\EntityFramework.dll", 
        AppDomain.CurrentDomain.BaseDirectory, dataPacket.Name, dataPacket.Version); 

       Assert.AreEqual(validDestinationPath, extractedFilePath); 

       if (File.Exists(extractedFilePath) == false) 
       { 
        Assert.Fail("SampleResources was not extracted correctly"); 
       } 
      } 
      // When setting a breakpoint here and take a look with external SQL Browser 
      // (e.g. Microsoft SQL Server Management Studio), following is in order: 
      // Note! Not all columns are shown 
      // ----------------------------------------------------------------------------------------------- 
      // DataPacketId | Name   | RowVersion | Content  | MD5      | Version 
      //   1 | SampleResources | NULL  | 0x504B03... | 2zSV8IChaiyf0UfnezDHKg== | 1 


      // Manually modify MD5 field in database for MD5 verification 
      using (var testDBContext = new TestDBContext()) 
      { 
       string sqlUpdate = String.Format("UPDATE dbo.DataPackets SET MD5 = 'another_MD5' WHERE DataPacketId = {0}", 
        dataPacketId); 
       testDBContext.Database.ExecuteSqlCommand(sqlUpdate); 
      } 
      // When setting a breakpoint here we can clearly see that the row has been changed: 
      // Note! Not all columns are shown 
      // ---------------------------------------------------------------------------------- 
      // DataPacketId | Name   | RowVersion | Content  | MD5   | Version 
      //   1 | SampleResources | NULL  | 0x504B03... | another_MD5 | 1 

      // Verify MD5 
      using (var testDBContext = new TestDBContext()) 
      { 
       // Fetch dataPacket with modified MD5 
       DataPacket dataPacket = DataPacketService.GetByNameAndVersion("SampleResources", "1", testDBContext); 

       // Verify that the raw SQL command has been successful: 
       Assert.AreEqual("another_MD5", dataPacket.MD5); 
       // BANG!!!!!!!!!!!!!! 
       // Result Message: Assert.AreEqual failed. Expected:<another_MD5>.Actual:<2zSV8IChaiyf0UfnezDHKg==>. 
      } 
     } 
    } 
} 

Entity:

public class DataPacket 
{ 
    /// <summary> 
    /// Identifier 
    /// </summary> 
    public int DataPacketId { get; set; } 

    /// <summary> 
    /// Concurrency Token 
    /// </summary> 
    public byte[] RowVersion { get; set; } 

    /// <summary> 
    /// Name 
    /// </summary> 
    public string Name { get; set; } 

    /// <summary> 
    /// Description of data packet 
    /// </summary> 
    public string Description { get; set; } 

    /// <summary> 
    /// Version of data packet 
    /// </summary> 
    public string Version { get; set; } 

    /// <summary> 
    /// MD5 of the data packet (i.e. MD5 of Content byte array) 
    /// </summary> 
    public string MD5 { get; private set; } 

    private byte[] content; 

    /// <summary> 
    /// Byte content of the data packet (i.e. 
    /// </summary> 
    public byte[] Content 
    { 
     get { return content; } 
     set 
     { 
      content = value; 
      UpdateMD5(); 
     } 
    } 

    /// <summary> 
    /// TestCase navigation DataPacket <== One-To-Many ==> TestCases 
    /// </summary> 
    public ICollection<TestCase> TestCases { get; set; } // DataPacket <== One-To-Many ==> TestCases 

    /// <summary> 
    /// Update MD5 checksum depending on content 
    /// </summary> 
    private void UpdateMD5() 
    { 
     if (content != null) 
     { 
      this.MD5 = GetMD5ForBytes(content); 
     } 
    } 

    /// <summary> 
    /// Get MD5 checksum for content byte array 
    /// </summary> 
    /// <param name="content">Content byte array</param> 
    /// <returns>MD5 checksum</returns> 
    public static String GetMD5ForBytes(byte[] content) 
    { 
     if (content != null) 
     { 
      System.Security.Cryptography.MD5 md5Object = System.Security.Cryptography.MD5.Create(); 
      return System.BitConverter.ToString(md5Object.ComputeHash(content)).Replace("-", ""); 
     } 

     return null; 
    } 
} 

GetByNameAndVersion

public static DataPacket GetByNameAndVersion(string name, string version, TestDBContext testDBContext) 
     { 
      IQueryable<DataPacket> query = testDBContext.Set<DataPacket>(); 
      query = query.Where(t => t.Name == name).Where(t => t.Version == version); 
      return query.Single(); 
     } 

Hinweis! Ich verwende eine lokale Datenbank.

+1

Ich denke nichts falsch mit EF, höchstwahrscheinlich irgendwo in Ihrer 'DataPacketService.GetByNameAndVersion' Implementierung, zum Beispiel MD5 neu berechnet anstatt aus db etc zu lesen, oder zum Beispiel Ihre Tests haben einige Probleme mit Transaktionen etc - schwer zu sage ohne ganzes Bild. – Lanorkin

+0

Müssen uns GetByNameAndVersion Code tatsächlich zeigen. – Evk

+0

Danke, ich habe die Frage aktualisiert =) – kungcc

Antwort

1

Es ist kein EF Kontext Problem (es funktioniert wie erwartet), aber inkorrekter Test/Logik in Ihrer DataPacket Klasse.

Sie haben zwei ähnliche Eigenschaften, abgebildet sowohl auf Datenbanktabellenspalten:

/// <summary> 
/// MD5 of the data packet (i.e. MD5 of Content byte array) 
/// </summary> 
public string MD5 { get; private set; } 

private byte[] content; 

/// <summary> 
/// Byte content of the data packet (i.e. 
/// </summary> 
public byte[] Content 
{ 
    get { return content; } 
    set 
    { 
     content = value; 
     UpdateMD5(); 
    } 
} 

Der # -Code Client C nur die Content eingestellt, die wiederum aktualisiert die MD5 - in Ordnung. Was passiert aber, wenn EF die Entität aus der Datenbank lädt? In der Tat verwendet es die gleichen Eigenschaften Setter (private ist kein Problem, weil EF Reflexion/Code-Generation verwendet, so dass es extern jede Art von Setter aufrufen kann).

Jetzt hängt alles von der Bestellung der Aufrufer Setter. In Ihrem Fall wird der MD5 zuerst angerufen, dann der Content. Da Ihr SQL-Befehl die MD5-Spalte aktualisiert, aber Content unverändert gelassen hat, setzt der erste Setter den Wert MD5 aus der Datenbank und der zweite Setter aktualisiert ihn wieder von der Content. Was natürlich dazu führt, dass die Behauptung einen Fehler meldet.

Es liegt an Ihnen zu entscheiden, ob die Aktualisierung der Spalte MD5 in der Datenbank über SQL gültig ist (die MD5 und Content sind im Prinzip nicht synchron). Die Reihenfolge des Aufrufs der Eigenschaften-Setter in undefined - derzeit, wenn Sie die MD5 Eigenschaftserklärung nach der Content Eigenschaft verschieben, wird der Test bestanden, aber es ist etwas, auf das Sie sich nicht verlassen können.

+1

Das klingt sehr vernünftig! Danke für die gute und einfache Erklärung =) Ich habe versucht, Bounty +50, aber es sagt, dass ich 17h warten muss, werde ich tun. – kungcc

Verwandte Themen