Как реализовать класс, поддерживающий асинхронные методы?

#c# #oop #asynchronous #ado.net

#c# #ооп #асинхронный #ado.net

Вопрос:

Я уже создал группу классов, которые будут отвечать за получение данных и сохранение их в источнике. и я хочу добавить возможности асинхронности в эти классы, но я слаб в асинхронном программировании и не знаю, как лучше всего это реализовать. Я написал пример того, что я пытаюсь сделать

Как наилучшим образом реализовать асинхронные методы?

это основной класс:

 public sealed class SourceManager : IDisposable
{
    public SourceManager(string connectionString)
    {
        ConnectionString = connectionString;

        MainDataSet = new DataSet();
        Elements = new List<SourceElement>();


        // this is for example 
        Elements.Add(new SourceElement(this, "Table1"));
        Elements.Add(new SourceElement(this, "Table2"));
        Elements.Add(new SourceElement(this, "Table3"));
        Elements.Add(new SourceElement(this, "Table4"));
    }

    public void Dispose()
    {
        MainDataSet?.Dispose();
        Elements?.ForEach(element => element.Dispose());
    }

    public DataSet MainDataSet { get; }

    public string ConnectionString { get; }


    public List<SourceElement> Elements { get; }


    public void LoadElements() 
    {
        Elements.ForEach(element => element.Load());
    }

    public Task LoadElementsAsync()
    {
        throw new NotImplementedException();
    }


    public void UpdateAll()
    {
        Elements.ForEach(element => element.Update());
    } 



    public void UpdateAllAsync()
    {
        throw new NotImplementedException();
    }
}
 

это класс element :

 public sealed class SourceElement : IDisposable
{
    private readonly SqlDataAdapter _adapter;

    public SourceElement(SourceManager parentManager, string tableName)
    {
        ParentManager = parentManager;
        TableName = tableName;


        _adapter = new SqlDataAdapter($"SELECT * FROM [{TableName}];", 
  ParentManager.ConnectionString);

        _adapter.FillSchema(ParentManager.MainDataSet, SchemaType.Mapped, 
    TableName);
    }

    public void Dispose()
    {
        _adapter?.Dispose();
    }

    public string TableName { get; }


    private SourceManager ParentManager { get; }


    public void Load()
    {
        _adapter.Fill(ParentManager.MainDataSet, TableName);
    }


    public Task LoadAsync()
    {
        throw new NotImplementedException();
    }

    public void Update()
    {
        _adapter.Update(ParentManager.MainDataSet.Tables[TableName]);
    }



    public Task UpdateAsync()
    {
        throw new NotImplementedException();
    }
}
 

и вот как я это использую

 public partial class Form1 : Form
{
    private SourceManager sourceManager;
    public Form1()
    {
        InitializeComponent();

        // here we initialize the sourceManager cuz we need its elements 
   on draw the controls in the form
        sourceManager = new 
     SourceManager("Server=myServerAddress;Database=myDataBase;User 
    Id=myUsername;Password=myPassword;");

    }


    protected override void OnLoad(EventArgs e)
    {
        base.OnLoad(e);

        // here I want to fill the data tables without interrupting the interface 
        // I need to show a progress 
        sourceManager.LoadElementsAsync();

    }


    public void SaveAll()
    {
        // Here I I want to save the data without interrupting the interface thread
        sourceManager.UpdateAllAsync();
    }

    public void SaveData(string tableName)
    {
        // Here I I want to save the data without interrupting the interface thread
        sourceManager.Elements.Find(element => element.TableName.Equals(tableName))?.UpdateAsync();
    }
}
 

Ответ №1:

SqlDataAdapter не имеет асинхронных методов. Вам придется реализовать это самостоятельно, чего я не рекомендую.

пример

 await Task.Run(() =>_adapter.Fill(ParentManager.MainDataSet, TableName));
 

Но я бы рассмотрел альтернативное решение, используя другие ADO.NET библиотекам нравится использовать асинхронный SqlDataReader.

пример

     public async Task SomeAsyncMethod()
    {
        using (var connection = new SqlConnection("YOUR CONNECTION STRING"))
        {
            await connection.OpenAsync();

            using (var command = connection.CreateCommand())
            {
                command.CommandText = "YOUR QUERY";

                var reader = await command.ExecuteReaderAsync();

                while (await reader.ReadAsync())
                {
                    // read from reader 
                }
            }
        }
    }
 

Посмотрите раздел Асинхронные функции программирования, добавленные в .NET Framework 4.5

https://docs.microsoft.com/en-us/dotnet/framework/data/adonet/asynchronous-programming

Но я бы, вероятно, даже не стал беспокоиться об этом и просто использовал Dapper, который поддерживает асинхронные методы, без необходимости писать весь шаблонный код.

https://dapper-tutorial.net/async

Ответ №2:

Реализуйте асинхронные версии ваших методов следующим образом:

 public async Task LoadElementsAsync()
{
     await Task.Factory.StartNew(LoadElements);
}