2017-04-11 46 views
3

在本單元測試中,我將驗證內容字節列的MD5是否被計算,保持並正確提取。實體框架上下文6.1.3未刷新/銷燬?

但是,似乎實體框架(6.1.3)上下文沒有刷新/銷燬,原因是原始SQL UPDATE明顯生效後,但在用新上下文獲取該行時未顯示。

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==>. 
      } 
     } 
    } 
} 

實體:

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

注意!我正在使用localDB數據庫。

+1

我不認爲EF有任何問題,最有可能在你的'DataPacketService.GetByNameAndVersion'實現中的某個地方,例如MD5重新計算而不是從db等讀取,或者例如你的測試在事務等方面有一些問題 - 很難說不完整的圖片。 – Lanorkin

+0

確實需要向我們顯示GetByNameAndVersion代碼。 – Evk

+0

謝謝,我已經更新了這個問題=) – kungcc

回答

1

這不是EF上下文問題(它按預期工作),但在DataPacket類中有錯誤的測試/邏輯。

你有兩個相關的屬性,都映射到數據庫表列:

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

客戶端的C#代碼只能設置Content這反過來又更新MD5 - 罰款。但是當EF從數據庫加載實體時會發生什麼?事實上,它使用相同的屬性設置器(因爲EF使用反射/代碼生成,所以它可以在外部調用任何類型的setter),因此private不是問題。

現在一切都取決於調用setters的訂單。在你的情況下,首先調用MD5,然後調用Content。由於您的SQL命令更新了MD5列,但未更改左側Content,所以第一個設置器將設置數據庫中的MD5值,第二個設置器將從Content更新它。當然,這導致斷言報告失敗。

由您決定是否通過SQL更新數據庫中的MD5列是有效操作(基本上使MD5Content不同步)。以未定義方式調用屬性設置器的順序 - 當前如果您在Content屬性後移動MD5屬性聲明,測試將會通過,但這是您不能依賴的。

+1

這聽起來很合理!感謝你的好和簡單的解釋=)試圖賞金+50,但它說,我必須等待17小時,將這樣做。 – kungcc