Avatar billede bildsoe Nybegynder
31. maj 2011 - 12:45 Der er 14 kommentarer og
2 løsninger

Backgroundworker og databaseadgang

Hej

Jeg har lavet en forholdsvis simpel Winforms app i c# der henter noget data i en database og viser det på skærmen. For at systemet ikke skal hænge har jeg lagt databasetilgangen ud i en backgroundworker, men nu opstår problemet. De forskellige queries konflikter. F.eks. henter jeg info om menunavne osv i en tabel og noget tekstindhold i en anden tabel. Fordi de har hver deres backgroundworker så når den at gå i gang med at hente tekstindholdet selvom den ikke er færdig med at hente menunavne osv. Er der en bestemt tilgang man normalt bruger? Kan man tjekke om en connection er åben og vente til den bliver lukket. Mit alternative er at bruge RunWorkerCompleted-event'en til at sætte næste databasetilgang igang. Dette virker dog meget omstændigt og uelegant.
Avatar billede Red0z Nybegynder
31. maj 2011 - 15:38 #1
Kan du evt. lægge koden ud så vi kan se hvordan det hænger sammen indtil videre? :)
Avatar billede janus_007 Nybegynder
31. maj 2011 - 21:02 #2
Hvilken version af .Net kører du?
Avatar billede oneeighty Nybegynder
31. maj 2011 - 21:16 #3
Kunne man evt køre det hele med samme backgroundworker?

Eksempel:

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Globalization;
using System.ComponentModel;

namespace TestConsole
{
    public interface ITask
    {
        void Execute();
        bool HasBeenExecuted { get; }
        object Result { get; }
    }

    class WorkerTask<T> : ITask
    {
        private readonly Func<T> _work;

        public WorkerTask(Func<T> work)
        {
            _work = work;
        }

        public void Execute()
        {
            Result = _work.Invoke();
            HasBeenExecuted = true;
        }

        public object Result { get; private set; }
        public bool HasBeenExecuted { get; private set; }
    }


    class Program
    {
        static void Main()
        {
            BackgroundWorker worker = new BackgroundWorker();
            worker.DoWork += DoWork;
            worker.RunWorkerCompleted += WorkerCompleted;

            var tasks = new List<ITask>();
           
            // add some tasks for the backgroundworker to do
            tasks.Add(new WorkerTask<string>(() => "database operation"));
            tasks.Add(new WorkerTask<int>(() => 15));
            worker.RunWorkerAsync(tasks);

            Console.ReadKey();
        }

        static void DoWork(object sender, DoWorkEventArgs e)
        {
            if (!(e.Argument is IList<ITask>))
                return;

            var tasks = e.Argument as IList<ITask>;

            // execute tasks sequentially
            foreach (var task in tasks)
                task.Execute();

            e.Result = tasks;
        }

        static void WorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            if (!(e.Result is IList<ITask>))
                return;

            var tasks = e.Result as IList<ITask>;

            foreach (var task in tasks.Where(t => t.HasBeenExecuted))
            {
                // do stuff with results
                Console.WriteLine(task.Result);
            }
        }
    }
}
Avatar billede bildsoe Nybegynder
01. juni 2011 - 10:31 #4
@Red0z - kan desværre ikke lægge hele koden ud.

@janus_007 - jeg mener det er version 4, men er ikke helt sikkert, ved du hvordan jeg kan tjekke det?

@oneeighty - umiddelbart ville det være en god løsning til at sørge for at der ikke er konfliktende tilgange til databasen, men en del af min idé var også at lade dele af mit gui og indhold loade separat, så app'en ikke hænger men brugeren stadig kan gøre noget.
Avatar billede bildsoe Nybegynder
01. juni 2011 - 11:26 #5
@oneeighty:

Er lidt i tvivl om, hvor jeg placerer udførelsen af selve databasequery'en henne. Kan det godt passe at jeg placerer det jeg vil have den til at udføre i denne metode:

public WorkerTask(Func<T> work)
        {
            _work = work;
        }
Avatar billede bildsoe Nybegynder
01. juni 2011 - 11:37 #6
Følgende er et eksempel på, hvad jeg f.eks. gerne vil kunne bruge en backgroundworker til:

caseDB.casesDataTable cases;

cases = _casesAdapter.GetData();

cases1.resetBoxes();

cases1.selectedBox = activeBox;

int counter = 0;

foreach (caseDB.casesRow casesRow in cases)
{
  int caseJobs = (int)_jobsAdapter.JobCount(casesRow.caseId);
  int caseJobsDone = (int)_jobsAdapter.finishCount(casesRow.caseId);

  float caseWorkStatus = ((float)caseJobsDone / (float)caseJobs) * 100F;

  bool errorState;
 
  if (casesRow.caseState == 3)
  {
      errorState = true;
  }
  else
  {
      errorState = false;
  }


  int colNum = (counter / 4);

  int rowNum = (counter % 4);

  cases1.createBoxes(casesRow.caseName, casesRow.caseId, (int)caseWorkStatus, errorState, rowNum, colNum);

  counter++;
}

Hvordan vil jeg kunne sende databasetilgangene i dette eksempel til backgroundworker ved brug af dit eksempel? Har iøvrigt brugt de tableAdapters der er indbygget i VSE2008, er det bedre at lave databaseadgangen helt manuelt?
Avatar billede oneeighty Nybegynder
01. juni 2011 - 22:34 #7
Jeg er ikke helt sikker på jeg er helt med på din kode, men et bud kunne være således

http://pastebin.com/6DWDrGnB
Avatar billede janus_007 Nybegynder
02. juni 2011 - 21:21 #8
Hej bildsoe

Hvis du bruger .Net 4, så ville jeg helt sikkert anbefale dig at kigge på Task og ContinueWith, ovenstående vil ret nemt kunne styres som du gerne vil :)
Avatar billede bildsoe Nybegynder
06. juni 2011 - 10:21 #9
@oneeighty:

Hvis jeg bruger denne metode, er jeg jo vel nærmest nødt til at have en global BackgroundWorker som håndterer alle de tasks der måtte opstå. Men hvordan behandler jeg så resultatet forskelligt alt efter, hvilken task det er den bliver sat i gang med? Og mit problem er jo, at tasks forekommer når brugeren klikker, så jeg jo tilføjer tasks til listen løbende. Kan dette lade sig gøre?

@janus_007:

Jeg prøver at kigge på Task og ContinueWith. Tak.
Avatar billede bildsoe Nybegynder
06. juni 2011 - 11:20 #10
@janus_007:

Jeg har kun mulighed for at bruge .Net Framework 3.5 har jeg fundet ud af, så jeg har ikke mulighed for at benytte Task.
Avatar billede oneeighty Nybegynder
06. juni 2011 - 20:15 #11
Du kan behandle resultatet ved at kontrollere hvilken type resultatet er

foreach (var task in tasks)
{
    if (task.Result is caseDB.casesDataTable)
    {
        var result = task.Result as caseDB.casesDataTable;
        // gør noget med result
    }   
}

Jeg er lidt usikker på hvad det er du ønsker helt konkret umiddelbart ser jeg det ikke nødvendigt med en "global"-backgroundworker ej eller at det ikke kan bruges selvom der bliver klikket på knapper.

Der kan uden tvivl laves bedre og mere generiske løsninger end den jeg har præsenteret dig for, det var som sagt hurtigt lavet og mest ment som inspiration.
Avatar billede janus_007 Nybegynder
06. juni 2011 - 20:20 #12
Surt... jamen så er du lidt på herrens mark :) En thread er ikke garanteret den eksekveringsorden du efterlyser, så du må selv bygge den som du også er inde på.

Jeg ville kigge på Reactive Extensions og se om der ikke var noget : http://msdn.microsoft.com/en-us/data/gg577609

her er noget med lidt ordering tror jeg.. : http://stackoverflow.com/questions/4715850/using-rx-to-block-and-possibly-timeout-on-an-asynchronous-operation

Jeg har dog aldrig arbejdet med Rx :-|
Avatar billede bildsoe Nybegynder
07. juni 2011 - 11:20 #13
Hmm... ok jeg kan se på det hele at jeg lige har lavet en "monster" newbie fejl. Jeg fik ikke lukket database connection og det går det jo lidt svært at udnytte de automatiske concurrency mekanismer.

@oneeighty:
Jeg får helt sikkert brug for at kunne lave denne form for task kontrol senere så tak for hjælpen.

@janus_007.
Jeg går over til .Net 4.0 snart så regner med at jeg kan kigge på task og continueWith til den tid.

Jeg vil gerne give jer begge point. Kan det lade sig gøre i samme spørgsmål eller skal jeg lave et nyt spm som en af jer kan svare på?
Avatar billede Syska Mester
07. juni 2011 - 12:47 #14
Det kan lade sig gøre i dette ... de skal bare begge to smide et svar.

mvh
Avatar billede janus_007 Nybegynder
07. juni 2011 - 19:55 #15
Tjiptjoo :=}
Avatar billede oneeighty Nybegynder
07. juni 2011 - 20:58 #16
svar
Avatar billede Ny bruger Nybegynder

Din løsning...

Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.

Loading billede Opret Preview
Kategori
IT-kurser om Microsoft 365, sikkerhed, personlig vækst, udvikling, digital markedsføring, grafisk design, SAP og forretningsanalyse.

Log ind eller opret profil

Hov!

For at kunne deltage på Computerworld Eksperten skal du være logget ind.

Det er heldigvis nemt at oprette en bruger: Det tager to minutter og du kan vælge at bruge enten e-mail, Facebook eller Google som login.

Du kan også logge ind via nedenstående tjenester