代码优先实体框架无法更新多个相关实体

时间:2015-05-26 11:10:22

标签: c# entity-framework

我在TicketsTags之间存在多对多关系,可以在没有问题的情况下为故障添加标签。但是,我无法更新/删除故障单中的标记。

测试

[TestMethod]
        public void TagRepository_UpdateTicketWithTwoTagsToNone_TicketHasZeroTag()
        {
            // Arrange
            var ticketId = new Guid("54E86203-71F9-E411-80E5-000C29193DF7");
            var selectedTags = "";

            using (var context = new TicketModelContext())
            {
                using (new TransactionScope())
                {
                    var ticketToUpdate = context.Tickets.Include(t=>t.Tags).First(t => t.TicketId == ticketId);
                    Assert.AreEqual(0, ticketToUpdate.Tags.Count);

                    ticketToUpdate.Tags.Add(context.Tags.Find(new Guid("D1757675-A06C-4C1F-9DAD-03EE00BB1100")));
                    ticketToUpdate.Tags.Add(context.Tags.Find(new Guid("96C66A97-9C3E-4B15-BD70-A4C832EEDE8B")));
                    context.SaveChanges();

                    var setupTicket = context.Tickets.Single(t => t.TicketId == ticketId);
                    Assert.AreEqual(2, setupTicket.Tags.Count);


                    // Act
                    new TagRepository().UpdateTicketTags(ticketId, selectedTags);

                    // Assert
                    var updatedTeicket = context.Tickets.Include(t => t.Tags).First(t => t.TicketId == ticketId);
                    Assert.AreEqual(0, updatedTeicket.Tags.Count);
                    // HERE I EXPECT 0 BUT GET 2
                }
            }
        }

存储库方法

public void UpdateTicketTags(Guid ticketId, string selectedTags)
        {
            var tags = new List<Tag>();

            using (var context = new TicketModelContext())
            {
                if (!String.IsNullOrEmpty(selectedTags))
                {
                    foreach (var selectedTag in selectedTags.Split(','))
                    {
                        tags.Add(context.Tags.Find(new Guid(selectedTag)));
                    }
                }

                var ticketToUpdateTags = context.Tickets.Find(ticketId);

                context.Entry(ticketToUpdateTags).Collection(t => t.Tags).Load();

                ticketToUpdateTags.Tags = tags; // I EXPECT TAGS TO BE EMPTY IN THIS TEST
                context.SaveChanges();
            }
        }

命令context.Database.Log = s => System.Diagnostics.Debug.WriteLine(s);

输出的SQL
DELETE [dbo].[TagTickets]
WHERE (([Tag_TagId] = @0) AND ([Ticket_TicketId] = @1))


-- @0: '96c66a97-9c3e-4b15-bd70-a4c832eede8b' (Type = Guid)

-- @1: '54e86203-71f9-e411-80e5-000c29193df7' (Type = Guid)

-- Executing at 26/05/2015 13:16:39 +01:00

-- Completed in 24 ms with result: 1



DELETE [dbo].[TagTickets]
WHERE (([Tag_TagId] = @0) AND ([Ticket_TicketId] = @1))


-- @0: 'd1757675-a06c-4c1f-9dad-03ee00bb1100' (Type = Guid)

-- @1: '54e86203-71f9-e411-80e5-000c29193df7' (Type = Guid)

-- Executing at 26/05/2015 13:16:39 +01:00

-- Completed in 21 ms with result: 1

我花了好几个小时尝试使用不同测试的不同代码变体,并且都有相同的结果。任何帮助将不胜感激!

3 个答案:

答案 0 :(得分:1)

基本上,您的测试不起作用的原因是因为未反映在实体框架中的不同上下文中发生的更改。即使您使用了TransactionScope,如果在内部上下文中调用SaveChanges()(发生在UpdateTicketTags()中),那么这些更改将不会反映在外部上下文中。

我通常更喜欢将操作分解为较小的块而不是一个长的上下文,所以

  • 它模拟了应用程序中真实动作的发生方式(例如,如果这是一个ASP.NET MVC应用程序,多个控制器正在处理多个请求,可能会也可能不会修改同一个表)和
  • 它为我提供了每个对象的最新信息。

    [TestMethod]
    public void TagRepository_UpdateTicketWithTwoTagsToNone_TicketHasZeroTag()
    {
        // Arrange
        var ticketId = new Guid("54E86203-71F9-E411-80E5-000C29193DF7");
        var selectedTags = "";
    
        using (var context = new TicketModelContext())
        {
            var ticketToUpdate = context.Tickets.Include(t=>t.Tags).First(t => t.TicketId == ticketId);
            Assert.AreEqual(0, ticketToUpdate.Tags.Count);
    
            ticketToUpdate.Tags.Add(context.Tags.Find(new Guid("D1757675-A06C-4C1F-9DAD-03EE00BB1100")));
            ticketToUpdate.Tags.Add(context.Tags.Find(new Guid("96C66A97-9C3E-4B15-BD70-A4C832EEDE8B")));
            context.SaveChanges();
    
            var setupTicket = context.Tickets.Single(t => t.TicketId == ticketId);
            Assert.AreEqual(2, setupTicket.Tags.Count);
        }
    
        // Act
        new TagRepository().UpdateTicketTags(ticketId, selectedTags);
    
        using (var context = new TicketModelContext())
        {
            // Assert
            var updatedTeicket = context.Tickets.Include(t => t.Tags).First(t => t.TicketId == ticketId);
            Assert.AreEqual(0, updatedTeicket.Tags.Count);
        }
    }
    

答案 1 :(得分:0)

嗯我不确定你得到了什么样的错误,但我猜你在web.config中将MultipleActiveResultSets设置为false。尝试将此添加到web.config MultipleActiveResultSets = True到您的连接字符串。这将使EF能够在检索数据时进行查询等。从我的观点来看,您的代码看起来很好。

“多个活动结果集(MARS)是一种与SQL Server一起使用的功能,允许在单个连接上执行多个批处理。” https://msdn.microsoft.com/en-us/library/h32h3abf%28v=vs.110%29.aspx

答案 2 :(得分:0)

我重新构建了我的测试代码,以便在数据库上下文中删除存储库调用,并将我的事务范围移到外面以实现我所需要的。主要学习点是don't try and make changes to the database inside a context by calling another context

[TestMethod]
        public void TagRepository_UpdateTicketWithTwoTagsToNone_TicketHasZeroTag()
        {
            // Arrange
            var ticketId = new Guid("54E86203-71F9-E411-80E5-000C29193DF7");
            var selectedTags = "";

            using (new TransactionScope())
            {
                using (var context = new TicketModelContext())
                {

                    var ticketToUpdate = context.Tickets.Include(t => t.Tags).First(t => t.TicketId == ticketId);
                    Assert.AreEqual(0, ticketToUpdate.Tags.Count);

                    ticketToUpdate.Tags.Add(context.Tags.Find(new Guid("D1757675-A06C-4C1F-9DAD-03EE00BB1100")));
                    ticketToUpdate.Tags.Add(context.Tags.Find(new Guid("96C66A97-9C3E-4B15-BD70-A4C832EEDE8B")));
                    context.SaveChanges();

                    var setupTicket = context.Tickets.Single(t => t.TicketId == ticketId);
                    Assert.AreEqual(2, setupTicket.Tags.Count);
                }

                // Act
                new TagRepository().UpdateTicketTags(ticketId, selectedTags);

                // Assert
                using (var context2 = new TicketModelContext())
                {
                    var updatedTeicket = context2.Tickets.Include(t => t.Tags).First(t => t.TicketId == ticketId);
                    Assert.AreEqual(0, updatedTeicket.Tags.Count);
                }
            }
        }