Iterazione tramite IQueryable con risultati lazyload in più DataReader aperti

0

Ho riscontrato un problema con il caricamento lazy in combinazione con un ciclo foreach.

Sto lavorando su un codice prima del backend di asp.net per un sito web, ea volte voglio raccogliere informazioni su più tabelle / modelli.

Ad esempio, prendi questo:

public class Foo
{
    [Key]
    public Int32 Id { get; set; }
    public virtual Bar Bar { get; set; }
    public Int32 someInt { get; set; }
}

public class Bar
{
    [Key]
    public Int32 FooId { get; set; }

    [ForeignKey("FooId")]
    public virtual Foo Foo { get; set; }

    public String someString { get; set; }
}

public async Task<IHttpActionResult> getSomeInformation
{
    using(WhateverContext context = new WhateverContext())
    {
        var Foos = context.Foos;

        List<FrontendFriendlyModel> ForTheFrontend = new List<FrontendFriendlyModel>();

        foreach (var Foo in Foos)
        {
            var entry = new FrontendFriendlyModel();
            entry.Id = Foo.Id; //Works fine
            entry.someInt = Foo.someInt; //Works also fine
            entry.someString = Foo.Bar.someString; //Crashes, "There is already an open DataReader associated with this Command"

            ForTheFrontend.Add(entry);
        }

        return Ok(ForTheFrontend); //I convert it to a Json String with Json.net, but thats not relevant I guess
    }
}

So che posso attivare MARS nella mia stringa String.

Potrei anche caricare la barra sul posto ed evitare il problema.

var Foos = context.Foos.Include(f => f.Bar);

Da tutto ciò che ho raccolto, l'uso di MARS non è generalmente considerato una buona pratica, e non mi piace il fatto che devo caricarmi di tutto all'inizio.

Cose che potrei trovare su google a proposito di questo, non mi aiutano davvero qui (forse mi limito a succhiarlo su google), e sento che mi manca qualcosa di veramente semplice.

Quindi mi chiedo, qual è il modo corretto qui. Attivando MARS? Vuoi caricare tutto ciò di cui ho bisogno? Trasmetti l'IQueryable a un elenco?

        var Foos = context.Foos.ToList();
        foreach (var Foo in Foos)
        {
            var entry = new FrontendFriendlyModel();
            entry.Id = Foo.Id; //Works fine
            entry.someInt = Foo.someInt; //Works also fine                
            entry.someString = Foo.Bar.someString; //Now that works, but I don't get why it doesnt work as IQueryable.
            ForTheFrontend.Add(entry);
        }

Non capisco perché non posso lavorare con un IQueryable qui, quindi forse qualcuno può illuminarmi.

    
posta user3021443 14.02.2017 - 14:13
fonte

1 risposta

1

Hai due scelte:

  1. Puoi caricare ansiosamente o
  2. Puoi abilitare MARS .

Presumibilmente, si desidera utilizzare IQueryable per ottenere l'esecuzione posticipata. Presumo che tu voglia chiamare getSomeInformation() con await . Se si desidera la ricerca differita dal database, è necessario utilizzare MARS; non hai scelta.

Tuttavia, si noti che è ancora possibile ottenere la maggior parte dei benefici dell'esecuzione posticipata caricando con entusiasmo l'entità correlata. Il tuo metodo sarà ancora async . L'unica vera differenza è che tu precarichi l'entità in memoria invece di recuperarla dal database on-demand in un secondo momento, che è probabilmente quello che vuoi comunque.

Riguardo all'opportunità o meno che l'uso di MARS sia una pratica "buona" o "cattiva", penso che dipenda dal modo in cui utilizzarla correttamente. È facile fare il caricamento avido correttamente; è un po 'più difficile con MARS. Considera questi errori che possono verificarsi con MARS:

The transaction operation cannot be performed because there are pending requests working on this transaction.

The server failed to resume the transaction. The transaction active in this session has been committed or aborted by another session.

The underlying provider failed on Open. The connection was not closed. The connection's current state is "connecting."

ExecuteNonQuery requires the command to have a transaction when the connection assigned to the command is in a pending local transaction. The Transaction property of the command has not been initialized.

The server failed to resume the transaction.

Tutti questi errori dimostrano fondamentali equivoci su come funziona MARS e su come utilizzare la connessione e il modello di transazione sottostanti. Microsoft ha una guida adeguata (collegata di seguito) per l'utilizzo di MARS in modi che non causano questi errori.

Ulteriori letture
Uso di più insiemi di risultati attivi
Abilitazione di più insiemi di risultati attivi
Eccezioni quando si utilizza MARS

    
risposta data 14.02.2017 - 23:37
fonte

Leggi altre domande sui tag