Question

I'm trying to do something and here is a "demo", very simple example. (this demo make no sense but it give a general idea of what I'm trying to do)

i commented out what i have tried

is there a way to do this without creating a new context?

this is database first on sql server

c# code

using System;
using System.Data.Entity;
using System.Data.Entity.Core.Objects;
using System.Data.Entity.Infrastructure;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            //remove then add it back, without select from the db
            using (var db = new testing())
            {
                var lookup = new edmx.LookupTable() { LookupId = 1 };

                var main = new edmx.MainTable() { MainId = 10 };
                main.LookupTables.Add(lookup);

                db.LookupTables.Attach(lookup);

                db.MainTables.Attach(main);

                main.LookupTables.Remove(lookup);

                db.SaveChanges();

                //imagine this is a loop from n to m and it happen that you have to add back something that you just deleted
                lookup = new edmx.LookupTable() { LookupId = 1 };

                //crash if nothing

                //db.Entry(lookup).State = EntityState.Unchanged; //crash
                //db.LookupTables.Attach(lookup); //crash

                if (!db.context.IsAttachedTo(lookup))
                {
                    //db.Entry(lookup).State = EntityState.Unchanged; //crash
                    //db.LookupTables.Attach(lookup); //crash
                }

                bool isDetached = db.Entry(lookup).State == EntityState.Detached;
                if (isDetached)
                {
                    //db.Entry(lookup).State = EntityState.Unchanged; //crash
                    //db.LookupTables.Attach(lookup); //crash
                }

                main.LookupTables.Add(lookup);

                db.SaveChanges();
            }

            Console.ReadKey();
        }
    }

    public partial class testing : edmx.testingEntities
    {
        public testing()
            : base()
        {
            this.Database.Log = x => System.Diagnostics.Debug.Write(x);
        }
        public ObjectContext context
        {
            get { return (this as IObjectContextAdapter).ObjectContext; }
        }
    }

    public static class helper
    {
        public static bool IsAttachedTo(this ObjectContext context, object entity)
        {
            ObjectStateEntry entry;
            if (context.ObjectStateManager.TryGetObjectStateEntry(entity, out entry))
            {
                return (entry.State != EntityState.Detached);
            }
            return false;
        }
    }
}

SQL script

USE [testing]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[LookupTable](
    [LookupId] [int] NOT NULL,
 CONSTRAINT [PK_LookupTable] PRIMARY KEY CLUSTERED 
(
    [LookupId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[MainTable](
    [MainId] [int] NOT NULL,
 CONSTRAINT [PK_MainTable] PRIMARY KEY CLUSTERED 
(
    [MainId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[MainTable_LookupTable](
    [MainId] [int] NOT NULL,
    [LookupId] [int] NOT NULL,
 CONSTRAINT [PK_MainTable_LookupTable] PRIMARY KEY CLUSTERED 
(
    [MainId] ASC,
    [LookupId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
INSERT [dbo].[LookupTable] ([LookupId]) VALUES (1)
GO
INSERT [dbo].[MainTable] ([MainId]) VALUES (10)
GO
INSERT [dbo].[MainTable_LookupTable] ([MainId], [LookupId]) VALUES (10, 1)
GO
ALTER TABLE [dbo].[MainTable_LookupTable]  WITH CHECK ADD  CONSTRAINT [FK_MainTable_LookupTable_LookupTable] FOREIGN KEY([LookupId])
REFERENCES [dbo].[LookupTable] ([LookupId])
GO
ALTER TABLE [dbo].[MainTable_LookupTable] CHECK CONSTRAINT [FK_MainTable_LookupTable_LookupTable]
GO
ALTER TABLE [dbo].[MainTable_LookupTable]  WITH CHECK ADD  CONSTRAINT [FK_MainTable_LookupTable_MainTable] FOREIGN KEY([MainId])
REFERENCES [dbo].[MainTable] ([MainId])
GO
ALTER TABLE [dbo].[MainTable_LookupTable] CHECK CONSTRAINT [FK_MainTable_LookupTable_MainTable]
GO

solution, in my case, is this code

for (var i = 1; i < 3; ++i)
{
    lookup = db.LookupTables.Local.FirstOrDefault(x => x.LookupId == i);

    if (lookup == null)
    {
        lookup = new edmx.LookupTable() { LookupId = i };
        db.LookupTables.Attach(lookup);
    }

    main.LookupTables.Add(lookup);
}
Was it helpful?

Solution

The lookup instance (with id 1) is still attached to EF, the Remove you are calling is just removing the link between main (10) and lookup (1).

When you create the lookup instance again it's a different instance in .NET but for the database/EF it's actually a duplicate.

You should either re-use the previous lookup instance, or use the DbSet's Local property to find it again.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top