2017-04-11 48 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