Entity Object Cannot Be Referenced by Multiple Instances of Ientitychangetracker. While Adding Related Objects to Entity in Entity Framework 4.1

entity object cannot be referenced by multiple instances of IEntityChangeTracker. while adding related objects to entity in Entity Framework 4.1

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.

An entity object cannot be referenced by multiple instances of IEntityChangeTracker. On multiple entries

Ultimately, this is the same problem mentioned here: How can I use EF to add multiple child entities to an object when the child has an identity key?

If you have a auto generated PK and attempt to do an Add after doing a previous udpate/delete on the same context, it will crash because you end up with the same PKs for multiple objects at the same time.

To fix this, you must either set all those WorkIds (I assume this is the PK) to -1, -2, -3 and their proper FK references to those new numbers

OR

You must add all new entities first before you do any updates/deletes since EF will realize that those IDs will be set in the future.

dbContext An entity object cannot be referenced by multiple instances of IEntityChangeTracker

The problem is NewInstallationBoms are attached in a context. But you try to add them in another context in AddInstallToDbCalcTotalCost.

Solution 1: You should use same context. Detailed answer

Solution 2: You should first detach them in first context and attach them in second context. Detailed answer.

Solution 3 (Credits to grek40): If you use AsNoTracking() in first context. It will not be tracked/cached. Therefore you can safely use it in second context.

In your stiuation it's better to follow solution 1. If you use dependency injection container, you can make your dbcontext as per request.
Other than this,you can manage lifetime of dbcontext on upper level or you can make your class disposable and manage db context in your class.

An entity object cannot be referenced by multiple instances of IEntityChangeTracker using code-first and EntityFramework 6 ,WFA

it seems like you get your Circular c = DG_C.CurrentRow.DataBoundItem as Circular; from some kind of a datagrid that's filled using a diffrent context than the one you use the get this Red_Circular red = context.Reds.SingleOrDefault(r => r.Circular__ID == c.ID_Circular);

try to use one global context in your class and then use that to fill your grid and get your Red_Circular

that sould solve it

Error:An entity object cannot be referenced by multiple instances of IEntityChangeTracker

I found two solutions:

The first was to use the exiting DbContext without casting:

This requires additional infrastructure to use a gereral DbContext instance or a specific PHSRP_DashboardDataModel based on the entity state

I don’t like it since it does not explain or correct the underlying problem, but it works.

PHSRP_DashboardDataModel _DBC = null;
DbContext _PDBC = null;
Boolean existingContext;

if (GetDbContextFromEntity(ActiveWorkSession)!=null) // is ActiveWorkSession still attached to a Dbcontext ?
{
_PDBC = GetDbContextFromEntity(ActiveWorkSession); // Get that Context
existingContext = true;
}
else
{
_DBC = new PHSRP_DashboardDataModel(); // Open new Context and attach
_DBC.EmployeeWorkSessions.Attach(ActiveWorkSession);
existingContext = false;
}

switch (lPunch.Type)
{
case PunchType.OutForShift:
{
// Remove END punch, set SessionEnd time to null
try
{
WorkEndRecord wsr = ActiveWorkSession.WorkEndRecords.First();

if (existingContext)
{
ActiveWorkSession.WorkEndRecords.Remove(wsr);
ActiveWorkSession.WorkEndDateTime_Actual = null;
_PDBC.SaveChanges();
}
else
{
_DBC.WorkEndRecords.Remove(wsr);
ActiveWorkSession.WorkEndDateTime_Actual = null;
_DBC.SaveChanges();
}

}
catch (Exception ex)
{
// handle exception
}

tcUser.UpdateClockState();
UpdateClockView();

break;
}

|
|
Other cases
|
|
}

Frank Fajardo’s question sent me looking for other references to the Database Context, and ultimately the second, better solution.

The task that this code is “undoing” is a clock-out. That’s where I found my prior, still attached, reference to ActiveWorkSession.

My reference to the Database context in the SessionClockOut method was not enclosed in a “using” block. As a test, I added a detach instruction- that worked. I then placed the _DBC in a “using” block. That allowed my original btn_UndoButton_Click code to work without needing to detach the ActiveWorkSession object from the Context:

private Boolean SessionClockOut(DateTime timeOUT)
{
Boolean success = false;
Boolean abort = false;

tcPunch newPunch = new tcPunch();

// Added 'using' statement
using (PHSRP_DashboardDataModel _DBC = new PHSRP_DashboardDataModel())
{
while (!success && !abort)
{
var wsUpdate = _DBC.EmployeeWorkSessions
.Where(w => (w.EmployeeWorkSessionID == ActiveWorkSession.EmployeeWorkSessionID))
.Include(w => w.WorkStartRecords)
.Include(w => w.WorkEndRecords)
.Include(w => w.WorkSessionBreaks)
.FirstOrDefault();

if (wsUpdate != null)
{
WorkEndRecord er = new WorkEndRecord();

try
{
wsUpdate.WorkEndDateTime_Actual = timeOUT;

er.EmployeeWorkSessionID = ActiveWorkSession.EmployeeWorkSessionID;
er.EditTimeStamp = DateTime.Now;
er.WorkEndDateTime_Official = timeOUT;
er.VarianceReasonID = VarianceReason.VR_None;
er.EditByID = ActiveWorkSession.EmployeeID;

wsUpdate.WorkEndRecords.Add(er);
_DBC.SaveChanges();

ActiveWorkSession = wsUpdate;
tcUser.UpdateClockState();
UpdateClockView();

// Tried this before adding the using statement. It also worked.
// _DBC.Entry(ActiveWorkSession).State = EntityState.Detached; // Release from DB Context
PreviousWorkSession = ActiveWorkSession; // needed For Undo Punch
ActiveWorkSession = null; // Worksession has ended
success = true;

}
catch (DbUpdateException e)
{
// handle exception...
abort = true;
throw;
}

newPunch.EmployeeID = wsUpdate.EmployeeID;
newPunch.WorksessionID = wsUpdate.EmployeeWorkSessionID;
newPunch.Type = PunchType.OutForShift;
newPunch.TimeStamp = er.EditTimeStamp;
if (tcUser.hasPriv(PrivilegeTokenValue.TC_UndoRecentPunch)) tcLastPunchUndo.StartUndoTimer(newPunch);
} // if
} // while

} // using

return success;
}

Being new to Entity Framework I don't yet understand all the issues related to working with the DbContext.

Entity Framework An entity object cannot be referenced by multiple instances of IEntityChangeTracker

The most likely cause of this is to do with any dependency injection tool you're using.

There should only be one DbContext in play per unit of work. If you are newing up a new one each time, make sure the old one is disposed of.

Otherwise, you will have multiple instances of the same context running alongside each other.

This is where the change tracker gets confused and is unable to track changes to your entities.

An entity object cannot be referenced by multiple instances of IEntityChangeTracker

You have a method Update in your BaseRepository which receives an entity, and somehow attach it to a DbContext which is created inside the constructor of this base repository.

You also have a GetById method which you call to fetch that user, add roles, and later pass to Update method.

The problem is that you have two service classes: UserService and RoleService, both inheriting BaseService.

Then you call this:

return new RoleService()
.GetAll()
.Select(r => new SelectItem
{
Text = r.Name,
Value = r.Id.ToString(),
Selected = roles.Any(ar => ar.Id == r.Id)
})
.ToList();

or this:

return roles = new RoleService(unitOfWork)
.GetAll()
.Where(r => selectedRoles.Any(sr => r.Id == Convert.ToInt32(sr)))
.ToList();

in both situations RoleService won't use the same DbContext instance used inside the Update() method from BaseRepository.

I can afirm this because there are two ways your RoleService is using a DbContext to get roles:

Since it inherits BaseService, the BaseService always creates a new instance of BaseRepository, which creates a new instance of DbContext.

Since you are using a UserService to get the user by id, and using RoleService to get roles, these two service classes are not using the same DbContext, each one has its own BaseRepository with their own DbContext.

TL;DR

You can't get roles from one DbContext, and add it to a user queried from another DbContext. This will not work:

var userService = new UserService();
var user = userService.GetById(1);

var roles = userService.GetSelectedRoles(null);
user.AddRoles(roles);

userService.Update(user);

You could fix your code by always using the DbContext from your UnitOfWork:

// Never use this, unless you are using only one entity (e.g user)
// and not using two or more related (e.g users and roles)
// public BaseRepository()
// {
// db = new BookATableContext();
// dbSet = db.Set<T>();
// }

public BaseRepository(UnitOfWork unitOfWork)
{
// Don't create a new DbContext instance, use a unique shared instance
// from UnitOfWork
// db = new BookATableContext();
dbSet = db.Set<T>();
this.unitOfWork = unitOfWork;
this.db = unitOfWork.db;
}

and whenever you need to create UserService or RoleService, use the same UnitOfWork instance to make sure there is no 2 instances of DbContext. Also, you might need to make the db property of your UnitOfWork public (or create a public getter).

So, you need to fix this method as well:

public class Base`enter code here`Service<T> where T: BaseEntity, new()
{
private BaseRepository<T> Repository;
protected UnitOfWork unitOfWork;

// Must not use this constructor, we always need the unitofWork to share DbContext
// public BaseService()
// {
// this.Repository = new BaseRepository<T>();
// }

public BaseService(UnitOfWork unitOfWork)
{
this.unitOfWork = unitOfWork;
this.Repository = new BaseRepository<T>(this.unitOfWork);

}
}

So all classes inheriting BaseService must have constructors with UnitOfWork, you can't use the parameterless constructor...also fix this:

public List<SelectItem> GetSelectedRoles(List<Role> roles)
{
roles = roles ?? new List<Role>();
return new RoleService(this.unitOfWork) // always pass unitOfWork
.GetAll()
.Select(r => new SelectItem
{
Text = r.Name,
Value = r.Id.ToString(),
Selected = roles.Any(ar => ar.Id == r.Id)
})
.ToList();
}

Example method in your Controller:

public ActionResult AddRoleToUser()
{
var unitOfWork = new UnitOfWork();

var userService = new UserService(unitOfWork); // This unitOfWork will be passed to RoleService and BaseRepository as well...
var user = userService.GetById(1);

var roles = userService.GetSelectedRoles(null);
user.AddRoles(roles);

userService.Update(user);

}


Related Topics



Leave a reply



Submit