Avatar billede bumle90 Nybegynder
09. august 2004 - 16:46 Der er 51 kommentarer og
2 løsninger

Overblik over ADO.net

Jeg er gået i gang med at lave en klasse der kan håndtere databaseforespørgsler.
Det skal være en klasse der skal bruges i mange forskellige applikationer, dvs. et dll-lib jeg inkluderer, som så utrolig let og fleksibelt kan håndtere al DB-adgang.
Jeg mangler dog at få et totalt overblik over de forskellige DB-muligheder og faciliteter der er i .net. Jeg er godt klar over at der er en MASSE faciliteter, så det ville jo være smart at ha en klasse der kunne tage sig af de mest gængse funktioner .
Jeg kunne desuden godt tænke mig at klassen kunne tage sig af ting som SQL-injection og ConnectionPooling.
Så selvfølgelig specialiserede forespørgsler hvor der kun kommer 1 record tilbage osv.
Jeg kan jo nok ikke undgå at jeg er nødt til at retunere fx. en datareader fra en SQL-SELECT og sådan...som jeg så er nødt til at behndle udenfor objektets scope. Men sådan er det jo bare.
Det jeg bare gerne vil stræbe efter er at al DB-behandling bliver håndteret inde i min DBHandler klasse.
Noget andet den også skulle kunne var at gøre brugen af OLE-DB og MS SQL tilgang ret let, således at man kun behøver sende en parameter fx. der afgør om der skal bruges SqlClient el. OleDb.
ALle disse ting kræver jo altså overblik, så mit spørgsmål er om nogle kender rigtig gode steder hvor jeg kan læse om alt det her.
Evt. hvis der findes noget kode i forevejen der kan alt det her...Det e rvel et ret generelt problem og ellers bare gode fif, tricks og code snippets....
Håber jeg har udtrykt problemet klart nok.
Mvh. bumle90
Avatar billede erve Nybegynder
09. august 2004 - 16:59 #1
Der findes en såkald ADO helper Application block, der måske kan hjælpe dig: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnbda/html/daab-rm.asp

Mht Connection pooling understøtter SQL provideren allerede detm så det skulle du måske tage i betragtning, medmindre det er for sjov du vil lave den.
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguide/html/cpconconnectionpoolingforsqlservernetdataprovider.asp
Avatar billede bumle90 Nybegynder
09. august 2004 - 17:10 #2
okay...men hvad med OLEDB provideren..laver den også conn pooling?
Avatar billede bumle90 Nybegynder
09. august 2004 - 17:19 #3
Og SQL-injection...er det også forebygget i de allerede indbyggede ADO.net klasser?
Avatar billede arne_v Ekspert
09. august 2004 - 17:29 #4
Alle de providere der kommer med .NET framework understøtter connection pooling.

SQL injection undgår du ved at bruge parameter's.

Jeg lavede engang følgend elille stykke kode til at encapsulate forskellene
mellem forskellige databaser:

using System;
using System.Data;
using System.Data.SqlClient;
using System.Data.OleDb;
using System.Data.Odbc;

public class MultiDb
{
    public static IDbConnection GetConnection(string constr)
    {
        if(constr.ToUpper().IndexOf("DRIVER=") >= 0)
        {
            return new OdbcConnection(constr);         
        }
        else if(constr.ToUpper().IndexOf("PROVIDER=") >= 0)
        {
            return new OleDbConnection(constr);       
        }
        else if(constr.ToUpper().IndexOf("TRUSTED_CONNECTION=") >= 0 ||
                constr.ToUpper().IndexOf("INTEGRATED SECURITY=") >= 0)
        {
            return new SqlConnection(constr);         
        }
        else
        {
            return null;
        }
    }
}
class TestClass
{
    private static void test(string constr)
    {
        IDbConnection con = MultiDb.GetConnection(constr);
        con.Open();
        IDbCommand cmd = con.CreateCommand();
        cmd.CommandText = "SELECT * FROM T1";
        IDataReader rdr = cmd.ExecuteReader();
        while(rdr.Read()) {
            int f1 = (int)rdr[0];
            string f2 = (string)rdr[1];
            Console.WriteLine(f1 + " " + f2);
        }
        con.Close();
    }
    public static void Main(string[] args)
    {
        test("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=D:\\Database\\MSAccess\\Test.mdb");
        test("server=ARNEPC2;Integrated Security=SSPI;database=Test");
    }
}
Avatar billede bumle90 Nybegynder
09. august 2004 - 17:40 #5
SQL injection undgår du ved at bruge parameter's.
Hvad mener du helt præcist ved det arne?
Avatar billede arne_v Ekspert
09. august 2004 - 17:57 #6
Kode fragment:

MySqlCommand ins = new MySqlCommand("INSERT INTO pics VALUES (@id, @pic)", con);
ins.Parameters.Add("@id", MySqlDbType.Int);
ins.Parameters.Add("@pic", MySqlDbType.MediumBlob);
...
ins.Parameters["@id"].Value = i;
ins.Parameters["@pic"].Value = data;
ins.ExecuteNonQuery();

Det her eksempel er så med ByteFX MySQL provider, men det er det samme med
andre providere.
Avatar billede bumle90 Nybegynder
11. august 2004 - 13:07 #7
ahhh okay...det er sq da supersmart...Jeg går ud fra den laver en form for replace af plinger osv. Altså en art encoding. Hvad så når det skal ud af DB igen...skal det så decodes igen på en måde når der er brugt parameters?

Et andet spg. jeg håbede du ville svare på er om der er en smart måde at genbruge sin connection på i en webapp istedet for at oprette en ny hver gang?
Altså fx. i din MultiDb-klasse ville det kræve at man hver gang en ny side var loadet skulle initialisere objektet igen...lave ny forbindelse osv. som man så kunne bruge. Er der ikke en smartere måde...således at jeg kan bruge mit connection objekt gennem et helt forløb?
Skal jeg gemme det i en sessionsvariabel eller hvad er smart?
Jeg tænker sådan rent performancemæssigt hvad der er smartest?
Avatar billede arne_v Ekspert
11. august 2004 - 13:11 #8
Den laver et eller andet. Præcist hvad afhænger formentligt af netværks protokollen
til database serveren.

Nej - det er helt transparent. Data gemme si databasen helt normalt. Og de trækkes
ud igen helt normalt.

Du bør genbruge din connection under processing af en enkelt request. Du bør
ikke gemme connection mellem requests (altså i sessionen).

For det første får du problemer med antal connections, hvis du har mange brugere.

For det andet har .NET som sagt indbygget connection pool, så omkostningen
ved at hente en ny connection igen er ikke så stor.
Avatar billede bumle90 Nybegynder
11. august 2004 - 13:16 #9
Okay, så dvs. det klogeste er at i page_load på alle mine sider(der bruger DB) at lave et nyt DB-objekt, og gemme det i en global public klassevariabel?
Avatar billede bumle90 Nybegynder
11. august 2004 - 13:17 #10
Grunden til jeg vil lave det public er fordi jeg instantierer andre klasser i min page-klasse som også skal bruge databasen....lidt dumt hvis jeg har connection objekter i alle de klasser jo
Avatar billede arne_v Ekspert
11. august 2004 - 13:19 #11
Jeg formoder at du mener instans variabel (altså ikke static).
Avatar billede arne_v Ekspert
11. august 2004 - 13:21 #12
Jeg kan ikke lide det public felt

Send connection med over til de andre klasser.

Eller lav et helt andet design, hvor database aktiviterne er samlet mere.
Avatar billede bumle90 Nybegynder
11. august 2004 - 13:29 #13
hmm johh problemet er at jeg har bygget mit design op med tanke på andre ansvarsområder. Fx. en række objekter der opbygger dynamisk pdf-filer. De skal bruge data fra databasen. Så dem er jeg næsten nødt til at ha for sig selv.
Så er der nogle drop-downs der skal bruge databasen, og endelig skal jeg også lave et datagrid der gør brug af DB. egentlig får jeg kun 2 steder jeg skal bruge db så. Måske er det bedre at sende DB-objektet med til konstruktøren for pdf-objekterne. Og så ellers oprette objektet i de forskellige page_load. Så ellers bare gemme det i en privat instans variabel. Hvad siger du til det?
Avatar billede arne_v Ekspert
11. august 2004 - 13:36 #14
Det var absolut en måde at gøre det på.

En anden (som nok primært er interessant ved lav volumen) er at lave en 1-2-3-4
singleton klasser med hver sin connection og metoder som bruger lock på den til
at sikre at kun en enkelt operation foretages af gangen.
Avatar billede bumle90 Nybegynder
11. august 2004 - 13:38 #15
Min kode for min DBHandler er so far følgende:
(Der skal bygges lidt videre på den efterhånden som der kommer flere behov, men hvad synes du om den som udgangspunkt?(Der er et problem med at lukker connection efter hver tilgang...)


using System;
using System.Data;
//using System.Data.Common;
//using System.Data.Odbc;
using System.Data.OleDb;
using System.Data.SqlClient;
//using System.Data.SqlTypes;


namespace certregister
{
    /// <summary>
    /// Summary description for DBHandler.
    /// </summary>
    public class DBHandler
    {
        private string  connectionstring;
        private OleDbConnection OleConn;
        public static int SQLSERVER=0;
        public static int OLEDBSERVER=1;
       
        public static String SQLEncode(String val)
        {   
            return(val.Replace("'","''"));
        }

        public DBHandler(string dbpath,int DBtype) //kaster alle exceptions videre
        {
            //connectionstring = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source="+dbpath;
            if(DBtype==OLEDBSERVER)
            {
                connectionstring = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source="+dbpath;
                OleConn = new OleDbConnection(connectionstring);//(connectionstring);
                OleConn.Open();
            }
        }

        public OleDbDataReader fetch(string sql)
        {
            OleDbCommand comm = new OleDbCommand(sql, OleConn);
            OleDbDataReader r = 
                comm.ExecuteReader(CommandBehavior.CloseConnection);
            return(r);

        }

        public object fetchSingleValue(string sql)
        {
            OleDbDataReader r=fetch(sql);
            object temp=null;
            while(r.Read())
                temp=r.GetValue(0);
            return(temp);
        }

        public bool insert()
        {
            return true;
        }
    }
}


Istedet for den sql_indkod burde jeg nok lave sådan en parameterize metode, som lave parameters på sql-strengen istedet. Men hvad siger du til den?
Avatar billede arne_v Ekspert
11. august 2004 - 13:42 #16
Jeg siger at:

* fetch bør returnere en IDataReader så den kan bruges med både SQLServer og OLE DB

* fetchSingleValue bør bruge ExecuteScalar som er der til det samme
Avatar billede bumle90 Nybegynder
11. august 2004 - 13:46 #17
okay, men jeg kan ikke forstå hvorfor den kommer med fejl når jeg laver flere på hinanden følgende fetch.
Så siger den connection obejctet er blevet lukket.
Hvis jeg skifter følgende linie
    comm.ExecuteReader(CommandBehavior.CloseConnection);
ud med fx.
    comm.ExecuteReader(CommandBehavior.SingleResult);
Brokker den sig istedet over at der eksisterer datareader i forvejen. Hvad gør jeg forkert?
"There is already an open DataReader associated with this Connection which must be closed first"
Avatar billede arne_v Ekspert
11. august 2004 - 13:48 #18
Brug:

comm.ExecuteReader()

og inden næste kald af fetch kalder du Close metoden på den returnerede reader !
Avatar billede bumle90 Nybegynder
11. august 2004 - 13:52 #19
okay, er det smart at retunere en System.Data.IDataReader? Altså jeg mener bare hvis man ser sådan ideelt på det bør alt databasebehandling jo foregå inde i DB-handleren. Dvs. der bør ikke engang importes nogle DB-namespaces andre steder end i DB-handleren.
For sådan som det er nu er jeg jo nødt til at bruge fx. System.Data.IDataReader uden for DBhandleren....inde i selve applikationen. Men jeg kan også godt se at det blir lidt noget rod hvis man skal til at kopiere hele settet over i en eller anden datastruktur først for at retunere den istedet. Det er faktisk lidt et problem jeg har tænkt over et par gange. Hvad er den "rigtige" så at sige, løsning på det problem?
Avatar billede bumle90 Nybegynder
11. august 2004 - 13:57 #20
Hmmm luk datareaderen inden næste kald....Der må da være en pænere måde at gøre det på. Ellers er jeg nødt til at gemme min datareader i en midlertidig variabel for kunne lukke den. Altså fx. her(Dette illustrerer også meget godt hvad jeg mener med det jeg skrev før)

selWelderMethod.DataSource=dbHandler.fetch("SELECT SVmetode FROM T_SvMetode");
selWelderMethod.DataTextField="SVmetode";
selWelderMethod.DataBind();

Her er det eneste DB-specifikke SQL-strengen der sendes med. Hvis jeg skal lukke readeren er jeg nødt til at lave en datareader-reference            som jeg så kan bruge til at lukke den med. Det virker bare ret omsonst på enelleranden måde synes jeg....Er du med på hvad jeg mener?
Avatar billede arne_v Ekspert
11. august 2004 - 14:06 #21
Det er bedre at returnere en IDataReader end en connection specifik klasse.

Enten returnerer du en reader og processer data udenfor database klassen eller
du returnerer en collection (ArrayList, Hashtable eller lignende) med data.

Hvad der er bedst afhænger lidt af applikationen.
Avatar billede arne_v Ekspert
11. august 2004 - 14:09 #22
Ja men jeg kender ikke nogen super optimal løsning.
Avatar billede bumle90 Nybegynder
11. august 2004 - 14:10 #23
hmm ja oki...jeg kunne jo selvfølgelig bare lave 2 fetch. En der retunerer IDataReader og en der retunrer en Hashtable...Så kan man jo selv vælge hvilken man vil bruge...
Avatar billede bumle90 Nybegynder
11. august 2004 - 14:27 #24
Hmm det her var hvad jeg lige kunne præstere....
Findes der mon en smartere måde?

        public System.Collections.ArrayList fetchCollection(string sql)
        {
            comm = new OleDbCommand(sql, OleConn);
            System.Data.IDataReader r = comm.ExecuteReader();
            System.Collections.ArrayList tempList=new System.Collections.ArrayList();
            while(r.Read())
                tempList.Add(r.NextResult());
            return(tempList);
        }
Avatar billede arne_v Ekspert
11. august 2004 - 14:29 #25
Øh. Jeg tror altså at du skal gemme data i den ArrayList !
Avatar billede bumle90 Nybegynder
11. august 2004 - 14:39 #26
Gør jeg skam også :)
tempList.Add(r.getValue(0))
hehe
Avatar billede bumle90 Nybegynder
11. august 2004 - 14:52 #27
Må jeg ha lov at spørge dig om en sidste point inden du får point arne?
Avatar billede bumle90 Nybegynder
11. august 2004 - 14:55 #28
2 ting :D
Har lige et hurtigt tillægs....Parameters, virker de også på en whereclause?
Altså så man fx. ikke kan skrive ' or 1=1' i et søgefelt
Avatar billede bumle90 Nybegynder
11. august 2004 - 14:58 #29
et sidste spg. mener  jeg selvfølgelig :)
og ikke en sidste point hehe
Avatar billede arne_v Ekspert
11. august 2004 - 15:21 #30
Ja du kan godt bruge parametre i en WHERE betingelse.
Avatar billede bumle90 Nybegynder
11. august 2004 - 15:49 #31
okay, hvordan vil det være smart at integrere en metode der ordner alt med paramterers i DBHandleren synes du?
Avatar billede arne_v Ekspert
11. august 2004 - 15:55 #32
Der er så mange måder at gøre det på.

Jeg tænker umiddelbart i retning af:
  - et antal commands instantieres i constructor
  - hver command har en metode som kaldes med det antal parametre der skal sættes
Avatar billede bumle90 Nybegynder
12. august 2004 - 16:31 #33
Hmmm hvad mener du med et antal commands?
At man definerer alle sine sql i DB-handleren?
Hvad så hvis den skal bruges i flere forskellige applikationer?
Man har jo fx. en sql-streng "SELECT blabla WHERE bla='her er en fare' AND bla2 LIKE 'her er endnu en fare'"
Man ved jo ikke på forhånd hvilke og hvor mange parametre i sql-strengen der skal encodes...
Og der er heller ikke smart hvis man skal til at lave command-strengen udenfor db-objektet...Det er ret svært egentlig
Avatar billede bumle90 Nybegynder
12. august 2004 - 16:41 #34
Kunne man ikke lave et interface der specificerer en klasse man blir nødt til at implementere for den enkelte applikation...Denne klasse indeholder så samtlige sql-sætninger...og hmm på en eller anden måde en mulighed for at fylde data i sql-sætningerne....
Avatar billede arne_v Ekspert
12. august 2004 - 16:46 #35
I mit eksempel er DB-handleren applikations specifik.

Jeg tror ikke så meget på den applikations uafhængige DB-handler. Hvis den var
nem at lave så havde Microsoft lavet den !
Avatar billede arne_v Ekspert
12. august 2004 - 16:47 #36
Jeg kan ikke se pointen i interfacet.

Men jeg kunne måske godt se pointen i en applikations uafhængig abstrakt basis
klasse og applikations specifikke konkrete klasser som arver fra den.
Avatar billede bumle90 Nybegynder
12. august 2004 - 16:58 #37
Pointen i interfacet var at så kunne DBhandleren i sin konstruktør modtage et objekt af den type (interfacet) Dette objekt indeholdt så på en elleranden måde De SQL-sætninger der skulle bruges i den pågældende applikation.
Hvis der skal laves en forspørgsel kaldes så først en funktion på det objekt der indeholder SQL, som færdiggør SQLen...altså dvs. putter de data ind der er blevet hentet fra grænsefladen....Herefter kaldes DBHandler funktioenen...som så udfører SQL-strengen fra vores objekt...Gir det lidt mening?
Avatar billede arne_v Ekspert
12. august 2004 - 17:01 #38
Ja.

Men jeg tror bare ikke at det bliver nogen speciel køn løsning.

Simpelt er godt !
Avatar billede bumle90 Nybegynder
12. august 2004 - 17:02 #39
hmmm kunne du godt ha ret i...Men hvad er så simpelt?
Avatar billede bumle90 Nybegynder
12. august 2004 - 17:17 #40
Hmmm Arne_v, prøv at se hvad jeg fandt på nettet...Det kunne måske godt bruges hvis man lige modificerede det en lille smule
http://www.net-language.com/CodeExample.aspx?i=613
Avatar billede bumle90 Nybegynder
12. august 2004 - 17:18 #41
Hvad siger du til den?
Avatar billede arne_v Ekspert
12. august 2004 - 17:35 #42
Tja.

Den er ihvertfald simpel.

Den er iøvrigt ikke så langt fra det MS link som Erve linkede til.

Absolut en mulighed.

Største anke er at der ikke er det store check for om metode og SQL faktisk
matcher !
Avatar billede bumle90 Nybegynder
12. august 2004 - 17:40 #43
Hvad mener du med om metode og SQL matcher?
Avatar billede bumle90 Nybegynder
12. august 2004 - 17:43 #44
Problemet med Erve's link var at det vidst kun er til SQL-server
Avatar billede arne_v Ekspert
12. august 2004 - 17:44 #45
Du kalder GetXxxx med en SQL streng, men der er ikke noget check på
compile time og din SQL faktisk giver en Xxxx.
Avatar billede bumle90 Nybegynder
12. august 2004 - 17:47 #46
Nej det er korrekt....Der er heller ikke noget check på om den parameterstreng man sender med faktisk er korrekt....
Ville være rart hvis man kunne fange begge dele compileTime...Men det er jo bare stort set umuligt...
Avatar billede bumle90 Nybegynder
12. august 2004 - 17:48 #47
Men okay...så kan man så sige at det jo ikke er anderledes end at syntaksen i en SQL-streng heller ikke kan checkes compile-time...Men gir en exception når den blir fyret af...
Avatar billede bumle90 Nybegynder
12. august 2004 - 17:56 #48
På den anden side....Begrænser man sig egentlig ikke fra en masse DB-funktionalitet hvis man kapsler det hele ind i sådan et objekt....Medmindre man vil til at wrappe alle DB-funktioner der er i .net...og det er jo en større affærre....
arrgh, det er jo til at blive sindsyg af det her :D
Avatar billede arne_v Ekspert
12. august 2004 - 18:20 #49
Hvis der var en nem og god løsning, så ville alle jo nok bruge den.

Virkeligheden er jo at der er uendeligt mange måder at lave database
interface på.
Avatar billede bumle90 Nybegynder
12. august 2004 - 19:04 #50
jahhh sådan er det jo...Hvordan plejer du at gøre?
Avatar billede arne_v Ekspert
12. august 2004 - 22:49 #51
Jeg plejer normalt enten bare at bruge standard database API uden indpakning
eller at lave en meget applikations specifik indpakning.
Avatar billede arne_v Ekspert
22. august 2004 - 11:37 #52
Tid at få afsluttet dette spørgsmål ?

(også selvom vi ikke har revolutioneret db adgangen)
Avatar billede bumle90 Nybegynder
24. august 2004 - 18:10 #53
Undskyld den bratte afslutning, men jeg har lidt for mange løse ender...Blir nødt til at lukke for nogle af alle mine spg.
Tak for hjællpen. Hvis der er lagt svar får disse point, eller er jeg nødt til at tage dem selv for at lukke alle de her spg.
Mvh.
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
Kurser inden for grundlæggende programmering

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