[.NET|C#] Opzetten van een DAL

Pagina: 1
Acties:
  • 166 views sinds 30-01-2008
  • Reageer

  • Sybr_E-N
  • Registratie: December 2001
  • Laatst online: 20:01
Momenteel ben ik bezig met een webapplicatie in .NET waarbij ik een scheiding wil maken tussen Presentatie, Bussines Logic en Data Acces.

Voorals nog concentreer ik me hier op de Data Acces laag. Het is de bedoeling dat gedeelten van de webapplicatie herbruikbaar zijn. Zo wil ik graag dat er een mogelijkheid moet komen om te switchen van database, zonder veel code te moeten wijzigen. Nu heb ik bedacht dat het wellicht handig zou zijn om dat door een aparte class te laten afhandelen. Na veel geinventariseer heb ik besloten om mijn van DAL een Abstract Factory te maken.

Voor testpurposes heb ik het een en ander opgezet en een WinForm (Ik had nog geen locale webserver draaien) gemaakt in Visual Studio .NET 2003, maar het moet ook zo te gebruiken zijn in een webapp neem ik aan. De klassen die je ziet zijn voor het grootste gedeelte kompleet nutteloos, maar daar gaat het even niet om. Het gaat meer om de technieken erachter.

Inmiddels heb ik al (heel) veel informatie over dit onderwerp doorgenomen, over DAL's en Design Patterns e.d.:
[Kleine greep]
Software Design Patterns
J2EE - Data Acces Object
[Alg|.NET] Design van een generiek DbHelper object
[Alg/.NET] Architectuur smart client - Part II concrete case
Design Patterns (het GoF-boek)
[/Kleine greep]

Aangezien ik geen fatsoenlijke tekentools hier heb, geef ik mijn (relevante) stukken source. Omdat ik redelijk nieuw ben in de wereld van .NET heb ik (voor mij) simpel gehouden en zoveel mogelijk van dezelfde namespace in 1 bestand gedrukt. Mijn opzet ziet er zo uit:

DBFactory is een abstracte klasse en wordt geerfd door elke andere database klasse (MSSQL en Oracle hier.). De klasse zelf is een Singleton. Het soort database wat ik werkelijk gebruik, definieer ik in app.config of web.config voor het web. Als je al wilt swappen van database hoef je dat maar op 1 plek te wijzigen. Ook geef ik een geraamte om de juiste type klassen te kunnen gebruiken.

DBFactory.cs
C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
using System;
using System.Configuration;
using TestCompX.Interface;
using TestCompX.DALOracle;
using TestCompX.DALSqlServer;

namespace TestCompX {
  public abstract class DBFactory {
    protected static DBFactory _factory;
    private static string dbtype;
    private static string connectionString;

    public static DBFactory getInstance() {
      dbtype = ConfigurationSettings.AppSettings["DBTYPE"];
      connectionString = ConfigurationSettings.AppSettings["DBConnection"];

      if (_factory == null) {
        switch (dbtype) {
          case "MSSQL" : _factory = new MSSQLFactory(connectionString);
                         break;
          case "Oracle" : _factory = new OracleFactory(connectionString);
                          break;
        }
      }
      return _factory;
    }

    public abstract IVraag getVraag(int i);

    public abstract IAntwoord getAntwoord();

    public abstract IAntwoord getAntwoord(int id);
  }
}


Hier heb ik mijn Sql Server specifieke zaken gezet, deze factory returned de juiste klassen. Voor Oracle heb eenzelfde bestand, alleen returned getVraag een ietwat andere string.

DALSqlServer.cs
C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
using System;
using TestCompX.Interface;

namespace TestCompX.DALSqlServer {

  public class MSSQLFactory : DBFactory {

    private string connectionString;

    public MSSQLFactory() {
    }

    public MSSQLFactory(string connectionString) {
      this.connectionString = connectionString;
    }
    // Deze methode wordt vanuit het form uiteindelijk dan aangeroepen.
    // Maakt een nieuwe vraag met een int en een string.
    public override IVraag getVraag(int i) {
      return new Vraag(i, "Dit is een MSSQL Server test");
    }

    public override IAntwoord getAntwoord() {
      return new Antwoord();
    }

    public override IAntwoord getAntwoord(int id) {
      return null;
    }

  }

  /* IMPLEMENTATION */
  public class Vraag : IVraag {
    private string vraag;
    private int id;

    public Vraag() {
      this.vraag = "";
      this.id = 0;
    }

    public Vraag(int id, string vraag) {
      this.vraag = vraag;
      this.id = id;
    }

    public string getVraag() {
      return this.vraag;
    }
        
    public int InsertVraag(string vraag) {
      return 1;
    }

    public bool Update(int id) {
      return true;
    }

    public bool Delete(int id) {
      return false;
    }

    public IVraag SearchVraag(int id) {
      return new Vraag(1, "test");
    }

    public void SelectAllVragen() {
            
    }
  }

  public class Antwoord : IAntwoord {
    public Antwoord() {

    }
  }

}



Dit zijn de interfaces voor de concrete klassen welke gereturned worden door een van de concrete factory's.

Interfaces.cs
C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
namespace TestCompX.Interface {
  public interface IVraag {
    int InsertVraag(string vraag);
    bool Update(int id);
    bool Delete(int id);
    IVraag SearchVraag(int id);
    void SelectAllVragen();
    string getVraag();
  }

  public interface IAntwoord {

  }

}



Form1.cs
C#:
1
2
3
4
5
6
7
8
9
10
11
12
  ...
  public Form1() {
    //
    // Required for Windows Form Designer support
    //
    InitializeComponent();
    factory = DBFactory.getInstance();

    IVraag test = factory.getVraag(1);
    label1.Text = test.getVraag();  // Ik krijg mooi "Dit is een MSSQL Server test" om mijn scherm
   }
   ...


Alleen nu rijst er bij mij de vraag waar maak ik nou de connectie met de database. Volgens mij moet ik in mijn concrete Vraag en Antwoord klassen lekker SqlCommand en SqlConnection maken etc. Alleen nu sla ik die connection string op in mijn MSSQL factory en kan ik nooit een connectie opbouwen, tenzij ik weer de DBFactory klasse aanroep om alleen de connection string op te halen.

Maw, hoe krijg ik dit fatsoenlijk voor elkaar. Om nou voor elke concrete klasse (zoals Vraag) een aparte constructor op te nemen de connectionstring te initialiseren vind ik ook zo raar. Volgens mij hoort dat helemaal niet zo in een Abstract Factory. Ik maak volgens mij ergens een denkfout, maar waar?

  • whoami
  • Registratie: December 2000
  • Laatst online: 00:19
Ik zou het zo doen:

Een DBGatewayFactory object maken, die enkel 'gateways' of 'repositories' returned:

code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
public class DBGatewayFactory
{

    private IGatewayCreator   creator;
    private ITransactionHandlerFactory transFact;

    private DBGatewayFactory()
    {
            if( spull_in_config == "MSSQL" )
            {
                creator = new SqlGatewayCreator;
                transFact = new SqlTransactionHandlerFactory();
            }
            else if( .... )
            {
                creator = new OracleGatewayCreator;
                transFact = new OracleTransactionhandlerFactory();
            }
    }

    private static DBGatewayFactory _instance;

    public static DBGatewayFactory   Instance
    {
          // singleton impl.
          if( _instance == null ) _instance = new .... ();
         return _instance;
    }
    
    public IPersoonGateway    GetPersoonGateway()
    {
            return creator.GetPersonGateway();
    }

}

In mijn voorbeeld is creator dus van het type IGatewayCreator -een interface dus die bepaalt welke 'gateways' er kunnen gecreeërd worden, DBGatewayFactory doet eigenlijk niets anders dan een gateway voor een specifieke DB implementatie returnen.

Voorbeeld van een gateway:
code:
1
2
3
4
5
6
7
8
9
10
11
12
public class SqlServerPersonGateway : IPersoonGateway
{
       public PersonCollection   FindAllPersons( transactionHandler )
       {
              // SQL Server specifieke SQL query die alle personen ophaalt.
       }
 
        public Person GetPerson( int id )
        {
             // SQL Server specifieke query die persoon met gegeven id ophaalt.
        }
}


Nu om je vraag ivm de connectie string te beantwoorden: eigenlijk moet er een bovenliggende laag zijn die de transacties etc.. .afhandelt, want de DAL zou dit niet mogen doen.
Het zou dan ook de bovenliggende laag moeten zijn die de connectionstring moet hebben.

code:
1
2
3
4
5
6
7
8
9
10
11
public class PersonRepository
{
        public PersonCollection FindAllPersons()
        {

                 ITransactionHandler th = DBGatewayFactory.Instance.GetTransactionHandler(connectionString);

                 return DBGatewayFactory.Instance.GetPersoonGateway().FindAllPersons(th);

        }
}


Die ITransactionHandler is dus eigenlijk een 'db-helper' die verantwoordelijk is voor het uitvoeren van jouw queries. Die heeft dan ook de connectionstring.
Die TransactionHandler is nodig omdat je in je niet in je DAL je transacties wilt beheren. Je DAL voert gewoon queries uit, en zou niets mogen weten van transacties. het kan nl. zijn dat je, voor het saven van een persoon bv, meerdere queries wilt uitvoeren (en dus meerdere calls wilt doen naar 1 of meerdere gateway objecten), en dit allemaal binnen 1 transactie.

8)7 ik hoop dat ik een beetje duidelijk ben. 't Is nl. zware materie voor zo om 16h.

https://fgheysels.github.io/


  • whoami
  • Registratie: December 2000
  • Laatst online: 00:19
Eigenlijk zou het misschien nog beter zijn als m'n gateways DataTables, DataSets, Datarows, etc... returnen, en m'n repositories adhv die gegevens een (of meerdere) business object(s) maken.

https://fgheysels.github.io/


  • Sybr_E-N
  • Registratie: December 2001
  • Laatst online: 20:01
whoami schreef op woensdag 27 april 2005 @ 15:47:
't Is nl. zware materie voor zo om 16h.
Agree.

Maar tot zover ik het begrijp zou jij er nog een extra 'laag' tussen de database en de concrete klassen die queries voeren zetten. Ik probeer het mezelf even voor te stellen: class Vraag (of elke methode in die klasse) heeft een instantie van een (algemene, wel database specifiek) dbhelper/sqlhelper object. Je geeft als het ware je SQL-query door aan die helper.

Je noemt nu allerlei dingens als gateways, repositories, transactie handlers. Ik zie even niet meer hoe ik dit alles tot 1 samenwerkend geheel zou kunnen krijgen. Ik krijg wel het idee dat de applicatie er gestructureerder erop wordt.

  • whoami
  • Registratie: December 2000
  • Laatst online: 00:19
Sybr_E-N schreef op woensdag 27 april 2005 @ 16:24:
[...]

Maar tot zover ik het begrijp zou jij er nog een extra 'laag' tussen de database en de concrete klassen die queries voeren zetten. Ik probeer het mezelf even voor te stellen: class Vraag (of elke methode in die klasse) heeft een instantie van een (algemene, wel database specifiek) dbhelper/sqlhelper object. Je geeft als het ware je SQL-query door aan die helper.
Ik zie het als volgt:
Ik heb classes die enkel de queries bevatten naar m'n database toe, en die die queries gewoon uitvoeren.
(Bv, voor iedere tabel een class die de nodige methods bevat om de vereiste gegevens op te halen, te saven, etc...). Dit zijn de 'gateways'.
Zo'n gateway wil je in allerlei situaties kunnen gebruiken, en daarom mag die gewoon niets afweten ivm transacties.

Daarom heb je nog een laag daarboven nodig (Repositories). Een repository spreekt één of meerdere gateways aan om de gegevens die hij wil te verkrijgen of te saven. Het is ook zo'n repository die de transacties beheert.

De transactionHandler is eigenlijk het object dat je kunt aanzien als een 'DbHelper'. deze bevat bv een transaction (SqlTransaction) object, een connection (SqlConnection) object, en dit object gebruik je in je Repository en in je Gateway. In je repository kan je bv zeggen: start de transactie, en dan geef je dat object mee aan je gateway die het gebruikt om de query uit te voeren.

Jouw applicatie spreekt dan uiteindelijk tegen een repository:

code:
1
2
3
4
private btnFindPersons_Click(object sender, EventArgs e)
{
   PersonCollection persons =   myPersonRepository.FindAllPersons();
}
Je noemt nu allerlei dingens als gateways, repositories, transactie handlers. Ik zie even niet meer hoe ik dit alles tot 1 samenwerkend geheel zou kunnen krijgen. Ik krijg wel het idee dat de applicatie er gestructureerder erop wordt.
I agree, 'k ben niet meer helemaal fris.

[ Voor 13% gewijzigd door whoami op 27-04-2005 16:31 ]

https://fgheysels.github.io/


  • PhoneTech
  • Registratie: Mei 2000
  • Laatst online: 14:13
Waarom maak je geen gebruik van de Data Base Application Blocks van de Enterprise library. Daar zit al de functionaliteti om makkelijk te switchen van database al in namelijk...

De Application block lijkt heel erg veel op wat je nu probeert te realiseren...Met als voordeel dat het al helemaal uit is gewerkt, en dat de SQL code automatisch naar de geschikte provider wordt omgezet.

  • Sybr_E-N
  • Registratie: December 2001
  • Laatst online: 20:01
PhoneTech schreef op woensdag 27 april 2005 @ 16:29:
Waarom maak je geen gebruik van de Data Base Application Blocks van de Enterprise library. Daar zit al de functionaliteti om makkelijk te switchen van database al in namelijk...

De Application block lijkt heel erg veel op wat je nu probeert te realiseren...Met als voordeel dat het al helemaal uit is gewerkt, en dat de SQL code automatisch naar de geschikte provider wordt omgezet.
Klopt, die ben ik ook tegengekomen. :) Maar het leek me interresant en leerzamer (is voor een afstudeerproject namelijk.) om zelf iets te ontwerpen en bouwen, en niet altijd te grijpen naar kant-en-klaar oplossigen. Het is ook niet de bedoeling dat het een gigantische Enterprise applicatie wordt.

Het project waar ik aan werkt is vooral een pilot project(je) aangezien niemand hier op het stagebedrijf nooit met .NET heeft gewerkt. Een enkeling om te testen, maar nog niets serieus. Daarom leek het me leuk om ook zelf een DAL op te nemen in mijn project, en dan die zelf te implementeren. (De DAL is niet het hoofdonderdeel van mijn project) Ik ben nog groentje in de wereld van de software engineering, vandaar mijn keus om het zelf te doen en geen builing block te gebruiken.

[/tot zover de achtergrond informatie]

  • EfBe
  • Registratie: Januari 2000
  • Niet online
1 tip: als je zelf een DAL gaat bouwen ben je, sorry, dom bezig. Een DAL is standaard werk, je pakt een 3rd party toolkit of een generator en je bouwt daarmee je DAL. Een goede DAL met de hand bouwen kost nl. erg veel tijd, vergeet dat niet. Ik wil niet over 'een paar jaar' beginnen, maar je bent zeker 6 maanden zoet met het bouwen van een solide DAL met alle mogelijke functionaliteit die je kunt tegenkomen in de hogere tiers, mocht je het met de hand doen.
PhoneTech schreef op woensdag 27 april 2005 @ 16:29:
De Application block lijkt heel erg veel op wat je nu probeert te realiseren...Met als voordeel dat het al helemaal uit is gewerkt, en dat de SQL code automatisch naar de geschikte provider wordt omgezet.
haha, dream on. :) Dat DAAB geval zet helemaal niets om. Als jij een insert wilt doen met een identity column in SQLServer, maakt hij er echt geen sequenced query met select seq.nextval from dual; van op oracle hoor, maak jezelf geen illusies. Ook parameters met unique_identifier als type voor sqlserver kan hij echt niet omzetten naar een oracle parameter.

[ Voor 44% gewijzigd door EfBe op 27-04-2005 23:26 ]

Creator of: LLBLGen Pro | Camera mods for games
Photography portfolio: https://fransbouma.com


  • whoami
  • Registratie: December 2000
  • Laatst online: 00:19
EfBe schreef op woensdag 27 april 2005 @ 23:23:
1 tip: als je zelf een DAL gaat bouwen ben je, sorry, dom bezig. Een DAL is standaard werk, je pakt een 3rd party toolkit of een generator en je bouwt daarmee je DAL. Een goede DAL met de hand bouwen kost nl. erg veel tijd, vergeet dat niet.
Jij praat natuurlijk wel een beetje voor je eigen winkel. :P
Je hebt gelijk dat je zegt dat een goeie DAL bouwen veel tijd kost, maar ik denk niet dat het onoverkomelijk is. Tijd is natuurlijk wel geld, en de tijd die je kunt besteden aan het werkelijke probleem wordt wel groter, je spaart resources uit, etc..... helemaal waar.
Echter, als men een applicatie bouwt, die niet n-tier is, dan kruipt er ook tijd in het schrijven van sql queries, etc....
haha, dream on. :) Dat DAAB geval zet helemaal niets om. Als jij een insert wilt doen met een identity column in SQLServer, maakt hij er echt geen sequenced query met select seq.nextval from dual; van op oracle hoor, maak jezelf geen illusies. Ook parameters met unique_identifier als type voor sqlserver kan hij echt niet omzetten naar een oracle parameter.
Eens, DAAB gaat er afaik enkel voor zorgen dat de juiste 'driver' (SqlClient, OleDbClient, ...) genomen wordt.
Het spul is niet zo slim dat hij de juiste specifieke t-sql of pl/sql of whatever kan genereren.

https://fgheysels.github.io/


  • EfBe
  • Registratie: Januari 2000
  • Niet online
whoami schreef op donderdag 28 april 2005 @ 09:15:
[...]
Jij praat natuurlijk wel een beetje voor je eigen winkel. :P
Ongeacht je smilie vind ik het een rotopmerking. Net of ik niet weet waarover ik praat. Het zal me echt boeien wat hij gaat gebruiken, al wat ik wil is helpen, en daarbij wil ik niet gezeik horen dat ik voor mn eigen winkel praat, bevooroordeeld ben of wat dan ook. :)
Je hebt gelijk dat je zegt dat een goeie DAL bouwen veel tijd kost, maar ik denk niet dat het onoverkomelijk is. Tijd is natuurlijk wel geld, en de tijd die je kunt besteden aan het werkelijke probleem wordt wel groter, je spaart resources uit, etc..... helemaal waar.
Echter, als men een applicatie bouwt, die niet n-tier is, dan kruipt er ook tijd in het schrijven van sql queries, etc....
Het houdt niet op bij het schrijven van SQL queries. Je moet code hebben die die queries uitvoert, die die queries als een API aanbiedt aan andere code, die de data transporteert naar de verschillende lagen, die changes trackt in de PL bv, die met BL code kan samenwerken etc.

De TS moet het natuurlijk zelf weten, het is zijn tijd maar aangezien het een afstudeerproject is, zou ik de tijd die daarvoor beschikbaar is echt wel ergens anders aan besteden.

Creator of: LLBLGen Pro | Camera mods for games
Photography portfolio: https://fransbouma.com


  • D4Skunk
  • Registratie: Juni 2003
  • Laatst online: 20-10-2025

D4Skunk

Kind of Blue

@ efbe & whoami : respect @ both... 8)
Voor zover ik jullie replies gevolgd heb binnen andere topics hebben jullie beiden heel veel verstand van software engineering etc.. Software engineren is geen exacte wetenschap; er bestaat meestal geen algemene waarheid, enkel bepaalde strekkingen, en iedereen heeft natuurlijk zijn persoonlijke voorkeur.
Dit geldt ook voor de TS : er zijn verschillende benaderingen, en uiteindelijk ben jij het die de keuze maakt...

Persoonlijk begrijp ik volledig waarom je je DAL zelf wenst te fabriceren :
ik wou vroeger ook van alles precies weten hoe het in zijn werk ging, en zeker zijn dat ik de volledige werking van het pakket van binnen en van buiten kende. Nu sta ik, gezien sommige deadlines, iets meer open voor third-party libs.

Wanneer je een pakket voor je studies ontwikkelt, is dit IMHO zeer zeker een goede benadering; je geeft nl. te kennen dat je inzicht hebt in de werking van een DAL, en je docenten zullen zeer waarschijnlijk onder de indruk zijn van de prachtige gestructureerde aanpak.
Anderzijds heb je misschien ook docenten die eerder te vinden zijn voor productiviteit ipv het model op zich...

Wanneer iemand zo'n DAL zelf ontwikkelt, is dit meestal een model dat samen met je ervaring meegroeit en verder evolueert. In die zin volg ik efbe dat het niet zo evident is om zoiets op te zetten.
Anderzijds volg ik ook whoami, gezien je het kan bekijken als een investering voor je toekomstige programma's...

Uiteindelijk moet je zelf de keuze maken, maar het kan inderdaad interessant zijn om bij andere personen wat inspiratie op te doen...
Veel succes !!

Verwijderd

Ik ben het volledig eens met d4skunk dat het bouwen van een eigen DAL een zeer leerzame ervaring kan zijn, ik denk echter wel dat je het nu niet op de juiste manier aanpakt door zelf bij nul te beginnen.

Mijn advies zou zijn om de gratis demo van llblgen te downloaden (http://www.llblgen.com/pages/demo.aspx) en te onderzoeken hoe de door dit programma gegenereerde API in elkaar steekt. Omdat het programma je standaard de mogelijkheid biedt met 2 paradigma's (service/transparent) te werken kun je snel zien wat de gevolgen van bepaalde keuzes zijn.

Op basis van je ervaringen kun je vervolgens alsnog beslissen een eigen DAL te bouwen, met het grote voordeel dat je hebt kunnen zien hoe het moet, wat de problemen zijn en hoe complex het kan zijn. Daarnaast kun je dan llblgen op je CV zetten, wat je bij het zoeken naar een baan wellicht een voordeel kan geven tov andere kandidaten, zowel omdat je het pakket kent maar zeker ook omdat je ermee aangeeft minder dan gemiddeld last te hebben van het "not built here syndrome" (iets waar ik zelf ook last van heb overigens).

Het komt niet iedere dag voor dat je ahw "binnen kunt kijken" bij een Microsoft C# MVP (=EfBe), ik zou zeker gebruik maken van die kans, ook omdat je weet dat je met eventuele vragen over de architectuur hier terecht kunt.

  • Sybr_E-N
  • Registratie: December 2001
  • Laatst online: 20:01
@EfBe:
Dank voor de tip. Jij denkt nu dat ik in 1 keer een complete DAL met alles erop en er aan, aan het maken ben. Dat is op dit moment, voor mijn opdracht hier, niet aan de orde. Ik had inmiddels ook al mijn vermoedens dat een complete fatsoenlijke DAL al veel tijd in gaat zitten. Desalniettemin is het wel een onderdeel wat ik kwa theorie ga meenemen in mijn eindverslag, daar wordt ik uiteindelijk dan wel weer op beoordeeld en niet alleen op de analyse (UML enzo) en source code die ik heb geschreven.

Hetzelfde geldt ook voor het gebruik van reeds bestaande alternatieven zoals de building blocks van Microsoft of je DAL laten maken door een 3rd party tool (bijvoorbeeld LLBGEN). Door de DAL klein te houden.

@D4Skunk:
Jij haalt de woorden uit mijn mond :)
Ik doe het meer omdat ik er graag wat van wil leren, door er zelf over na te denken, uitzoeken en maken. Zoveel 'echte' programma's heb ik nou ook weer niet gemaakt en op school zijn ze niet zo streng op je programma ontwerp. Dit leek mee een goed moment om eens wat meer ervaring op te doen. Tot nu toe vind het het erg leuk om dit alles uit te zoeken e.d.

Als het stagebedrijf echt een DAL wil hebben voor hun webapplicaties zullen die Software Engineers eerder kiezen voor reeds bestaande producten, in plaats van alles zelf uit te gaan lopen zoeken en maken. Dat gaat te veel tijd en geld kosten ben ik bang.

@whoami:
Om 09.00 is alles een stuk duidelijker dan om 16.00 :). De lijnen die je mij hebt gegevens heb ik aangenomen en zo ongeveer opgezet in geimplementeerd. Ik volg nu voor een groot gedeelte jouw idee zoals je het hierboven hebt beschreven, en herender aangepast aan wat ik al had. Het is nu mogelijk om gegevens uit de database te trekken.

@paullooijmans:
Ik zal het zeker de een dezer dagen een doornemen dat programma en wat voor code er uit gegenereerd.

@ mezelf
Het moet uiteindelijk een (kleine) DAL worden waarmee ik wat queries kan vuren op een database en niet veel meer dan dat. Wat ik van plan ben/ was, is een meer een testprogramma waarin het een en ander in verwerkt is zoals het gebruik van een DAL.

Wat ik nu heb is een meer een klein DAL-etje. Mijn webapplicatie gaat hooguit uit een stuk of wat, 5 ofzo, tabellen uit een database gebruiken. Om heel grof te zijn is een dal misschien voor dit project wel overkill, maar dat geeft niet. Het gaat om de gedachte achter de webapplicatie. De manier waarop het gebouwd is, rekening houden met dat het wel enigzins uitgebreid kan worden etc. De DAL is onderdeel van een pilot-project welke ik zelf weer heb bedacht. De manier waarop ik deze test(pilot) heb opgezet/bedacht zou voor de programmeurs hier een manier kunnen zijn, een kijk op (er zijn maar weing personen met OOP ervaring ben ik achter gekomen), om het echt framework annex webapplicatie te bouwen.

Werk verricht
Op dit moment het ik het een en ander gemaakt en werkt naar behoren. Het is misschien lang niet perfect, maar ik kan nu queries uitvoeren en gegevens op mijn scherm toveren.

whoami bedankt voor jouw oplossing, en alle anderen ook thnx voor de discussie. Het is zeker iets wat ik ga meenemen in mijn eindverslag.

  • D4Skunk
  • Registratie: Juni 2003
  • Laatst online: 20-10-2025

D4Skunk

Kind of Blue

* D4Skunk zou wel graag eens de source zien van je DAL zoals ie nu is :)

  • EfBe
  • Registratie: Januari 2000
  • Niet online
Sybr_E-N schreef op donderdag 28 april 2005 @ 14:36:
@EfBe:
Dank voor de tip. Jij denkt nu dat ik in 1 keer een complete DAL met alles erop en er aan, aan het maken ben. Dat is op dit moment, voor mijn opdracht hier, niet aan de orde. Ik had inmiddels ook al mijn vermoedens dat een complete fatsoenlijke DAL al veel tijd in gaat zitten. Desalniettemin is het wel een onderdeel wat ik kwa theorie ga meenemen in mijn eindverslag, daar wordt ik uiteindelijk dan wel weer op beoordeeld en niet alleen op de analyse (UML enzo) en source code die ik heb geschreven.
Ok, dan is het ok. Het gaat er mij niet om dat je meteen naar een toolkit moet grijpen, ongeacht welke dat is (gratis of commercieel), het is alleen zo dat tevaak mensen tijd verliezen in het bouwen van dingen die eigenlijk vrij repeterend zijn en saai. Een DAL die veel functionaliteit heeft is nl. veel werk, een DAL die bv alleen procedures aanroept is vrij simpel en gewoon veel typewerk. Almetal tijdverlies.
Hetzelfde geldt ook voor het gebruik van reeds bestaande alternatieven zoals de building blocks van Microsoft of je DAL laten maken door een 3rd party tool (bijvoorbeeld LLBGEN). Door de DAL klein te houden.
LLBLGen Pro is commercieel, dus is het voor jou wellicht beter om naar gratis alternatieven om te kijken. Voor je project is het wellicht aardig te lezen wat de verschillende uitgangspunten zijn en wat de voors/tegens zijn en dan kun je beter een afgewogen beslissing neerzetten in je verslag, wat je stagewerk (ik zei afstudeerproject eerder geloof ik) weer ten goede komt ;)
Bijvoorbeeld: http://weblogs.asp.net/fbouma/archive/2004/10/09/240225.aspx

Als leertraject kan een DAL leuk zijn, maar dan moet het wel een DAL met ballen zijn, een platte pannekoek die simpel procs called is dan niet echt een uitdaging. ;)
Wat ik nu heb is een meer een klein DAL-etje. Mijn webapplicatie gaat hooguit uit een stuk of wat, 5 ofzo, tabellen uit een database gebruiken. Om heel grof te zijn is een dal misschien voor dit project wel overkill, maar dat geeft niet. Het gaat om de gedachte achter de webapplicatie. De manier waarop het gebouwd is, rekening houden met dat het wel enigzins uitgebreid kan worden etc. De DAL is onderdeel van een pilot-project welke ik zelf weer heb bedacht. De manier waarop ik deze test(pilot) heb opgezet/bedacht zou voor de programmeurs hier een manier kunnen zijn, een kijk op (er zijn maar weing personen met OOP ervaring ben ik achter gekomen), om het echt framework annex webapplicatie te bouwen.
Omdat het een stage is voor je, zou ik het wel blijven zien als leertraject. Het is een traject waar je iets van moet opsteken. Dus ga het niet zien als een testflutje wat toch als test wordt gebruikt, want dan neem je waarschijnlijk veel shortcuts en dan leer je niets. Ik zou het als full application blijven zien en dat er maar 5 tables worden gebruikt is dan niet aan de orde. Een DAL gebruik je om de lagen erop het gemak te bieden dat wat zij moeten doen met de database met simpele statements kan. M.a.w.: dat wanneer jij in je middle tier (business logic laag (BL)) een 'customer' record nodig hebt, je dat makkelijk kunt laden en niet dat je dan eerst moet uitzoeken waar de connection string staat, en meer van die low level handel. Want dat kost tijd en het nare is, wanneer je ergens anders in je BL weer een customer record nodig hebt, je dezelfde code moet herhalen, wellicht op zoek moet naar "ik heb dat eerder gedaan, waar stond dat!?"..

Zolang je DAL dat oplost is het functioneel.

Creator of: LLBLGen Pro | Camera mods for games
Photography portfolio: https://fransbouma.com


  • Sybr_E-N
  • Registratie: December 2001
  • Laatst online: 20:01
D4Skunk schreef op donderdag 28 april 2005 @ 14:51:
* D4Skunk zou wel graag eens de source zien van je DAL zoals ie nu is :)
Ik post morgen wel even wat code, het is nu nog wat rommelig aangezien ik ook nog bezig ben met wat bussines classen e.d.
EfBe schreef op donderdag 28 april 2005 @ 15:23:
LLBLGen Pro is commercieel, dus is het voor jou wellicht beter om naar gratis alternatieven om te kijken. Voor je project is het wellicht aardig te lezen wat de verschillende uitgangspunten zijn en wat de voors/tegens zijn en dan kun je beter een afgewogen beslissing neerzetten in je verslag, wat je stagewerk (ik zei afstudeerproject eerder geloof ik) weer ten goede komt ;)
Bijvoorbeeld: http://weblogs.asp.net/fbouma/archive/2004/10/09/240225.aspx
Thnx, link bookmarked.
Als leertraject kan een DAL leuk zijn, maar dan moet het wel een DAL met ballen zijn, een platte pannekoek die simpel procs called is dan niet echt een uitdaging. ;)

Omdat het een stage is voor je, zou ik het wel blijven zien als leertraject. Het is een traject waar je iets van moet opsteken. Dus ga het niet zien als een testflutje wat toch als test wordt gebruikt, want dan neem je waarschijnlijk veel shortcuts en dan leer je niets. Ik zou het als full application blijven zien en dat er maar 5 tables worden gebruikt is dan niet aan de orde. Een DAL gebruik je om de lagen erop het gemak te bieden dat wat zij moeten doen met de database met simpele statements kan. ..."ik heb dat eerder gedaan, waar stond dat!?"..
Zolang je DAL dat oplost is het functioneel.
Het is wel een afstudeeropdracht hoor :) (laatste stage, afstuudeerstage, afstuudeeropdracht/project, iig laatste hindernis om mijn HI diploma te bemachtigen), de webapplicatie is eerder een onderdeel uit mijn afstudeerproject. Naast dit programmeergedeelte ben ik nog met andere IT zaken bezig geweest, via de bril van een manager bekeken ipv een programmeur, zeg maar. Daarom ben ik bezig een een relatief kleiner programma, omdat ik maar de helft van de tijd beschikbaar heb voor het echte programmeren.

Ik ben eigenlijk wat pionierwerk voor het bedrijf aan het verrichten op/in het .NET framework. Uit de webapplicatie, die ze nu hebben in classic ASP, haal ik een klein stukje en werk dat uit in .NET. Om dat ene kleine stukje te laten werken, heb je wel een soort van basis nodig waaronder database toegang e.d.

  • D4Skunk
  • Registratie: Juni 2003
  • Laatst online: 20-10-2025

D4Skunk

Kind of Blue

[quote]whoami schreef op woensdag 27 april 2005 @ 16:29:
[...]


Jouw applicatie spreekt dan uiteindelijk tegen een repository:

code:
1
2
3
4
private btnFindPersons_Click(object sender, EventArgs e)
{
   PersonCollection persons =   myPersonRepository.FindAllPersons();
}


ff een vraagje @whoami:
Als ik het correct begrepen heb, gaat dus al je dataaccess via je repository object.
Is het niet net de bedoeling van een DAL om de dataacces te verbergen, zodat de programmeur geen rekening hoeft te houden met de database ? vb :
C#:
1
2
3
4
5
   PersonCollection persons =   new PersonCollection(PersonFilters.AllPersons);    
   persons[0].Boss = persons[5];
   persons[6].Delete();
   persons.Add(new Person("Naam","Voornaam"));
   persons.Persist();

  • Sybr_E-N
  • Registratie: December 2001
  • Laatst online: 20:01
Ik heb even het een en ander uitgewerkt, is nog niet helemaal naar mijn zin. Op dit moment maakt abstrace class een singlton factory aan, deze factory maakt op zijn beurt een bijbehorende transactiehandler aan. Via de singleton instantie kan ik nu bij de repositry om de gewenste gegevens op te halen. Zoals een lijst van alle vragen. Deze gegevens worden verpakt in een algmene class, als een Question of een QuestionCollection klasse.

De abstracte factory klasse:
C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public abstract class DBFactory {
  protected static DBFactory _factory;
  private static string dbtype;
  private static string connectionString;

  public static DBFactory getInstance() {
    dbtype = ConfigurationSettings.AppSettings["DBTYPE"];
    connectionString = ConfigurationSettings.AppSettings["DBConnection"];

    if (_factory == null) {
      switch (dbtype) {
        case "MSSQL" : _factory = new MSSQLFactory(connectionString);
                                  break;
        case "Oracle" : _factory = new OracleFactory(connectionString);
                                   break;
      }
    }
    return _factory;
  }

  public abstract IScriptRepository getScriptRepository();
}


Een van de concrete Database klassen, (Sql Server gebruik ik om te testen):
C#:
1
2
3
4
5
6
7
8
9
10
11
12
public class MSSQLFactory : DBFactory {
  private SqlServerTransActionHandler trans;

  public MSSQLFactory(string connectionString) {
    trans = new SqlServerTransActionHandler(connectionString);
  }

  public override IScriptRepository getScriptRepository() {
    return new ScriptRepository(trans);
  }

}


De transaction handler voert de queries uit. (hier nog even zonder transacties.)
C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class SqlServerTransActionHandler : ITransActionHandler {
...     
  public SqlServerTransActionHandler(string connection) {
    this.connection = new SqlConnection(connection);
    this.cmd = this.connection.CreateCommand();
    this.cmd.Connection = this.connection;
    this.da = new SqlDataAdapter();
  }

  public DataSet ExecuteQuery(string query) {
    DataSet ds = new DataSet();
    try {
      this.cmd.Connection.Open();
      this.cmd.CommandText = query;
      this.da.SelectCommand = this.cmd;
      da.Fill(ds);
    } 
    ...
    return ds;
  }
}


De Repository.
In mijn geval levert getQuestionWithAllAnswers een object op met een vraag + bijbehorende antwoorden.
Heeft uit de database twee tabellen nodig, 1 voor de vragen en 1 voor de antwoorden.
Om een vraag te krijgen is er de QuestionGateway, en AnswerGateway voor de antwoorden. De antwoorden worden nog eens
verpakt in een collection classe (=ArrayList). Deze Repository geeft een van de business ojecten terug.
C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class ScriptRepository : IScriptRepository {
  private SqlServerTransActionHandler th;

  ...

  public Question getQuestion(int questionId) {
    QuestionGateway vg = new QuestionGateway(th);
    return new Question(questionId, vg.getQuestion(questionId));
  }

  public QuestionWithAllAnswers getQuestionWithAllAnswers(int questionId) {
    QuestionGateway qg = new QuestionGateway(th);
    Question q = new Question(questionId, qg.getQuestion(questionId));

    AnswerGateway ag = new AnswerGateway(th);
    DataSet ansDs = ag.getAllAnswers(questionId);
    AnswerCollection ac = new AnswerCollection();

    foreach (DataRow row in ansDs.Tables[0].Rows) {
      ac.Add(new Answer((int) row[0], 0, (string) row[1]));
    }

    return new QuestionWithAllAnswers(q, ac);
  }
}



De Gateway, bevat de queries en laten deze weer uitvoeren op hun beurt de transactionhandler.
C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class QuestionGateway : IQuestionGateway {
  private SqlServerTransActionHandler th;

  public QuestionGateway(SqlServerTransActionHandler th) {
    this.th = th;
  }

  public string getQuestion(int questionId) {
    string query = "SELECT id, vraag FROM Script_Vragen WHERE id = " + questionId;
    DataSet ds = th.ExecuteQuery(query);
            
    return Convert.ToString(ds.Tables[0].Rows[0]["vraag"]);
  }
        
   ...

}


Voor een Oracle database heb ik eenzelfde Factory, handler, repository en gateway's. (Deze voeren op dit moment niets uit)


Verder heb ik nog enkele bussines objecten, enkele staan hieronder:
C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
//Klasse welke een vraag bevat, zelfde voor antwoord
public class Question {
  private int id;
  private string question;

  public Question(int id, string question) {
    this.id = id;
    this.question = question;
  }

  public int getId() {
    return this.id;
  }

  public string getQuestion() {
    return this.question;
  }
}
//Klasse welke een vraag met alle bijbehorden antwoorden bevat
//AnswerCollection, is een ArrayList
public class QuestionWithAllAnswers {
  private Question question;
  private AnswerCollection answers;

  public QuestionWithAllAnswers(Question q, AnswerCollection a) {
    this.question = q;
    this.answers = a;
  }

  public Question getQuestion() {
    return this.question;
  }

  public AnswerCollection getAnswerCollection() {
    return this.answers;
  }
}



Zo gebruik ik het nu, het print uiteindelijk een vraag met alle bijbehorende antwoorden in een listbox:
C#:
1
2
3
4
5
6
QuestionWithAllAnswers vea = DBFactory.getInstance().getScriptRepository().getQuestionWithAllAnswers(2);
listBox1.Items.Add(vea.getQuestion().getQuestion());
System.Collections.IEnumerator it = vea.getAnswerCollection().GetEnumerator();
while(it.MoveNext()){
  listBox1.Items.Add(((Answer)it.Current).getAnswer());
}


Ik ben nog niet helemaal tevreden. Zoals te zien is moet ik een hele zin type om uiteindelijk te zijn waar ik wil zijn. (Nu typ ik het in dit samples in 1 keer, maar je kunt ook meerdere variabelen aanmaken.) Ook geef ik de transactionhandler door via een constructor en niet via de methoden zelf, zoals whoami demostreerde. Ik vond het zelf ietwat netter om het via de constructor door te geven. Zit ik nu aardig op het goede spoor of ben ik kompleet verwaald?

  • D4Skunk
  • Registratie: Juni 2003
  • Laatst online: 20-10-2025

D4Skunk

Kind of Blue

bedankt voor de code...
ziet er goed uit...
Wat je evt ook nog zou kunnen doen is de case in de dbfactory droppen, en initialiseren dmv creatinstance imho...

  • EfBe
  • Registratie: Januari 2000
  • Niet online
Sybren: XML comments en naamgeving. Daar moet je echt op letten. 2 letterige namen deden we vroeger op de MSX omdat die maar 2 characters per naam onthield, maar tegenwoordig niet meer. XML comments zijn essentieel in C#. Wil je die niet met de hand tikken, download dan de Ghostdoc add-in, waarmee je met 1 toetscombinatie vaak al de XML comments krijgt :)

Verder zijn methods altijd PasCal Cased. Kleine zeurdingetjes, maar je krijgt ze waarschijnlijk op je bord, kun je er maar beter vroeg bij zijn ;)

Creator of: LLBLGen Pro | Camera mods for games
Photography portfolio: https://fransbouma.com


  • Sybr_E-N
  • Registratie: December 2001
  • Laatst online: 20:01
D4Skunk schreef op vrijdag 29 april 2005 @ 16:48:
bedankt voor de code...
ziet er goed uit...
Wat je evt ook nog zou kunnen doen is de case in de dbfactory droppen, en initialiseren dmv creatinstance imho...
Thnx.

Die switch zou er inderdaad uit kunnen, maar daar kijk ik maandag wel even naar. (niet meer op dit tijdstip iig :))
EfBe schreef op vrijdag 29 april 2005 @ 16:58:
Sybren: XML comments en naamgeving. Daar moet je echt op letten. 2 letterige namen deden we vroeger op de MSX omdat die maar 2 characters per naam onthield, maar tegenwoordig niet meer. XML comments zijn essentieel in C#. Wil je die niet met de hand tikken, download dan de Ghostdoc add-in, waarmee je met 1 toetscombinatie vaak al de XML comments krijgt :)

Verder zijn methods altijd PasCal Cased. Kleine zeurdingetjes, maar je krijgt ze waarschijnlijk op je bord, kun je er maar beter vroeg bij zijn ;)
Ow, ik heb nog helemaal geen comments erin. Alleen even voor de samples hier gezet voor wat duidelijkheid. Ook heb ik het een en ander aangepast om wat ruimte hier op got te besparen, zoals { op 1 regel ipv direct onder een methode (VS.net doet dat allemaal zelf, eigenwijs programma :))

XML is dus de way to go in C#. (Visual Studio doet dat standaard ook voor wat code wat ze zelf genereerd dacht ik zo). Ik heb er nog maar weinig info over kunnen vinden in C# programmeerboeken die ik hier heb. Even iets wat ik me afvraag nu, is er ook zoiets als JavaDoc maar dan voor .NET (Ik ben hetzelf nog niet voorbij gekomen, maar ben dan ook nog niet echt naar opzoek geweest)? Dat je naast het vermelden van pre en post condities ook een document krijgt, waarin alles staat.

En naamgeving is niet echt mijn sterkste punt, wat je nu ziet heb ik geërft van Delphi->PHP->Java->C++ style. Een grote brei dus, alhoewel ik wel een eigen style aanhouw. Komt omdat we in het eerste jaar ons toendertijd aan de Code Conventies in Delphi van de leraar moesten houden :). Dat zit er nog steeds in.

De methoden van .NET zelf zijn ook in PasCal case en niet zoals ik het doe pasCal case. Dan is het wel handig om dat ook over te nemen. Hetzelfde geldt voor die 2 letter variablelen, zoals it ipv myIterator bijvoorbeeld.

Voorde rest ben ik wel beniewd op jullie mijn opzet vinden? Klopt het een beetje, of heb ik alles verkeerd begrepen.

  • whoami
  • Registratie: December 2000
  • Laatst online: 00:19
[quote]D4Skunk schreef op vrijdag 29 april 2005 @ 12:26:
whoami schreef op woensdag 27 april 2005 @ 16:29:
[...]


Jouw applicatie spreekt dan uiteindelijk tegen een repository:

code:
1
2
3
4
private btnFindPersons_Click(object sender, EventArgs e)
{
   PersonCollection persons =   myPersonRepository.FindAllPersons();
}


ff een vraagje @whoami:
Als ik het correct begrepen heb, gaat dus al je dataaccess via je repository object.
Is het niet net de bedoeling van een DAL om de dataacces te verbergen, zodat de programmeur geen rekening hoeft te houden met de database ? vb :
C#:
1
2
3
4
5
   PersonCollection persons =   new PersonCollection(PersonFilters.AllPersons);    
   persons[0].Boss = persons[5];
   persons[6].Delete();
   persons.Add(new Person("Naam","Voornaam"));
   persons.Persist();
Die client programmeur hoeft dat ook niet te doen, want hij praat tegen de repository.
Wat die repository precies doet, zal'm worst wezen. Praat die repository tegen een DAL die een DB benadert, of eender wat, dat maakt niet uit.

https://fgheysels.github.io/


  • whoami
  • Registratie: December 2000
  • Laatst online: 00:19
Javadoc vraag: er is zoiets als NDoc.

Misschien moet je rechtstreeks tegen je repository praten, en die repository laten bepalen welke DB-type of welke persistentie-bron hij moet aanspreken...

[ Voor 69% gewijzigd door whoami op 30-04-2005 09:59 ]

https://fgheysels.github.io/

Pagina: 1