If you create a context with Entities cxt = new Entities()
in your controller you create a new ctx
instance with each web request, especially you have then different context instances for the two wizard forms that select the regions and that select the tags. Therefore cxt.Regions.Single(...)
and cxt.Tags.Single(...)
retrieve Region
and Tag
entities with two different contexts and you add those entities to the request
object graph. When you later add this request
object with cxt.Request.Add(request)
(probably even in a third ctx
instance) EF notices that the related Region
s and Tag
s have references to different contexts (usually the case if they are proxy objects due to some virtual
properties) which is forbidden and the root of the exception.
It might be possible to solve the problem when you disable proxy creation before you load the regions and tags and then reattach the entities to the final context before you save. However, this is a very hacky solution in my opinion. I would very much prefer to hold anything else but entities in the Session
object (for example some simple data objects or view models that just represent the data that have been posted back from the wizard forms) to avoid problems like this and then convert those data into entities only in the final action that saves to the database.
For example, store the needed data in three session values - one for each wizard form:
Session["request"] = data.SomeRequestData; // your basic request info
Session["regions"] = data.Regions; // your collection of region IDs
Session["tags"] = data.Tags; // your collection of tag IDs
And only in the final action you put the pieces together to an entity and you do it in a single context instance:
var request = new Request();
var requestData = Session["request"] as SomeRequestDataModel;
request.Property1 = requestData.Property1;
request.Property2 = requestData.Property2;
// etc., just copy the properties
long val;
var regionIDs = Session["regions"] as IEnumerable<string>;
foreach (var item in regionIDs)
{
val = Convert.ToInt64(item);
request.Regions.Add(cxt.Regions.Single(r => r.Id == val)); // or .Find(val)
}
var tagIDs = Session["tags"] as IEnumerable<string>;
foreach (var item in tagIDs)
{
val = Convert.ToInt64(item);
request.Regions.Add(cxt.Tags.Single(r => r.Id == val)); // or .Find(val)
}
cxt.Request.Add(request);
cxt.SaveChanges();
(I've left out the checks for null
of Session["XXX"]
for simplicity.) With this procedure you can be sure that all entities that are part of the final object graph are attached to the same context instance ctx
and your exception won't occur.