entity-object kan niet worden verwezen door meerdere instanties van IEntityChangeTracker. terwijl gerelateerde objecten worden toegevoegd aan entiteit in Entity Framework 4.1

Ik probeer de gegevens van de werknemer op te slaan, die verwijzingen hebben naar de stad. Maar elke keer dat ik mijn contact probeer op te slaan, wat gevalideerd is, krijg ik de uitzondering “ADO.Net Entity Framework Een entiteitsobject kan niet worden verwezen door meerdere instanties van IEntityChangeTracker”

Ik had zoveel berichten gelezen, maar kreeg nog steeds niet precies het idee van wat ik moest doen…
mijn klikcode voor de knop Opslaan staat hieronder

protected void Button1_Click(object sender, EventArgs e)
    {
        EmployeeService es = new EmployeeService();
        CityService cs = new CityService();
        DateTime dt = new DateTime(2008, 12, 12);
        Payroll.Entities.Employee e1 = new Payroll.Entities.Employee();
        Payroll.Entities.City city1 = cs.SelectCity(Convert.ToInt64(cmbCity.SelectedItem.Value));
        e1.Name = "Archana";
        e1.Title = "aaaa";
        e1.BirthDate = dt;
        e1.Gender = "F";
        e1.HireDate = dt;
        e1.MaritalStatus = "M";
        e1.City = city1;        
        es.AddEmpoyee(e1,city1);
    }

en Werknemersservicecode

public string AddEmpoyee(Payroll.Entities.Employee e1, Payroll.Entities.City c1)
        {
            Payroll_DAO1 payrollDAO = new Payroll_DAO1();
            payrollDAO.AddToEmployee(e1);  //Here I am getting Error..
            payrollDAO.SaveChanges();
            return "SUCCESS";
        }

Antwoord 1, autoriteit 100%

Omdat deze twee regels …

EmployeeService es = new EmployeeService();
CityService cs = new CityService();

… neem geen parameter in de constructor, ik denk dat je een context creëert binnen de klassen. Wanneer u de city1

. laadt

Payroll.Entities.City city1 = cs.SelectCity(...);

…je voegt de city1toe aan de context in CityService. Later voegt u een city1toe als verwijzing naar de nieuwe Employeee1en voegt u e1met deze verwijzing toe naar city1naar de context in EmployeeService. Als gevolg hiervan heb je city1gekoppeld aan twee verschillende contexten en dat is waar de uitzondering over klaagt.

Je kunt dit oplossen door een context buiten de serviceklassen te maken en deze in beide services te injecteren en te gebruiken:

EmployeeService es = new EmployeeService(context);
CityService cs = new CityService(context); // same context instance

Uw serviceklassen lijken een beetje op repositories die verantwoordelijk zijn voor slechts één type entiteit. In zo’n geval heb je altijd problemen zodra er relaties tussen entiteiten in het spel zijn als je aparte contexten gebruikt voor de diensten.

U kunt ook een enkele service maken die verantwoordelijk is voor een reeks nauw verwante entiteiten, zoals een EmployeeCityService(die één context heeft) en de hele bewerking delegeren in uw Button1_Clickmethode naar een methode van deze service.


Antwoord 2, autoriteit 12%

Stappen om te reproduceren kunnen worden vereenvoudigd tot dit:

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 zonder fouten:

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();

Het gebruik van slechts één EntityContextkan dit oplossen. Raadpleeg andere antwoorden voor andere oplossingen.


Antwoord 3, autoriteit 4%

Dit is een oude thread, maar een andere oplossing, waar ik de voorkeur aan geef, is om gewoon de cityId bij te werken en niet het hole-model Stad toe te wijzen aan Werknemer… om dat te doen, zou Werknemer er als volgt uit moeten zien:

public class Employee{
    ...
    public int? CityId; //The ? is for allow City nullable
    public virtual City City;
}

Dan is het genoeg toewijzen:

e1.CityId=city1.ID;

Antwoord 4, autoriteit 2%

Als alternatief voor injectie en nog erger Singleton, kunt u de methode Detachgebruiken vóór Toevoegen.

EntityFramework 6: ((IObjectContextAdapter)cs).ObjectContext.Detach(city1);

EntityFramework 4: cs.Detach(city1);

Er is nog een andere manier, voor het geval je het eerste DBContext-object niet nodig hebt. Sluit het gewoon af met metzoekwoord:

Payroll.Entities.City city1;
using (CityService cs = new CityService())
{
  city1 = cs.SelectCity(Convert.ToInt64(cmbCity.SelectedItem.Value));
}

Antwoord 5, autoriteit 2%

Ik had hetzelfde probleem, maar mijn probleem met de oplossing van @Slauma (hoewel in bepaalde gevallen geweldig) is dat het aanbeveelt dat ik de context doorgeef aan de service, wat inhoudt dat de context beschikbaar is vanaf mijn controller. Het dwingt ook een nauwe koppeling tussen mijn controller en servicelagen af.

Ik gebruik Dependency Injection om de service/repository-lagen in de controller te injecteren en heb als zodanig geen toegang tot de context van de controller.

Mijn oplossing was om de service/repository-lagen dezelfde instantie van de context te laten gebruiken – Singleton.

Context Singleton-klasse:

Referentie: http://msdn.microsoft.com/en-us /bibliotheek/ff650316.aspx
en 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-klasse:

public class ProjectRepository : IProjectRepository
{
  MyModelDbContext context = MyModelDbContextSingleton.Instance;
  [...]

Er bestaan ook andere oplossingen, zoals het één keer instantiëren van de context en doorgeven aan de constructors van uw service-/repository-lagen of een andere waarover ik heb gelezen en die het Unit of Work-patroon implementeert. Ik weet zeker dat er meer zijn…


Antwoord 6

In mijn geval gebruikte ik het ASP.NET Identity Framework. Ik had de ingebouwde UserManager.FindByNameAsyncmethode gebruikt om een ApplicationUserentiteit op te halen. Ik heb toen geprobeerd om naar deze entiteit te verwijzen op een nieuw gemaakte entiteit op een andere DbContext. Dit resulteerde in de uitzondering die u oorspronkelijk zag.

Ik heb dit opgelost door een nieuwe entiteit ApplicationUserte maken met alleen de Idvan de methode UserManageren te verwijzen naar die nieuwe entiteit.

>


Antwoord 7

Ik had hetzelfde probleem en ik kon het oplossen door een nieuwe instantie te maken van het object dat ik probeerde bij te werken. Toen heb ik dat object doorgegeven aan mijn repository.


Antwoord 8

In dit geval blijkt de fout heel duidelijk te zijn: Entity Framework kan een entiteit niet volgen met behulp van meerdere instanties van IEntityChangeTrackerof doorgaans meerdere instanties van DbContext. De oplossingen zijn: gebruik één instantie van DbContext; toegang krijgen tot alle benodigde entiteiten via een enkele repository (afhankelijk van één instantie van DbContext); of het uitschakelen van tracking voor alle entiteiten die toegankelijk zijn via een andere repository dan degene die deze specifieke uitzondering veroorzaakt.

Bij het volgen van een omkering van het besturingspatroon in .Net Core Web API, merk ik vaak dat ik controllers heb met afhankelijkheden zoals:

private readonly IMyEntityRepository myEntityRepo; // depends on MyDbContext
private readonly IFooRepository fooRepo; // depends on MyDbContext
private readonly IBarRepository barRepo; // depends on MyDbContext
public MyController(
    IMyEntityRepository myEntityRepo, 
    IFooRepository fooRepo, 
    IBarRepository barRepo)
{
    this.fooRepo = fooRepo;
    this.barRepo = barRepo;
    this.myEntityRepo = myEntityRepo;
}

en gebruik zoals

...
myEntity.Foo = await this.fooRepository.GetFoos().SingleOrDefaultAsync(f => f.Id == model.FooId);
if (model.BarId.HasValue)
{
    myEntity.Foo.Bar = await this.barRepository.GetBars().SingleOrDefaultAsync(b => b.Id == model.BarId.Value);
}
...
await this.myEntityRepo.UpdateAsync(myEntity); // this throws an error!

Aangezien alle drie de opslagplaatsen afhankelijk zijn van verschillende DbContext-instanties per verzoek, heb ik twee opties om het probleem te vermijden en afzonderlijke opslagplaatsen te behouden: wijzig de injectie van de DbContext om slechts één keer per aanroep een nieuwe instantie te maken :

// services.AddTransient<DbContext, MyDbContext>(); <- one instance per ctor. bad
services.AddScoped<DbContext, MyDbContext>(); // <- one instance per call. good!

of, als de onderliggende entiteit alleen-lezen wordt gebruikt, het bijhouden van die instantie uitschakelen:

myEntity.Foo.Bar = await this.barRepo.GetBars().AsNoTracking().SingleOrDefault(b => b.Id == model.BarId);

Antwoord 9

Ik heb hetzelfde probleem ondervonden na het implementeren van IoC voor een project (ASP.Net MVC EF6.2).

Meestal zou ik een datacontext initialiseren in de constructor van een controller en dezelfde context gebruiken om al mijn repositories te initialiseren.

Het gebruik van IoC om de repositories te instantiëren zorgde er echter voor dat ze allemaal aparte contexten hadden en ik kreeg deze foutmelding.

Voor nu ben ik teruggegaan naar het vernieuwen van de repositories met een gemeenschappelijke context terwijl ik een betere manier bedenk.


Antwoord 10

Gebruik hetzelfde DBContext-object gedurende de hele transactie.


Antwoord 11

Dit is hoe ik dit probleem ben tegengekomen. Eerst moet ik mijn Orderopslaan, die een verwijzing naar mijn ApplicationUser-tabel nodig heeft:

 ApplicationUser user = new ApplicationUser();
  user = UserManager.FindById(User.Identity.GetUserId());
  Order entOrder = new Order();
  entOrder.ApplicationUser = user; //I need this user before saving to my database using EF

Het probleem is dat ik een nieuwe ApplicationDbContext initialiseer om mijn nieuwe Order-entiteit op te slaan:

ApplicationDbContext db = new ApplicationDbContext();
 db.Entry(entOrder).State = EntityState.Added;
 db.SaveChanges();

Dus om het probleem op te lossen, gebruikte ik dezelfde ApplicationDbContext in plaats van de ingebouwde UserManager van ASP.NET MVC.

In plaats van dit:

user = UserManager.FindById(User.Identity.GetUserId());

Ik heb mijn bestaande ApplicationDbContext-instantie gebruikt:

//db instance here is the same instance as my db on my code above.
user = db.Users.Find(User.Identity.GetUserId()); 

Antwoord 12

Voor mijn scenario hebben we een oplossing met verschillende applicaties die verwijzen naar dezelfde context. Ik moest het unity.config-bestand bijwerken door het levensduurtype aan de context toe te voegen.

<lifetime type="PerResolveLifetimeManager" />

Antwoord 13

Foutbron:

ApplicationUser user = await UserManager.FindByIdAsync(User.Identity.Name);
ApplicationDbContext db = new ApplicationDbContent();
db.Users.Uploads.Add(new MyUpload{FileName="newfile.png"});
await db.SavechangesAsync();/ZZZZZZZ

Ik hoop dat iemand wat kostbare tijd bespaart

Other episodes