• Najnowsze pytania
  • Bez odpowiedzi
  • Zadaj pytanie
  • Kategorie
  • Tagi
  • Zdobyte punkty
  • Ekipa ninja
  • IRC
  • FAQ
  • Regulamin
  • Książki warte uwagi

question-closed Działanie aplikacji/kodu w tle

Object Storage Arubacloud
+1 głos
337 wizyt
pytanie zadane 2 czerwca 2022 w C# przez troian1337 Użytkownik (720 p.)
zamknięte 2 czerwca 2022 przez troian1337

Witam, mam napisany prosty program do pobierania danych z bazy danych i zapisywanie go do pliku csv.

Problem w tym że przy uruchomieniu program się zawiesza/zacina, do momentu pobrania danych i zapisaniu ich.

Czy istnieje jakiś sposób aby ta operacja wykonywała się w tle? Bez zawieszania aplikacji? 

Coś jak DownloadManager który pobiera plik w tle i nie powoduje zatrzymania działania aplikacji

Oto jak wygląda kod mojej aplikacji:

namespace app1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            DateTime localDate = DateTime.Now;
            this.textBox1.Text = localDate.ToString("yyyy-MM-dd");
        }

        private void button1_Click(object sender, EventArgs e)
        {
            this.button1.Text = "Trwa generowanie";
            var mysqlManager = new mysqlManager();
            mysqlManager.saveManager(this.textBox1.Text);
        }
    }

    public class mysqlManager
    {
        public void saveManager(string date)
        {
            string csv = "header;header2\r\n";
            string sql = "SELECT * FROM ...";

            StringBuilder errorMessages = new StringBuilder();

            string connectionString = "Data Source=";

            using (SqlConnection connection = new SqlConnection(connectionString))
            {
                SqlCommand command = new SqlCommand(sql, connection);
                try
                {
                    command.CommandTimeout = 300;
                    command.Connection.Open();
                    SqlDataReader dataReader;
                    dataReader = command.ExecuteReader();
                    while (dataReader.Read())
                    {

                        for (int i = 0; i < 20; i++)
                        {
                            string source = i != 18 ? dataReader.GetValue(i).ToString() : "";
                            csv += Regex.Replace(source.Replace("\r", " ").Replace("\n", " ").Replace("\r\n", " ").Replace("\t", " "), @"[ ]{2,}", " ");
                            csv += i < 19 ? ";" : "\r\n";
                        }
                    }
                    command.Connection.Close();
                }
                catch (SqlException ex)
                {
                    for (int i = 0; i < ex.Errors.Count; i++)
                    {
                        errorMessages.Append("Index #" + i + "\n" +
                            "Message: " + ex.Errors[i].Message + "\n" +
                            "LineNumber: " + ex.Errors[i].LineNumber + "\n" +
                            "Source: " + ex.Errors[i].Source + "\n" +
                            "Procedure: " + ex.Errors[i].Procedure + "\n");
                    }
                    Console.WriteLine(errorMessages.ToString());
                    Console.ReadKey();
                }
            }

            string directory = Directory.GetCurrentDirectory() + Path.DirectorySeparatorChar;
            string fileName = date + "_generated.csv";
            File.WriteAllText(directory + fileName, csv);
        }
    }
}

 

komentarz zamknięcia: Rozwiązano problem
komentarz 3 czerwca 2022 przez VBService Ekspert (252,660 p.)

BTW,

jeżeli plik ma zostać zapisany w tej samej lokalizacji co aplikacja, można też zapisać

File.WriteAllText(date + "_generated.csv", csv);

 

2 odpowiedzi

+2 głosów
odpowiedź 2 czerwca 2022 przez Velta Maniak (52,010 p.)
wybrane 2 czerwca 2022 przez troian1337
 
Najlepsza

Uczyń event dla przycisku asynchronicznie wykonywanym. Najprościej możesz w taki sposób:

private async Task button1_Click(object sender, EventArgs e)
{
    this.button1.Text = "Trwa generowanie";
    var mysqlManager = new mysqlManager();
    await Task.Run(() => 
    {
        mysqlManager.saveManager(this.textBox1.Text); // raczej ta linijka wykonuje się dość długo ;p
    });
}

Najlepiej będzie oczywiście uczynić tylko tę logikę asynchroniczną, która tego naprawdę potrzebuje – chociażby użyć metod SqlConnection.OpenAsyncSqlCommand.ExecuteReaderAsync czy SqlDataReader.ReadAsync:

public abstract class mysqlManager
{
	public async Task saveManager(string date)
	{
		string csv = "header;header2\r\n";
		string sql = "SELECT * FROM ...";

		StringBuilder errorMessages = new StringBuilder();

		string connectionString = "Data Source=";

		using (SqlConnection connection = new SqlConnection(connectionString))
		{
			SqlCommand command = new SqlCommand(sql, connection);
			try
			{
				command.CommandTimeout = 300;
				await command.Connection.OpenAsync();
				SqlDataReader dataReader;
				dataReader = await command.ExecuteReaderAsync();
				while (await dataReader.ReadAsync())
				{
					for (int i = 0; i < 20; i++)
					{
						string source = i != 18 ? dataReader.GetValue(i).ToString() : "";
						csv += Regex.Replace(source.Replace("\r", " ").Replace("\n", " ").Replace("\r\n", " ").Replace("\t", " "), @"[ ]{2,}", " ");
						csv += i < 19 ? ";" : "\r\n";
					}
				}
				command.Connection.Close();
			}
			catch (SqlException ex)
			{
				for (int i = 0; i < ex.Errors.Count; i++)
				{
					errorMessages.Append("Index #" + i + "\n" +
						"Message: " + ex.Errors[i].Message + "\n" +
						"LineNumber: " + ex.Errors[i].LineNumber + "\n" +
						"Source: " + ex.Errors[i].Source + "\n" +
						"Procedure: " + ex.Errors[i].Procedure + "\n");
				}
				Console.WriteLine(errorMessages.ToString());
				Console.ReadKey();
			}
		}
	}
}

BTW, możesz uprościć tworzenie ścieżki zapisu pliku:

string filePath = Path.Combine(Directory.GetCurrentDirectory(), date + "_generated.csv");

EDIT: usunięcie zbędnych linijek w drugim wycinku kodu.

komentarz 2 czerwca 2022 przez troian1337 Użytkownik (720 p.)
Super! Tego właśnie potrzebowałem, kolejna przydatna wiedza na przyszłość :)

Mam jeszcze jedno pytanie, w jaki sposób mogę zakomunikować użytkownikowi o zakończeniu generowania pliku?

Z klasy mysqlManager nie mogę operować na elementach z innej klasy(  this.button1.Text = "Zakończono generowanie"; ).

No chyba że można zastosować tutaj metodę rozszerzenia jak extends dla php?

Zdaje sobie sprawę że to dość zapewne trywialne pytanie ale nawet nie bardzo wiem jak je sformułować aby poszukać czegoś w google na ten temat.
1
komentarz 2 czerwca 2022 przez Velta Maniak (52,010 p.)

Proponowałbym jeden z poniższych sposobów:

  • użycie osobnej kontrolki etykiety (Label) – po prostu zmieniasz właściwość Text,
  • wyświetlenie okna komunikatu (MessageBox) – możesz wyświetlić tytuł, treść, a także dobrać przyciski oraz ikonę. Na przykład:
MessageBox.Show("Pomyślnie wygenerowano plik.", "Sukces!", MessageBoxButtons.OK, MessageBoxIcon.Information);

 

1
komentarz 2 czerwca 2022 przez troian1337 Użytkownik (720 p.)
Ehh brawo ja, o najprostszym rozwiązaniu nie pomyślałem.

Bardzo dziękuje za pomoc :)
0 głosów
odpowiedź 2 czerwca 2022 przez VBService Ekspert (252,660 p.)
edycja 3 czerwca 2022 przez VBService

Możesz użyć Application.DoEvents - chociaż generalnie nie jest to zalecane.

Raczej zalecane jest użycie np. BackgroundWorker.


EDIT:  

Podobne pytania

0 głosów
0 odpowiedzi 156 wizyt
0 głosów
1 odpowiedź 443 wizyt
0 głosów
1 odpowiedź 226 wizyt
pytanie zadane 12 listopada 2022 w C# przez Beginner555 Obywatel (1,760 p.)

92,539 zapytań

141,382 odpowiedzi

319,477 komentarzy

61,928 pasjonatów

Motyw:

Akcja Pajacyk

Pajacyk od wielu lat dożywia dzieci. Pomóż klikając w zielony brzuszek na stronie. Dziękujemy! ♡

Oto polecana książka warta uwagi.
Pełną listę książek znajdziesz tutaj.

Akademia Sekuraka

Kolejna edycja największej imprezy hakerskiej w Polsce, czyli Mega Sekurak Hacking Party odbędzie się już 20 maja 2024r. Z tej okazji mamy dla Was kod: pasjamshp - jeżeli wpiszecie go w koszyku, to wówczas otrzymacie 40% zniżki na bilet w wersji standard!

Więcej informacji na temat imprezy znajdziecie tutaj. Dziękujemy ekipie Sekuraka za taką fajną zniżkę dla wszystkich Pasjonatów!

Akademia Sekuraka

Niedawno wystartował dodruk tej świetnej, rozchwytywanej książki (około 940 stron). Mamy dla Was kod: pasja (wpiszcie go w koszyku), dzięki któremu otrzymujemy 10% zniżki - dziękujemy zaprzyjaźnionej ekipie Sekuraka za taki bonus dla Pasjonatów! Książka to pierwszy tom z serii o ITsec, który łagodnie wprowadzi w świat bezpieczeństwa IT każdą osobę - warto, polecamy!

...