L’istanza del tipo di entità non possono essere monitorati perché un altro esempio di questo tipo con la stessa chiave è già registrato

Ho un Servizio in Oggetto 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
}

Questo è dove sto chiamando questo metodo da:

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

Qui è il GetAsNotTracking metodo:

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

Fetch metodo:

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

Tutte le idee?

Ho guardato i seguenti articoli /discussioni. Inutilmente:ASP.NET GitHub Problema 3839

AGGIORNAMENTO:

Ecco le modifiche per 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();
}
  • Penso che se si sta mettendo AsNoTracking() potrebbe essere un po ‘ in ritardo, come _context.Clients è già registrato. Prova a mettere il tuo AsNoTracking() direttamente sul _context.Clients chiamata. Tutti i tuoi lncludes, probabilmente, dovrebbe essere AsNoTracking(), a meno che il piano di monitoraggio di quelli.
  • cercando ora. Vi permetterà di conoscere.
  • Posso chiedere come mai hai trovato un modo originale e modificato? Perché non basta avere l’originale, apportare modifiche, quindi chiamare aggiornamento? Non vedo il punto di ottenere modificata?
  • Devo fare un registro di controllo. Quindi ho una Varianza Pedina che controlli sui cambi nelle due oggetti e li registra in un database.
  • Oh, ho appena visto il commento nel codice perso 🙂 @RobertHarvey risposta dovrebbe funzionare
  • Ho provato il tuo suggerimento. Ho aggiunto il AsNoTracking() per il _context chiamata. Non funziona. Devo aggiornare la domanda con le mie modifiche?
  • Ho aggiunto le modifiche consigliate.
  • Cosa succede se si chiama GetAsNoTracking sia modificato e originale?
  • Cerchiamo di continuare questa discussione in chat.
  • ha funzionato, grazie.
  • Per la cronaca: uno AsNoTracking() chiamata è sufficiente, non importa dove.

InformationsquelleAutor Rijnhardt | 2016-04-26



5 Replies
  1. 27

    Senza sovrascrivere EF sistema binario, si può anche Staccare il ‘locale’ di entrata e di allegare il tuo aggiornati voce prima di salvare :

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

    AGGIORNAMENTO:
    Per evitare la ridondanza del codice, si può fare un metodo di estensione :

    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;
    }

    Mio IIdentifier interfaccia è solo un Id proprietà di stringa.

    Qualsiasi Entità, è possibile utilizzare questo metodo sul contesto :

    _context.DetachLocal(tmodel, id);
    _context.SaveChanges();
    • Grazie uomo, funziona benissimo
  2. 5

    Suona come voi, davvero voglia di tenere traccia delle modifiche apportate al modello, di non tenere un untracked modello in memoria. Mi permetto di suggerire un approccio alternativo che provvederà a rimuovere completamente il problema?

    EF sarà automticallly traccia delle modifiche. Che ne dite di fare uso di che ha costruito in una logica?

    Ovverride SaveChanges() nel 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();
        }

    Buoni esempi possono essere trovati qui:

    Entity Framework 6: controllo e di tenere traccia delle modifiche

    Esecuzione di Audit Log /Cambiare la Storia con MVC & Entity Framework

    EDIT:
    Client può essere facilmente modificato per un’interfaccia. Diciamo ITrackableEntity. In questo modo è possibile centralizzare la logica e accedere automaticamente tutte le modifiche a tutte le entità che implementano un’interfaccia specifica. L’interfaccia, anche se non dispone di tutte le proprietà specifiche.

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

    Inoltre, dare un’occhiata a eranga‘s grande suggestione per iscriversi invece di sovrascrivere il metodo SaveChanges().

    • Effettivamente ho dato un’occhiata a questi esempi, alcuni problemi che ho avuto è stato che ha bisogno di essere attivata su certi tavoli. I casi d’uso utilizzato non c’era un ampia attuazione. Grazie per il feedback. Davvero apprezzare.
    • Ho fatto un edit al post.
    • questo potrebbe arrivare da qualche parte, grazie per l’input. una sola query, anche se-come faccio a ottenere l’autorizzazione dell’utente? Ho bisogno per il log di controllo. Posso utilizzare il User.GetUserId()?
    • Che dipenderà dalla vostra applicazione e come autenticare l’utente. È WPF, ASP, MVC, ecc. Io di solito astratto a un manager o un util che può recuperare l’utente corrente. Utente.Recuperaidutente() suona come qualcosa che è disponibile per il Controller, che è breve per HttpContext.Utente. Quindi è necessario HttpContext. Ecco qualcosa che vi aiuterà a: stackoverflow.com/questions/1918283/…, stackoverflow.com/questions/5898170/…
    • HttpContext.Corrente.Utente.Identità.Recuperaidutente();
  3. 2

    Nel mio caso, la tabella l’id di colonna non è stata impostata come una colonna Identity.

    • mi hai salvato 🙂 ho avuto il mio set di chiavi, ma a torto (Id e Livello, invece di un solo Id) e quindi ho ottenuto questo errore di eccezione.
  4. 0

    Se i dati sono cambiati ogni volta,si noterà che non traccia la tabella.per esempio alcuni di aggiornamento tabella (id[chiave]) utilizzando tigger.Se si traccia ,è la stessa id e ottenere il problema.

  5. 0

    Arhhh questo mi ha fatto e ho speso un sacco di tempo di risoluzione dei problemi. Il problema era il mio test venivano eseguiti in Parallelo (l’impostazione predefinita con XUnit).

    Per fare il mio test eseguiti sequenzialmente ho decorato ogni classe con questo attributo:

    [Collection("Sequential")]

    Questo è come ho lavorato fuori: Esecuzione test di unità in serie invece che in parallelo)


    Ho finto la mia EF In contesto di Memoria con 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();
    }

    Durante la Creazione di Dati di Test, da quello che posso dedurre, era viva in due ambiti (una volta di Dipendente del Test, mentre il Team di test sono stati in esecuzione):

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

    Avvolgere entrambe le Classi di Test con questo insieme sequenziale attributo ha cancellato l’apparente conflitto.

    [Collection("Sequential")]

    Ulteriori riferimenti:

    https://github.com/aspnet/EntityFrameworkCore/issues/7340

    EF Core 2.1 In memoria DB non l’aggiornamento dei record

    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

    Prevenire la verifica dei problemi quando si utilizza EF Core SqlLite in Unità di Test

Lascia un commento