Avatar billede bildsoe Nybegynder
15. juli 2011 - 11:55 Der er 15 kommentarer og
1 løsning

Oprettelse af DAL - tilgang til database på den rigtige måde - Hvordan?

Hej

Jeg er ved at lave en applikation der skal håndtere alle sager på et ingeniørkontor og har i forbindelse med det nogle spørgsmål til implementering af database og tilgang til denne.

I et forsøg på at gøre tingene rigtigt første gang er jeg begyndt at læse om design patterns og hvordan de kan bruges til at implementere databaser. MEN det er jo en jungle, og jeg kan, hverken finde hoved eller hale i det hele.

Jeg har læst om database factory pattern:

database factory

Har læst om repository pattern:

repository pattern

Kan ikke finde ud af om, det er samme problem man løser med de forskellige patterns, og hvis ikke, hvilket der så er bedst i hvilke situationer.

Jeg forsøger at adskille de forskellige lag mest muligt, og vil gerne lave en så løst koblet tilknytning til databasen som muligt, da denne nok bliver skiftet i den nærmeste fremtid.

Er der nogen der har et god idé til en strategi for implementering af databaser på vedligeholdelsesvenlig vis?
Avatar billede softspot Forsker
15. juli 2011 - 13:54 #1
Jeg ved ikke om det er den rigtige løsning (findes der overhovedet en universel rigtig løsning?).

Umiddelbart ville jeg sørge for at lave de dataaccess-klasser der var behov for og så tilgå dem via et interface. Dette giver dig mulighed for at skifte selve logikke ud, uden at skulle ændre i klienterne (de klasser der benytter DA-klasserne).

Du kunne så lave en factory-klasse der kunne oprette DA-klasser og måske aktivere disse igennem noget IoC (f.eks. Windsor Castle eller hvad ved jeg, jeg har ikke praktisk erfaring med formelle IoC endnu) - alternativt konfigurere dig ud af det, dvs. angive hvilken factoryklasse der skal bruges til at oprette DA-klasser i f.eks. web.config.
Avatar billede bildsoe Nybegynder
15. juli 2011 - 14:21 #2
Hmm, jeg tror ikke der findes en universel rigtig løsning, men er sikker på at jeg ved at lave systemet efter patterns får lavet "pænere" kode, der er mere vedligeholdelsesvenligt. Jeg synes det lyder som en god idé at separere data og klienter på den måde du skriver. Har du mere konkret eksempel, hvordan du vil gøre dette? Jeg ved ikke om du har skimmet de to eksempler jeg har fundet. Tænker at i begge eksempler er der fokus på at separere data access og business logik, men ved bare ikke om den ene fremgangsmåde er bedre end den anden, eller som sagt om det er forskellige problemer der løses i de to eksempler, og om man evt. kunne kombinere dem.
Avatar billede softspot Forsker
15. juli 2011 - 14:44 #3
Det jeg snakker om er helt præcis det repsitory pattern du henviser til.

Jeg ville dog gerne indføre muligheden for at filtrere data allerede ved opslaget databasen i setedet for at skulle indlæse alle rækker for derefter at filtrere dem med LINQ i forretningslaget...

Dette kunne evt. gøres ved at sende en filtreringsdefinition med til HentSager (eller hvad den nu skal hedde i dit system), så du f.eks. kan definere et sagsnøgle, sagsid, kundenr eller hvad der nu er behov for.
Avatar billede arne_v Ekspert
15. juli 2011 - 14:46 #4
Den foerstae artikler ligger til 03-5 tal (paa den gamle 13 skala).

Al den funktionalitet han laver for at create ting og sager eksisterer allerede i .NET frameworket.

Eksempel:

using System;
using System.Data;
using System.Data.Common;

namespace E
{
    public class SqlServer4
    {
        public static void Main(string[] args)
        {
            DbProviderFactory dbf = DbProviderFactories.GetFactory("System.Data.SqlClient");
            IDbConnection con = dbf.CreateConnection();
            con.ConnectionString = "server=ARNEPC3\\SQLEXPRESS;Integrated Security=SSPI;database=Test";
            con.Open();
            IDbCommand cre = con.CreateCommand();
            cre.CommandText = "CREATE TABLE j(id INTEGER NOT NULL, txt VARCHAR(50), PRIMARY KEY(id))";
            cre.Connection = con;
            cre.ExecuteNonQuery();
            IDbCommand ins = con.CreateCommand();
            ins.CommandText = "INSERT INTO j VALUES(@id,@txt)";
            ins.Connection = con;
            IDbDataParameter p1 = ins.CreateParameter();
            p1.ParameterName = "@id";
            p1.DbType = DbType.Int32;
            ins.Parameters.Add(p1);
            IDbDataParameter p2 = ins.CreateParameter();
            p2.ParameterName = "@txt";
            p2.DbType = DbType.String;
            p2.Size = 50;
            ins.Parameters.Add(p2);
            for(int i = 0; i < 5; i++)
            {
                ((IDbDataParameter)ins.Parameters["@id"]).Value = i+1;
                ((IDbDataParameter)ins.Parameters["@txt"]).Value = "Test #" + (i+1);
                ins.ExecuteNonQuery();
            }
            IDbCommand sel = con.CreateCommand();
            sel.CommandText = "SELECT id,txt FROM j WHERE id > @lim";
            sel.Connection = con;
            IDbDataParameter p = ins.CreateParameter();
            p.ParameterName = "@lim";
            p.DbType = DbType.Int32;
            p.Value = 3;
            sel.Parameters.Add(p);
            IDataReader rdr = sel.ExecuteReader();
            while(rdr.Read())
            {
                int id = rdr.GetInt32(0);
                String txt = rdr.GetString(1);
                Console.WriteLine(id + " : " + txt);
            }
            rdr.Close();
            IDbCommand drp = con.CreateCommand();
            drp.CommandText = "DROP TABLE j";
            drp.Connection = con;
            drp.ExecuteNonQuery();
            con.Close();
        }
    }
}

(provider og connection string skal naturligvis flyttes til config i den virkelige verden)
Avatar billede arne_v Ekspert
15. juli 2011 - 14:51 #5
Den anden artikel ligger til 9-10 tal (paa den gamle 13 skala).

Det er maaden at goere det paa.

Du definerer:
- data klasser
- et interface for dit DAL
- en DAL factory
- BLL kalder DAL factory med en streng fra config)  [det er hcad mangler i artiklen]
- dine konkrete DAL implementationer implementerer interfacet

factory kan saa bruge simpel reflection, MEF, Spring.NET, Castle etc. alt efter hvad man foretraekker.

For mange aar siden til et spoergsmaal om layers lavede jeg foelgende eksempel:

using System;
using System.IO;
using System.Xml;
using System.Data.OleDb;

namespace DAL
{
    public class AccountData
    {
        private string name;
        private decimal amount;
        public AccountData() : this("", 0.00m)
        {
        }
        public AccountData(string name, decimal amount)
        {
            this.name = name;
            this.amount = amount;
        }
        public string Name
        {
            get
            {
                return name;
            }
            set
            {
                name = value;
            }
        }
        public decimal Amount
        {
            get
            {
                return amount;
            }
            set
            {
                amount = value;
            }
        }
    }
    public interface AccountManager
    {
        AccountData Load(string name);
        void Save(AccountData ac);
    }
    public class AccountManagerFactory
    {
        public static AccountManager Create(string amtyp)
        {
            if(amtyp == "DB")
            {
                return new DatabaseAccountManager();
            }
            if(amtyp == "XML")
            {
                return new XmlAccountManager();
            }
            else
            {
                return null;
            }
        }
    }
    internal class DatabaseAccountManager : AccountManager
    {
        public AccountData Load(string name)
        {
            AccountData res;
            OleDbConnection con = new OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=C:\\Databases\\MSAccess\\Test.mdb");
            con.Open();
            OleDbCommand sel = new OleDbCommand("SELECT amount FROM accounts WHERE name='" + name + "'", con);
            OleDbDataReader rdr = sel.ExecuteReader();
            if(rdr.Read())
            {
                decimal amount = (decimal)rdr[0];
                res = new AccountData(name, amount);
            }
            else
            {
                res = null;
            }
            con.Close();
            return res;
        }
        public void Save(AccountData ac)
        {
            OleDbConnection con = new OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=C:\\Databases\\MSAccess\\Test.mdb");
            con.Open();
            OleDbCommand upd = new OleDbCommand("UPDATE accounts SET amount = " + ac.Amount + " WHERE name='" + ac.Name + "'", con);
            upd.ExecuteNonQuery();
        }
    }
    internal class XmlAccountManager : AccountManager
    {
        private const string XML_FILE = @"C:\accounts.xml";
        private XmlDocument doc;
        public XmlAccountManager()
        {
            doc = new XmlDocument();
            doc.Load(XML_FILE);
        }
        public AccountData Load(string name)
        {
            XmlNode n = doc.SelectSingleNode("//accounts/account[@name='" + name +"']");
            if(n != null)
            {
                decimal amount = decimal.Parse(n.Attributes["amount"].Value);
                return new AccountData(name, amount);
            }
            else
            {
                return null;
            }
        }
        public void Save(AccountData ac)
        {
            XmlNode n = doc.SelectSingleNode("//accounts/account[@name='" + ac.Name +"']");
            n.Attributes["amount"].Value = ac.Amount.ToString();
            StreamWriter sw = new StreamWriter(XML_FILE);
            doc.Save(sw);
            sw.Close();
        }
    }
}


(factory er lidt primitiv, men ....)
Avatar billede softspot Forsker
15. juli 2011 - 14:57 #6
Arne >> når du skriver "det er hvad mangler i artiklen" mener du så den der henvises til vedr. repository pattern? I saå fald er det vel beskrevet til sidst i afsnittet "Using Reflection to Super-Charge your Repository Pattern"...
Avatar billede arne_v Ekspert
15. juli 2011 - 15:25 #7
Du har ret - den er der faktisk.

10-11 saa.

(og dumpet til mig for ikke at laese til enden)
Avatar billede softspot Forsker
15. juli 2011 - 15:47 #8
Hæh! Bort set fra det, synes jeg det er rart med en autoritet til at bekræfte, hvad man selv havde på fornemmelsen - tak! :D
Avatar billede sandrasmurf Nybegynder
16. juli 2011 - 01:04 #9
Jeg er blevet allergisk overfor DIY DAL. Det er ufatteligt kedeligt at skrive alle CRUD metoderne og hvis din bagvedliggende database kan ændre sig under forløbet, så kan du bruge time efter time på, at debugge dine SQL kald og alle de uforståelige fejlmeddelelser, der er indbygget i OleDB.

Hvis det skal være nemt, så tag et kig på typed datasets
http://weblogs.asp.net/scottgu/archive/2006/01/15/Building-a-DAL-using-Strongly-Typed-TableAdapters-and-DataTables-in-VS-2005-and-ASP.NET-2.0.aspx

Jeg købte adgang til learnvisualstudio.net og lærte den indbyggede drag-drop dataset/tableadapter tilgang gennem deres video'er.

Hvis du ikke kender typed dataset tilgangen, så tag et kig på området. Til et mindre projekt er det MEGET hurtigere.

Jeg har selv planer om at kigge nærmere på Microsoft Entity Framework, men har ikke haft lejlighed endnu. Det lyder ellers  lovende. En slags udvidelse af dataset.

http://msdn.microsoft.com/en-us/vcsharp/Video/dd565857
Avatar billede arne_v Ekspert
16. juli 2011 - 03:11 #10
At lave det hele selv med XxxCommand og XxxReader giver en masse triviel kode som man kan undgaa.

Typed data set er en lidt underlig konstruktion. Et rigtigt ORM framework kan anbefales.

Om man foretraekker MS EF, Nhibernate eller et af de mindre kendte (der er nok en 20-30 stykker at vaelge imellem) maa man saa goere op med sig selv.

Jeg vil dog stadigt anbefale et DAL - det bliver bare med meget faerre linier kode.

Med ORM skal man ogsaa taenke sig lidt om. Der skal ikke skrives ret meget kode. Og det der skal skrives er rimeligt nemt at skrive. Men at faa det til at performe godt kraever indsigt i de finere finesser i ens ORM framework.
Avatar billede bildsoe Nybegynder
16. juli 2011 - 13:00 #11
Tak for alle svarene. Er det rigtigt forstået at f.eks. NHibernate som jeg har kigget lidt på implementerer selve data access delen, men at jeg med fordel så stadig kan implementere denne del i et seperat layer så jeg får en klar adskillelse mellem DAL og BLL?
Avatar billede arne_v Ekspert
16. juli 2011 - 16:23 #12
Ja
Avatar billede muddi Praktikant
17. juli 2011 - 14:13 #13
Jeg vil helt afgjort anbefale dig at kigge på Entity Framework 4, som er inkluderet i .NET Framework.

Samtidig vil jeg klart anbefale Programming Entity Framework af Julia Lerman. Hun har også lagt nogen ganske gode videoer på Youtube.

Ved at bruge EF (.NET 4), så har du mulighed for at bruge Data First, Model First eller Code First. Det er også ganske let at lave ændringer i din konceptuelle model, som så opdateres og mappes til din data model automatisk.

/Morten
Avatar billede bildsoe Nybegynder
18. juli 2011 - 10:13 #14
Tak for alle kommentarerne. Jeg ved ikke helt, hvem der skal have point? Hvem vil gerne have? bare smid et svar!
Avatar billede arne_v Ekspert
18. juli 2011 - 15:55 #15
svar fra mig
Avatar billede muddi Praktikant
18. juli 2011 - 20:24 #16
Jeg synes ikke jeg har bidraget særligt, så jeg springer over ;)

/Morten
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