Because these two lines ...
EmployeeService es = new EmployeeService();
CityService cs = new CityService();
... don't take a parameter in the constructor, I guess that you create a context within the classes. When you load the city1
...
Payroll.Entities.City city1 = cs.SelectCity(...);
...you attach the city1
to the context in CityService
. Later you add a city1
as a reference to the new Employee
e1
and add e1
including this reference to city1
to the context in EmployeeService
. As a result you have city1
attached to two different context which is what the exception complains about.
You can fix this by creating a context outside of the service classes and injecting and using it in both services:
EmployeeService es = new EmployeeService(context);
CityService cs = new CityService(context); // same context instance
Your service classes look a bit like repositories which are responsible for only a single entity type. In such a case you will always have trouble as soon as relationships between entities are involved when you use separate contexts for the services.
You can also create a single service which is responsible for a set of closely related entities, like an EmployeeCityService
(which has a single context) and delegate the whole operation in your Button1_Click
method to a method of this service.
Steps to reproduce can be simplified to this:
var contextOne = new EntityContext();
var contextTwo = new EntityContext();
var user = contextOne.Users.FirstOrDefault();
var group = new Group();
group.User = user;
contextTwo.Groups.Add(group);
contextTwo.SaveChanges();
Code without error:
var context = new EntityContext();
var user = context.Users.FirstOrDefault();
var group = new Group();
group.User = user; // Be careful when you set entity properties.
// Be sure that all objects came from the same context
context.Groups.Add(group);
context.SaveChanges();
Using only one EntityContext
can solve this. Refer to other answers for other solutions.
This is an old thread, but another solution, which I prefer, is just update the cityId and not assign the hole model City to Employee... to do that Employee should look like:
public class Employee{
...
public int? CityId; //The ? is for allow City nullable
public virtual City City;
}
Then it's enough assigning:
e1.CityId=city1.ID;
Alternatively to injection and even worse Singleton, you can call Detach method before Add.
EntityFramework 6: ((IObjectContextAdapter)cs).ObjectContext.Detach(city1);
EntityFramework 4: cs.Detach(city1);
There is yet another way, in case you don't need first DBContext object. Just wrap it with using keyword:
Payroll.Entities.City city1;
using (CityService cs = new CityService())
{
city1 = cs.SelectCity(Convert.ToInt64(cmbCity.SelectedItem.Value));
}
I had the same problem but my issue with the @Slauma's solution (although great in certain instances) is that it recommends that I pass the context into the service which implies that the context is available from my controller. It also forces tight coupling between my controller and service layers.
I'm using Dependency Injection to inject the service/repository layers into the controller and as such do not have access to the context from the controller.
My solution was to have the service/repository layers use the same instance of the context - Singleton.
Context Singleton Class:
Reference: http://msdn.microsoft.com/en-us/library/ff650316.aspx
and http://csharpindepth.com/Articles/General/Singleton.aspx
public sealed class MyModelDbContextSingleton
{
private static readonly MyModelDbContext instance = new MyModelDbContext();
static MyModelDbContextSingleton() { }
private MyModelDbContextSingleton() { }
public static MyModelDbContext Instance
{
get
{
return instance;
}
}
}
Repository Class:
public class ProjectRepository : IProjectRepository
{
MyModelDbContext context = MyModelDbContextSingleton.Instance;
[...]
Other solutions do exist such as instantiating the context once and passing it into the constructors of your service/repository layers or another I read about which is implementing the Unit of Work pattern. I'm sure there are more...
In my case, I was using the ASP.NET Identity Framework. I had used the built in UserManager.FindByNameAsync
method to retrieve an ApplicationUser
entity. I then tried to reference this entity on a newly created entity on a different DbContext
. This resulted in the exception you originally saw.
I solved this by creating a new ApplicationUser
entity with only the Id
from the UserManager
method and referencing that new entity.
I had the same problem and I could solve making a new instance of the object that I was trying to Update. Then I passed that object to my reposotory.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With