Implementacija DA layer-a (C#)

gost 390446

Iskusan
Poruka
5.726
1. DEO

Posto se cesto postavljaju razna pitanja oko toga kako upisati nesto u bazu podataka, kako ovo, kako ono, rekoh da podelim ovde jedan generalni primer dobre prakse kako implementirati DA layer.
Ovde cu dati primer za dve vreste baze podataka (MS SQL, i Firebird), a videcete kako dalje vrlo lako sami mozete da implementirate za bilo koju drugu bazu podataka (jednostavnim nasledjivanjem).
Pa, da pocnemo:

1. Kreirajte interfejs, gde cete opisati neophodne funkcije.

Kod:
public interface IUpdatesDA
{
        IUpdatesScope GetScope();
        bool Login(IUpdatesScope updatesScope, string userName, string password, out AccountData accountData, out Guid sessionGuid);
        bool SelectSomething(IUpdatesScope, Guid sessionGuid, int someID, out MyDatabaseData myDatabaseData);
        bool InsertSomething(IUpdatesScope updatesScope, Guid sessionGuid, DataToInsert dataToInsert);
}

2. Kreirajte interfejs za vas database Scope, koji ce biti nista drugo do implementacija IDisposable interfejsa.

Kod:
public interface IUpdatesScope : IDisposable
{
}

3. Implementirajte interfejs IUpdatesScope, za dve navedene baze podataka:

- MS SQL:

Kod:
public class MSSqlUpdatesScope : IUpdatesScope
    {
        public MSSqlUpdatesScope(string connectionString)
        {
            Connection = new SqlConnection(connectionString);
            Connection.Open();
        }

        ~MSSqlUpdatesScope()
        {
            Dispose(false);
        }

        public SqlConnection Connection { get; internal set; }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (!disposing)
                return;

            if (Connection == null)
                return;

            Connection.Dispose();
            Connection = null;
        }
    }

- Firebird db:
Kod:
public class FirebirdUpdatesScope : IUpdatesScope
    {
        public FbConnection Connection { get; internal set; }

        public FirebirdUpdatesScope(string connectionString)
        {
            Connection = new FbConnection(connectionString);
            Connection.Open();
        }

        ~FirebirdUpdatesScope()
        {
            Dispose(false);
        }
    
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }


        // The bulk of the clean-up code is implemented in Dispose(bool)
        protected virtual void Dispose(bool disposing)
        {
            if (!disposing) 
                return;

            if (Connection == null) 
                return;

            Connection.Dispose();
            Connection = null;
        }
    }

Napomena: ne zaboravite da dodate using klauzule za sve sto ce vam trebati, i adekvatne reference.
 
Poslednja izmena:
2. DEO: Implementacija IUpdateDA interfejsa i konkretna manipulacija sa bazom podataka.

Sada je na redu da implementiramo nas interfejs, odnosno njegove funkcije u konkretnim klasama:

- MSSQL:

Kod:
public class MSSqlUpdatesDA : IUpdatesDA
{
    public string ConnectionString { get; set; }
    
    public MSSqlUpdatesDA(string connectionString)
    {
        ConnectionString = connectionString;
    }

    public IUpdatesScope GetScope()
    {
        return new MSSqlUpdatesScope(ConnectionString);
    }
    public bool Login(IUpdatesScope updatesScope, string userName, string password, out AccountData accountData, out Guid sessionGuid)
    {
            sessionGuid = Guid.Empty;
            accounDatat = null;

            var scope = updatesScope as MSSqlUpdatesScope;
            if (scope == null)
                return false;

            using (var command = scope.Connection.CreateCommand())
            {
                command.CommandText = "OVDE IDE TEKST KLASICNE SQL KOMANDE";

                command.Parameters.Add(new SqlParameter("UserName", userName));  // Ovde dodajemo parametre 
                command.Parameters.Add(new SqlParameter("Password", password));

                using (var reader = command.ExecuteReader())  // i ovde izvrsavamo nasu komandu
                {
                    if (!reader.Read())
                        return false;

                    sessionGuid = Guid.NewGuid();
                    accountData = new AccountData
                    {
                       // OVDE SETUJEMO ACCOUNT DATA, NA OSNOVU ONOGA STO SMO DOBILI KAO REZULTAT IZVRSENJA KOMANDE
                       Id = (int)reader.Get<long>("ID"),
                       UserName = reader.Get<string>("USERNAME"),
                       UserGroup = reader.Get<string>("USERGROUP")
                    };

                    return account.Id > 0 && false;
                }
            }
    }
        
   // DALJE IDE IMPLEMENTACIJA OSTALIH FUNKCIJA

}

Da ne ponavljam, gotovo je identicno za Firebird database.

Postoji jos puno dobrih primera, ovo je jedan koji ja cesto koristim u real sistemima (i to samo krnji deo cele price).

To je ukratko, sto se tice DA layer-a.
Sledeci stadijum je implementacija BL (Business Layer-a), koji ce konzumirati ovaj sloj.
Kako cete implementirati BL, zavisi najvise od toga da li je pravite web service, ili bazi pristupate direktno iz neke klijentske (desktop) aplikacije.

Pretpostavimo da je ovo drugo u pitanju, onda ce vam trebati samo jedna DataProvider klasa, koja ce odluciti koju instancu da kreira (u zavisnosti koju bazu koristite), i jedan DataManager, koji ce pripremiti podatke prikupljene od UI-a, odnosno pripremiti podatke za UI (ili za neki lokalni storage, ili sta god).
 
Poslednja izmena:
Meni ovo sa dispose vise lici na workaround deficijencije C# nego nesto sto generalno moze da upotrebi ;)
Ja sam negde 2000 radio serijalizaciju u C++ gde je reader/writer interfejs odvojen od samog kreiranja objekta, pa sam mogao recimo db_stream >> object_list ili db_stream << object_list ili file_stream ili rpc_stream itd...
Mada imas Entity framework za C# pa ti to nije ni potrebno ;p
 
Meni ovo sa dispose vise lici na workaround deficijencije C# nego nesto sto generalno moze da upotrebi ;)
Ja sam negde 2000 radio serijalizaciju u C++ gde je reader/writer interfejs odvojen od samog kreiranja objekta, pa sam mogao recimo db_stream >> object_list ili db_stream << object_list ili file_stream ili rpc_stream itd...
Mada imas Entity framework za C# pa ti to nije ni potrebno ;p

Nisam zavrsio implementaciju, ovo je skraceni primer. Pun primer podrazumeva kreiranje instanci objekata koji implementiraju interfejse, putem Dependency Injection-a, tako da je zato implementacija IDisposable interfejsa obavezna. Inace, u pravu si, nije.
A Entity Framework je super stvar za velike stvari, velike podatke, i pogotovo za one koji se ne menjaju cesto.
Za male stvari, ceste sitne update-ove, je totalni overkill. Merili smo performanse, i veruj mi, od 2 do 5 puta brze radi direktan pristup, pogotovo ako lepo pripremis sve parametre komandi :)
 
Da, to je vec drugo, ubacivati komentare (u vidu opisnih atributa) koje generatori dokumentacije koriste, je cool stvar. Pre par godina sam nesto slicno doxygen-u koristio da dokumentujem neke web servise, i prilicno je cool. Ali nema teoretske sanse da se setim sta sam koristio, stize me alzhajmer polako :D
 

Back
Top