webentwicklung-frage-antwort-db.com.de

Die Instanz des Entitätstyps kann nicht verfolgt werden, da bereits eine andere Instanz dieses Typs mit demselben Schlüssel verfolgt wird

Ich habe ein Serviceobjekt Update 

public bool Update(object original, object modified)
{
    var originalClient = (Client)original;
    var modifiedClient = (Client)modified;
    _context.Clients.Update(originalClient); //<-- throws the error
    _context.SaveChanges();
    //Variance checking and logging of changes between the modified and original
}

Hier rufe ich diese Methode aus:

public IActionResult Update(DetailViewModel vm)
{
    var originalClient = (Client)_service.GetAsNoTracking(vm.ClientId);
    var modifiedClient = (Client)_service.Fetch(vm.ClientId.ToString());
    // Changing the modifiedClient here
    _service.Update(originalClient, modifiedClient);
}

Hier ist die GetAsNotTracking Methode:

public Client GetAsNoTracking(long id)
{
    return GetClientQueryableObject(id).AsNoTracking().FirstOrDefault();
}

Fetch Methode:

public object Fetch(string id)
{
   long fetchId;
   long.TryParse(id, out fetchId);
   return GetClientQueryableObject(fetchId).FirstOrDefault();
}

GetClientQueryableObject:

private Microsoft.Data.Entity.Query.IIncludableQueryable<Client, ActivityType> GetClientQueryableObject(long searchId)
{
    return _context.Clients
        .Where(x => x.Id == searchId)
        .Include(x => x.Opportunities)
        .ThenInclude(x => x.BusinessUnit)
        .Include(x => x.Opportunities)
        .ThenInclude(x => x.Probability)
        .Include(x => x.Industry)
        .Include(x => x.Activities)
        .ThenInclude(x => x.User)
        .Include(x => x.Activities)
        .ThenInclude(x => x.ActivityType);
 }

Irgendwelche Ideen?

Ich habe die folgenden Artikel/Diskussionen angeschaut. Ohne Erfolg: ASP.NET GitHub Ausgabe 3839

UPDATE:

Hier sind die Änderungen an GetAsNoTracking:

public Client GetAsNoTracking(long id)
{
    return GetClientQueryableObjectAsNoTracking(id).FirstOrDefault();
}

GetClientQueryableObjectAsNoTracking:

private IQueryable<Client> GetClientQueryableObjectAsNoTracking(long searchId)
{
    return _context.Clients
        .Where(x => x.Id == searchId)
        .Include(x => x.Opportunities)
        .ThenInclude(x => x.BusinessUnit)
        .AsNoTracking()
        .Include(x => x.Opportunities)
        .ThenInclude(x => x.Probability)
        .AsNoTracking()
        .Include(x => x.Industry)
        .AsNoTracking()
        .Include(x => x.Activities)
        .ThenInclude(x => x.User)
        .AsNoTracking()
        .Include(x => x.Activities)
        .ThenInclude(x => x.ActivityType)
        .AsNoTracking();
}
19
Rijnhardt

Ohne das EF-Verfolgungssystem zu überschreiben, können Sie auch den lokalen Eintrag entfernen und den aktualisierten Eintrag vor dem Speichern anfügen:

// 
var local = _context.Set<YourEntity>()
    .Local
    .FirstOrDefault(entry => entry.Id.Equals(entryId));

// check if local is not null 
if (!local.IsNull()) // I'm using a extension method
{
    // detach
    _context.Entry(local).State = EntityState.Detached;
}
// set Modified flag in your entry
_context.Entry(entryToUpdate).State = EntityState.Modified;

// save 
_context.SaveChanges();

UPDATE: Um Code-Redundanz zu vermeiden, können Sie eine Erweiterungsmethode verwenden:

public static void DetachLocal<T>(this DbContext context, T t, string entryId) 
    where T : class, IIdentifier 
{
    var local = context.Set<T>()
        .Local
        .FirstOrDefault(entry => entry.Id.Equals(entryId));
    if (!local.IsNull())
    {
        context.Entry(local).State = EntityState.Detached;
    }
    context.Entry(t).State = EntityState.Modified;
}

Meine IIdentifier-Schnittstelle verfügt nur über eine Id-Zeichenfolge.

Unabhängig von Ihrer Entität können Sie diese Methode in Ihrem Kontext verwenden:

_context.DetachLocal(tmodel, id);
_context.SaveChanges();
14
Andres Talavera

Es klingt so, als würden Sie wirklich nur die Änderungen verfolgen, die am Modell vorgenommen wurden, und nicht, um ein nicht protokolliertes Modell im Speicher zu behalten. Darf ich einen alternativen Ansatz vorschlagen, der das Problem vollständig beseitigt?

EF wird Änderungen automatisch nachverfolgen. Wie wäre es mit der eingebauten Logik?

Überschreiben Sie SaveChanges() in Ihrer DbContext.

    public override int SaveChanges()
    {
        foreach (var entry in ChangeTracker.Entries<Client>())
        {
            if (entry.State == EntityState.Modified)
            {
                // Get the changed values.
                var modifiedProps = ObjectStateManager.GetObjectStateEntry(entry.EntityKey).GetModifiedProperties();
                var currentValues = ObjectStateManager.GetObjectStateEntry(entry.EntityKey).CurrentValues;
                foreach (var propName in modifiedProps)
                {
                    var newValue = currentValues[propName];
                    //log changes
                }
            }
        }

        return base.SaveChanges();
    }

Gute Beispiele finden Sie hier:

Entity Framework 6: Audit/Änderungen verfolgen

Implementieren des Audit-Protokolls/Änderungsverlauf mit MVC & Entity Framework

EDIT:Client kann leicht in eine Schnittstelle geändert werden. Sagen wir ITrackableEntity. Auf diese Weise können Sie die Logik zentralisieren und alle Änderungen an allen Entitäten, die eine bestimmte Schnittstelle implementieren, automatisch protokollieren. Die Schnittstelle selbst hat keine spezifischen Eigenschaften.

    public override int SaveChanges()
    {
        foreach (var entry in ChangeTracker.Entries<ITrackableClient>())
        {
            if (entry.State == EntityState.Modified)
            {
                // Same code as example above.
            }
        }

        return base.SaveChanges();
    }

Werfen Sie einen Blick auf den großen Vorschlag von eranga , der abonniert werden soll, anstatt SaveChanges () tatsächlich zu überschreiben.

4
smoksnes

In meinem Fall wurde die ID-Spalte der Tabelle nicht als Identitätsspalte festgelegt.

0
daviesdoesit

Wenn sich Ihre Daten jedes Mal geändert haben, werden Sie feststellen, dass die Tabelle nicht nachverfolgt wird. Beispiel: Einige Tabellenaktualisierungs-ID ([Schlüssel]) mit tigger.

0
menxin

Arhhh das hat mich erwischt und ich habe viel Zeit damit verbracht, Fehler zu beheben. Das Problem war, dass meine Tests in Parellel ausgeführt wurden (der Standard mit XUnit).

Um meinen Testlauf nacheinander durchzuführen, habe ich jede Klasse mit diesem Attribut versehen:

[Collection("Sequential")]

So habe ich es ausgearbeitet: Führen Sie Unit-Tests seriell (anstatt parallel) durch


Ich verspotte meinen EF In Memory-Kontext mit GenFu:

private void CreateTestData(TheContext dbContext)
{
    GenFu.GenFu.Configure<Employee>()
       .Fill(q => q.EmployeeId, 3);
    var employee = GenFu.GenFu.ListOf<Employee>(1);

    var id = 1;
    GenFu.GenFu.Configure<Team>()
        .Fill(p => p.TeamId, () => id++).Fill(q => q.CreatedById, 3).Fill(q => q.ModifiedById, 3);
    var Teams = GenFu.GenFu.ListOf<Team>(20);
    dbContext.Team.AddRange(Teams);

    dbContext.SaveChanges();
}

Wenn ich Testdaten erstellte, waren diese in zwei Bereichen aktiv (einmal in den Tests des Mitarbeiters, während die Teamtests ausgeführt wurden):

public void Team_Index_should_return_valid_model()
{
    using (var context = new TheContext(CreateNewContextOptions()))
    {
        //Arrange
        CreateTestData(context);
        var controller = new TeamController(context);

        //Act
        var actionResult = controller.Index();

        //Assert
        Assert.NotNull(actionResult);
        Assert.True(actionResult.Result is ViewResult);
        var model = ModelFromActionResult<List<Team>>((ActionResult)actionResult.Result);
        Assert.Equal(20, model.Count);
    }
}

Das Umschließen beider Testklassen mit diesem Attribut für sequentielle Auflistung hat den offensichtlichen Konflikt behoben.

[Collection("Sequential")]

Zusätzliche Referenzen:

https://github.com/aspnet/EntityFrameworkCore/issues/734
EF Core 2.1 In der Speicher-DB werden keine Datensätze aktualisiert
http://www.jerriepelser.com/blog/unit-testing-aspnet5-entityframework7-inmemory-database/
http://gunnarpeipman.com/2017/04/aspnet-core-ef-inmemory/
https://github.com/aspnet/EntityFrameworkCore/issues/12459
Verhindern von Nachverfolgungsproblemen bei Verwendung von EF Core SqlLite in Komponententests

0
Jeremy Thompson