简体   繁体   中英

Entity Framework: setting navigation objects/foreign keys outside of the context

I'm new to Entity Framework and I've been struggling for over a day with an issue regarding navigation objects. I suspect I'm trying to work with EF in a way that just isn't suitable. I'm using framework 4 and EF v4.

I have created an example model to roughly demonstrate the problem I'm having. My actual system is an ASP.Net application. We have a page that allows users to create "DisplayBoards" and add one or more pre-existing "Bays" to it. Before saving, the user has the opportunity to jiggle the order in which the Bays are shown. This is recorded on the link record.

Because the link records are shown in a list ( in order ), at the point of selecting each bay I've been creating a new link object, adding the bay and then using the list of link objects to bind to a grid. This works fine.

The problem occurs on the final postback when attempting to add the link objects to a new DisplayBoard and save everything.

When calling "AddObject" on the new link objects it throws the exception: "The EntityKey property can only be set when the current value of the property is null."

Here's the basic table structure:

DisplayBoard
DisplayBoardID (PK, identity)
DisplayBoardName

BoardBayLink
LinkID (PK, identity)
DisplayBoardID (FK)
BayID (FK)
DisplayOrder (int)

Bay
BayID (PK, identity)
BayName

Here is some example code. It's a console app that uses three contexts, but that's to simulate the postbacks on an ASP.Net app.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace EF_Test
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Entity Framework Issue");
            Console.WriteLine("Setting FK outside of context");
            Console.WriteLine();

            DisplayBoard newBoard = null;
            Bay existingBayOne = null;
            Bay existingBayTwo = null;
            List<BoardBayLink> newLinksList = new List<BoardBayLink>();

            /* In this example there are two contexts.
             * This is to simulate an ASP.Net scenario...
             *      First, a new DisplayBoard is created at UI level.
             *      A Bay is searched for and selected via callback or postback.
             *      A new BoardBayLink is created at UI level and the Bay record is added.
             *      Once user is happy, final postback to save.
             *      
             * This is why there are two contexts here in a console app.
             */

            // New display board created
            newBoard = new DisplayBoard() { DisplayBoardName = "My new display board" };

            // User searches for and selects a Bay (via postback)
            using (EntityFrameworkTestEntities context = new EntityFrameworkTestEntities())
            {
                existingBayOne = context.Bays.Where(bay => bay.BayID == 1).FirstOrDefault();
            }
            // Now that the user has selected a bay, create a new BoardBayLink object and add the Bay to it.
            newLinksList.Add(new BoardBayLink() { Bay = existingBayOne, DisplayOrder = 1 });


            // User searches for and selects another Bay (via postback)
            using (EntityFrameworkTestEntities context2 = new EntityFrameworkTestEntities())
            {
                existingBayTwo = context2.Bays.Where(bay => bay.BayID == 2).FirstOrDefault();
            }
            // Now that the user has selected a second bay, create a new BoardBayLink object and add the Bay to it.
            newLinksList.Add(new BoardBayLink() { Bay = existingBayTwo, DisplayOrder = 2 });


            // Show the new board with it's newly added Bays in the UI
            // User has not yet clicked save.
            Console.Write("Display board: ");
            Console.WriteLine(newBoard.DisplayBoardName);

            Console.WriteLine("Bays: ");
            foreach (BoardBayLink link in newLinksList.OrderBy(link => link.DisplayOrder))
            {
                Console.WriteLine(link.Bay.BayName);
            }


            Console.WriteLine("Press any key to continue with save....");
            Console.ReadKey();

            // Now use a third context (because we're pretending this is a postback)
            using (EntityFrameworkTestEntities context3 = new EntityFrameworkTestEntities())
            {
                context3.DisplayBoards.AddObject(newBoard); // Add the new board to the context

                foreach (BoardBayLink link in newLinksList)
                {
                    context3.BoardBayLinks.AddObject(link);
                    newBoard.BoardBayLinks.Add(link);
                }

                context3.SaveChanges();
            }

            Console.WriteLine("Press any key to exit...");
            Console.ReadKey();
        }
    }
}

How can I add the bay to the link record and avoid the error message I'm seeing?

Thanks

You can't add the link again, you already created the relationship outside the context with

newLinksList.Add(new BoardBayLink() { Bay = ... };

Trying to add it again with newBoard.BoardBayLinks.Add(link) will cause an error.

To add the new links you can just do the following in your third context:

using (EntityFrameworkTestEntities context3 = new EntityFrameworkTestEntities())
{
    context3.DisplayBoards.Add(newBoard);
    context3.SaveChanges();
}

Calling the Add() method will mark the EntityState of all instances inside the graph as Added which in turn will create a new database record for each.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM