实体框架上下文6.1.3是否未刷新/销毁? [英] Entity Framework Context 6.1.3 not Refreshed / Destroyed?

查看:35
本文介绍了实体框架上下文6.1.3是否未刷新/销毁?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在本单元测试中,我将验证内容字节列的MD5是否已正确计算,持久化和获取.

In this unittest I will verify that the MD5 of the Content byte column is calculated, persisted and fetched correctly.

但是,似乎没有刷新/销毁Entity Framework(6.1.3)上下文,因为在原始SQL UPDATE明显生效后,但在使用新上下文获取行时未显示.

However, it seems that the Entity Framework (6.1.3) context is not refreshed / destroyed, since after the raw SQL UPDATE that clearly takes effect, but not shown when fetching the row with a new context.

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数据库.

Note! I am using a localDB database.

推荐答案

这不是EF上下文问题(它可以按预期工作),但是 DataPacket 类中的测试/逻辑不正确.

It's not a EF context issue (it works as expected), but incorrect test / logic in your DataPacket class.

您有两个相关的属性,都映射到数据库表列:

You have two related properties, both mapped to database table columns:

/// <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使用反射/代码生成,因此 private 没问题,因此它可以在外部调用任何类型的设置器).

The client C# code can only set the Content which in turn updates the MD5 - fine. But what happens when EF loads the entity from the database? Indeed, it uses the same property setters (the private is not a problem because EF uses reflection / code generation, so it can call externally any type of setter).

现在,一切都取决于呼叫设置者的顺序.在您的情况下,首先调用 MD5 ,然后调用 Content .由于您的SQL命令更新了 MD5 列,但未更改 Content ,因此第一个设置器将从数据库中设置 MD5 值,第二个设置器将设置设置者将从 Content 更新回来.当然,这是导致断言报告失败的原因.

Now everything depends on the order of calling setters. In your case the MD5 is called first, then the Content. Since your SQL command updated the MD5 column, but left Content unchanged, the first setter will set the MD5 value from the database, and the second setter will update it back from the Content. Which of course is causing the assertion to report failure.

由您决定是否通过SQL更新数据库中的 MD5 列是否有效(基本上不使用 MD5 Content 同步).以未定义的方式调用属性设置器的顺序-当前,如果将 MD5 属性声明移到 Content 属性之后,则测试将通过,但这是您不能依靠的.

It's up to you to decide whether updating the MD5 column in database through SQL is valid operation (basically leaving the MD5 and Content out of sync). The order of calling the property setters in undefined - currently if you move the MD5 property declaration after the Content property, the test will pass, but it's something you cannot rely on.

这篇关于实体框架上下文6.1.3是否未刷新/销毁?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆