我正在编写一个c#.NET 4.5库以进行常见的SQL数据库操作(备份,还原,执行脚本等)。我希望每次操作都有同步和异步功能,因为这库库都可以由控制台和GUI应用程序使用,但我不想到处都有重复代码。所以我看到它,我有两个选择:

  1. 写下在同步函数中执行工作的代码,然后只需将其包装在异步函数的任务中,如:

    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. 在异步函数中写下工作的代码,并使用.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.  
    }
    

    是比另一个更好的选项?是最好的做法吗?或者是否有其他(更好)替代品?

    我知道一个警告使用.wait()是异步函数中的所有等待语句都必须使用.configureawait(false)来避免死锁(如这里所讨论的),但是由于我正在编写一个永远不需要访问UI的库或webcontext我安全地做到这一点。

    我也可以注意到SQL库通常还具有可以使用的同步和异步函数,因此如果在同步函数中执行工作,我会调用它们的同步功能,如果在异步中执行工作功能,我会称他们的异步函数。

    思想/建议是赞赏的。

    - 编辑:我还发布了这个问题在这里的MSDN论坛上试试并获得官方MS响应 -

有帮助吗?

解决方案

我希望每次操作都有同步和异步功能,因为这两个库都是由控制台和GUI应用程序使用,但我不想在任何地方重复代码。

最好的答案是:别。

Stephen Toub在这个主题上有两个优秀的博客帖子:

其他提示

我有类似的情况,其中一些应用程序需要同步加载数据和其他应用程序。我决定创建一个我打电话给我的dataloader的界面:

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

赋值按回调只是一个简单的委托,其中涉及返回的项目列表:

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

现在,您可以在不知道您是否正在同步或异步时处理界面。

创建了三个类:一个基础,一个同步和一个异步:

 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);
    }
. 现在,在您的应用程序中,您可以决定如何加载数据...这可以通过IOC容器注入,但是对于演示目的而言,是硬编码的:
private ViewModelDataLoaders.IIMViewModelDL dataLoader = new ViewModelDataLoaders.AsyncIMViewModelDL();
. 现在,您的主叫代码看起来相同,是否是异步或同步的更明智:
private void LoadProjects() {
        dataLoader.LoadProjects(
            delegate(IEnumerable<Project> results) {
                Projects = new ObservableCollection<Project>(results);
            });
    }
.

我定期使用单位测试(同步),WPF应用程序(异步)和控制台应用程序(SYNC)。

在不使用等待的情况下,似乎只需将方法标记为异步即可。将其标记为异步不使其异步,它允许您使用等待(在等待中执行的代码是异步发生的事情,然后在方法的正文中也将在方法中异步完成:< / p>

通常,由Async关键字修改的方法包含至少一个等待表达式或语句。该方法同步运行,直到它到达第一个等待表达式,此时它暂停,直到已等待的任务完成。与此同时,控制将返回到该方法的呼叫者。如果该方法不包含等待表达式或语句,则它同步执行。编译器警告警告您对不包含等待的任何异步方法,因为情况可能表明错误。有关更多信息,请参阅Compiler警告CS4014。

from:异步 < / p>

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top