Domanda

Sto scrivendo un programma in C# .Net 4.5 libreria per fare comune di database sql (operazioni di backup, ripristino, eseguire lo script, etc.).Io voglio che sia sincrona che asincrona funzioni per ogni operazione, come la biblioteca sarà utilizzato da entrambe le console e applicazioni GUI, ma non voglio duplicare il codice ovunque.Così come la vedo io, ho due opzioni:

  1. Scrivere il codice che fa il lavoro in funzione sincrona, e poi basta avvolgerlo in un compito per la funzione async, in questo modo:

    public void BackupDB(string server, string db)  
    {  
        // Do all of the work and long running operation here  
    }
    
    public async Task BackupDBAsync(string server, string db)  
    {  
        await Task.Factory.StartNew(() => BackupDB(server, db)).ConfigureAwait(false);  
    }
    
  2. Scrivere il codice che fa il lavoro in una funzione asincrona, e chiamata da una funzione sincrona utilizzando .Wait():

    public async Task BackupDBAsync(string server, string db)  
    {  
        // Do all of the work and long running operation here, asynchronously.  
    }
    
    public void BackupDB(string server, string db)  
    {  
        BackupDBAsync(server, db).Wait(); // Execution will wait here until async function finishes completely.  
    }
    

È un'opzione migliore rispetto agli altri?È una best practice?O ci sono altri (meglio) alternative?

So che uno svantaggio di utilizzo .Wait() è che tutti attendono dichiarazioni in funzione async per l'uso .ConfigureAwait(false) per evitare i deadlock (come discusso qui), ma visto che sto scrivendo una libreria che mai necessario per accedere all'interfaccia utente o WebContext io sono sicuro farlo.

Faccio notare anche che la libreria SQL in genere ha anche sincrone e asincrone funzioni che possono essere utilizzate, quindi se si fa il lavoro in funzione di sincronizzazione, vorrei richiamare la loro funzione di sincronizzazione, e se fa il lavoro in funzione async, vorrei richiamare la loro funzione async.

Pensieri/suggerimenti sono sempre apprezzati.

-- edit:Ho anche postato questa domanda sul forum di MSDN qui per cercare di ottenere un ufficiale di MS di risposta --

È stato utile?

Soluzione

Io voglio che sia sincrona che asincrona funzioni per ogni operazione, come la biblioteca sarà utilizzato da entrambe le console e applicazioni GUI, ma non voglio duplicare il codice ovunque.

La risposta migliore è:non.

Stephen Toub ha due ottimi post di blog su questo argomento:

Egli raccomanda di esporre i metodi asincroni come asincrona e sincrona metodi sincroni.Se avete bisogno di esporre, quindi incapsulano le funzionalità più comuni in privato (sincrona) metodi e duplicare il async/sincronizzazione differenze.

Altri suggerimenti

Ho avuto una situazione simile, in cui alcune applicazioni bisogno di caricare i dati in modo sincrono e altri asyc.Ho deciso di creare un'interfaccia che ho chiamato il mio dataloader:

public interface IIMViewModelDL {
    void LoadProjects(AssignProjects callback);
}

Il AssignProjects richiamata è solo un semplice delegato che viene restituito l'elenco dei progetti:

public delegate void AssignProjects(IEnumerable<Project> results);

Ora, la bellezza di questo è che si può lavorare con l'interfaccia senza sapere se si è dealling in sincrono o asincrono.

Tre classi sono create:uno base, uno di sincronizzazione, e uno async:

 public abstract class BaseViewModelDL {
    protected IEnumerable<Project> LoadProjects() {
        BaseServiceClient client = new BaseServiceClient();
        return client.Projects();
    }

public class SynchronousViewModelDL : BaseViewModelDL, IIMViewModelDL {
    public void LoadProjects(AssignProjects callback) {
        callback(base.LoadProjects());
    }

public class AsyncIMViewModelDL : BaseViewModelDL, IIMViewModelDL {
    public void LoadProjects(AssignProjects callback) {
        BackgroundWorker loadProjectsAsync = new BackgroundWorker();
        loadProjectsAsync.DoWork += new DoWorkEventHandler(LoadProjectsAsync_DoWork);
        loadProjectsAsync.RunWorkerCompleted += new RunWorkerCompletedEventHandler(LoadProjectsAsync_RunWorkerCompleted);
        loadProjectsAsync.RunWorkerAsync(callback);
    }

void LoadProjectsAsync_DoWork(object sender, DoWorkEventArgs e) {
        var results = new ObservableCollection<Project>(base.LoadProjects());
        e.Result = new object[] { results, e.Argument };
    }

    void LoadProjectsAsync_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) {
        AssignProjects callback = (AssignProjects)((object[])e.Result)[1];
        IEnumerable<Project> results = (IEnumerable<Project>)((object[])e.Result)[0];
        callback(results);
    }

Ora, nella vostra applicazione, si può decidere come si desidera caricare i dati...questo potrebbe essere iniettato attraverso un IoC container, ma è codificata per scopi dimostrativi:

private ViewModelDataLoaders.IIMViewModelDL dataLoader = new ViewModelDataLoaders.AsyncIMViewModelDL();

Ora, il codice chiamante sembra la stessa, e nessuno il più saggio se è asincrona o sincrona:

private void LoadProjects() {
        dataLoader.LoadProjects(
            delegate(IEnumerable<Project> results) {
                Projects = new ObservableCollection<Project>(results);
            });
    }

Io la uso regolarmente per i test di unità (sync), le applicazioni WPF (async), e le applicazioni console (sync).

Non sembra essere un punto di contrassegnare semplicemente un metodo asincrono senza l'utilizzo di un attendono.Marcatura come async non è asincrona, permette di utilizzare attende (il codice eseguito in attesa che avviene in modo asincrono, e poi il resto del metodo asincrono sarà anche eseguita in modo asincrono) nel corpo del metodo:

In genere, un metodo modificato dal async parola chiave contiene almeno un espressione di attesa o di istruzione.Il metodo viene eseguito in modo sincrono fino a quando non raggiunge la prima espressione di attesa, il punto in cui è sospesa fino a quando l'atteso completamento dell'attività.Nel frattempo, il controllo viene restituito al chiamante del metodo.Se il metodo non contiene un'espressione di attesa o di istruzione, poi esegue in modo sincrono.Un avviso del compilatore segnala eventuali metodi asincroni che non contengono attendono, a causa di tale situazione potrebbe indicare un errore.Per ulteriori informazioni, vedere Avviso del Compilatore CS4014.

Da: async

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top