在此单元测试中,我将验证内容字节列的MD5是否已正确计算,保留和提取。
但是,似乎实体框架(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数据库。
答案 0 :(得分: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
会更新private
- 罚款。但是当EF从数据库加载实体时会发生什么?实际上,它使用相同的属性设置器(MD5
不是问题,因为EF使用反射/代码生成,因此它可以在外部调用任何类型的setter)。
现在一切都取决于调用setter的 order 。在您的情况下,首先调用Content
,然后调用MD5
。由于您的SQL命令更新了Content
列,但保持MD5
不变,因此第一个setter将从数据库中设置Content
值,第二个setter将从{{{}更新它。 1}}。这当然导致断言报告失败。
由您来决定是否通过SQL更新数据库中的MD5
列是有效操作(基本上使MD5
和Content
不同步)。在未定义的情况下调用属性setter的顺序 - 当前如果在MD5
属性之后移动Content
属性声明,测试将通过,但它是您不能依赖的东西。