BLOGas.lt
Sukurk savo BLOGą Kitas atsitiktinis BLOGas

Mano skaidrės iš Agile Diena konferencijos

Parašė Sergejus | 2011-05-12 10:14

Talpinu savo skaidres iš pristaymo “Kaip Agile skatina gerųjų praktikų panaudojimą”, kurį dariau Agile Diena konferencijos metu.

Rodyk draugams

Programavimo standartai

Parašė Sergejus | 2010-05-10 19:14

Kuriant programinę įrangą komandoje, atsiranda poreikis susitarti dėl programavimo standartų. Įdomu tai, kad bent jau projekto pradžioje žmonių požiūris į bendrus susitarimus yra pakankamai skeptiškas. Tai galima suprasti – kiekvienas turi savo stilių, savo ego ir nenori persimokinti. Problema atsiranda tada, kai tenka palaikyti kolegų kodą. Tokiais atvejais dažnai ir pradedama suprasti kam reikalingi susitarimai bei vieningas programavimo stilius.

Vykdant projektus, vienas pirmų dalykų kuriuos aš įvedu komandoje – vieningas programavimo standartas. Dažniausiai tai apima C# ir ASP.NET kodą. Internete galima rasti nemažai taip vadinamų „kodavimo standartų“, bet jų kokybė ir teisingumas kartais kelia didelių abejonių. Šiandien nusprendžiau pateikti sąrašą mano manymų vertų dėmesio programavimo standartų C#, ASP.NET bei JavaScript kalboms:

Iš kitų pagalbinių įrankių labai rekomenduoju Microsoft StyleCop, kuris leidžia tikrinti kodo atitikimą standartams tiesiogiai Visual Studio aplinkoje ar vykdant automatinį surinkimą Team Foundation Server pagalba. Negalima sakyti, kad su visomis jo taisyklėmis aš asmeniškai sutinku, bet komandose su daugiau nei 5 žmonėmis, mano manymu, tai yra „must have“ tipo įrankis.

Vienintelis dalykas, kurio dar nesu radęs (ir labai norėčiau jūsų pagalbos), tai išsamus T-SQL programavimo standartas. Siūlykite komentaruose!

Rodyk draugams

IRepository ir LinqToSqlRepository klasės - SBToolkit dalis

Parašė Sergejus | 2009-06-29 21:32

Ryšium su artėjančiais Lietuvos .NET vartotojų grupės susitikimais, kur mes diskutuosime apie geriausias praktikas, papildžiau SBToolkit projektą savu repozitorijaus projektavimo šablono variantu. Apie jį pirmą kartą esu rašęs prieš metus. Per tą laiką mano repozitorijus buvo išbandytas keliuose projektuose ir ne kartą papildytas, tad pagalvojau jums gali irgi praversti ;)

SBRepository kodą rasite projekto CodePlex puslapyje.

Rodyk draugams

Automatinis susiejimų registravimas Unity konteineryje

Parašė Sergejus | 2009-04-10 20:14

Pastaruoju metu aš pakankamai daug laiko dirbu su Unity - Microsoft priklausomybės injekcijos konteineriu. Tarkime, mes turime aprašytus kelis interfeisus ir juos atitinkančias realizacijas:



Labai dažnai, servisų interfeisus aš talpinu į kitą vardų sritį negu realizacijas (tai bus svarbu vėliau). Norėdamas aprašyti aukščiau aprašytų tipų susiejimus, standartiškai tai atrodytų taip:



Kaip matyti, po registracijos aš gaunu IPrintService interfeisą atitinkančią realizaciją ir iškviečiu jos metodą Print. Problema yra tame, kad tokių susiejimų su laiku gali būti labai daug. Kai mano projekte jų atsirado 40 – teko apgalvoti galimą problemos sprendimą. Galiausiai nusprendžiau pasirašyti klasę, kuri iš tam tikros vardų srities paimtų visus interfeisus ir pabandytų jiems rasti konkrečias klases su tuo pačiu pavadinimu, tik be „I“ priekyje. Taip interfeisui IPrintService būtų ieškoma klasė PrintService. Pati klasė atrodo taip:



Pirmasis argumentas reikalingas tam, kad sužinoti kokiame rinkinyje (angl. assembly) ieškoti interfeisų bei klasių. Šios klasės dėka galima taip perrašyti pradinį kodą:



Štai ir viskas! Vietoje to, kad rankiniu būdu rašyti akivaizdžius susiejimus, tai dabar daroma automatiškai. DITypeMapper klasę rasite mano SBToolkit projekte.

Rodyk draugams

Testuojamas LINQ to SQL duomenų atvaizdavimas ASP.NET lentelėse - 2 dalis

Parašė Sergejus | 2008-10-25 22:07

Praeitoje testuojamų LINQ to SQL duomenų atvaizdavimo ASP.NET lentelėse dalyje aš parodžiau, kaip atskiriant duomenų gavimą nuo ASP.NET galima pagerinti bendrą testuojamumą. Šiandien aš pratęsiu minėtą pavyzdį papildant jį filtravimo funkcionalumu.




Tarkime, mes norime Products lentelės duomenis filtruoti pagal kategoriją. Tam papildykime prieš tai apibrėžtą ProductService klasę:




Kaip matyti, mes aprašėme naujus GetPage ir Count metodus, kurie naudoja bazinius metodus bei turi papildomą categoryId argumentą, pagal kurį ir bus filtruojami duomenys.




Atitinkamai turime atnaujinti ir mūsų ASP.NET puslapį:



Pirma į ką reikia atkreipti dėmesį – mūsų objektinis duomenų šalinis odsProducts nuo šiol priima argumentą categoryId, kurio reikšmė ateina iš elemento ddlCategories. Mūsų išskleidžiamas sąrašas užsipildo duomenimis naudojant naujai apibrėžtą objektinį duomenų šaltinį odsCategories, o tas savo ruožtu naudoja CategoryService klasės metodą GetList. Šios klasės kodas pateiktas žemiau:



Kadangi norint užpildyti išskleidžiamą sąrašą duomenimis nebūtina krauti visų objektų pilnai, o užtenka tik kelių savybių, aš apibrėžiau bendrinį objektą ServiceList, kurį ir susieju su išskleidžiamu sąrašu. Pati klasė atrodo laibai paprastai:




Taigi taip nesunkiai mes galime pridėti duomenų bazės filtravimo galimybę ASP.NET lentelėse išlaikant puikų duomenų testuojamumą!

P.S.

Jeigu turite minčių, kaip dar labiau galima pagerinti ASP.NET testuojamumą - rašykite komentaruose!

Rodyk draugams

Testuojamas LINQ to SQL duomenų atvaizdavimas ASP.NET lentelėse - 1 dalis

Parašė Sergejus | 2008-10-19 17:18

Ši savaitė, tikriausiai kaip ir pastebėjote pagal naujų straipsnių nebuvimą, buvo man labai įtempta. Tai susiję su mano mintimi automatizuoti duomenų atvaizdavimą ASP.NET lentelėse naudojant prieš kurį laiką aprašytas repositorijaus ir servisų bibliotekas. Mano noras buvo pasiekti reikalingų duomenų atvaizdavimą, puslapiavimą ir rūšiavimą rašant minimaliai kodo, bet išlaikant galimybę pilnai valdyti duomenų išrinkimą ir įgalinti išrinkimo metodų testavimą per servisų (verslo logikos) sluoksnį.




Prieš pradedant, norėčiau paraginti pirma perskaityti mano straipsnių ciklą iš LINQ to SQL testuojamumo pagerinimo.




Kaip aš jau minėjau, pirmas noras buvo panaudoti repositorijaus ir servisų bibliotekas, todėl bendra sprendimo struktūra atrodo taip:





Biblioteka Data atsako už fizinį priėjima prie duomenų bazės, aprašo esybes bei realizuoja priėjimo abstrakciją repositorijaus pagalba. Priminsiu IRepository interfeiso aprašą:




SqlRepository klasė atitinkamai realizuoja IRepository interfeisą naudojant LINQ to SQL.




Biblioteka Services aprašo bazinę servisų klasę ir tam tikras pagalbines klases bei saugo su konkrečia esybe susijusią verslo logiką.




Kadangi mano noras buvo automatiškai įgalinti bet kuriai esybei taikyti puslapiavimą ir rūšiavimą, aš apsirašiau interfeisą ISortingAndPaging:



Metodas Count grąžina bendrą įrašų skaičių, o metodo GetPage perkrovos – atitinkamai suformuotas užklausas. Kodėl IQueryable? Kadangi aš noriu puslapiavimą ir rūšiavimą daryti duomenų bazės pusėje, tai leidžia formuoti galutinę SQL užklausą keliais žingsniais.




Bazinė servisų klasė, realizuojanti minėtą interfesą, atrodo taip:



Parametras sortExpression gali turėti tris pavidalus: null – jeigu duomenys nėra rūšiuojami; stulpelio pavadinimas – jeigu duomenys rūšiuojami didėjimo tvarka; stulpelio pavadinimas per tarpą kryptis – jeigu duomenys rūšiuojami mažėjimo / didėjimo tvarka.




Jums gali kilti natūralus klausimas, o kodėl būtent tokie metodai, tokia signatūra? Taip yra todėl, nes vėliau jie bus naudojami kartu su ObjectDataSource.




Kitas dalykas i kurį norėčiau atkreipti dėmesį – praplėtimo metodas Page. Tai yra įdomiausias metodas pagal savo realizacija su kuriuo man teko dirbti paskutiniu metu. Nors galutinio kodo ir nėra daug, bet prie jo aš prasidėjau apie dvi dienas…



Pagrindinė šio metodo paskirtis yra performuoti perduotą užklausą taip, kad joje būtų atsižvelgta į puslapiavimo ir rūšiavimo funkcionalumą. Jeigu puslapiavimas realizuojamas tiesiog iškviečiant Skip ir Take metodus, tai su rūšiavimu viskas yra žymiai įdomiau. Kaip žinia LINQ rūšiavimui siūlo du metodus: OrderBy ir OrderByDescending. Problema yra tame, kad metodas kaip argumentą priima LINQ lambda išraišką. Kadangi stulpelį pagal kurį norima rūšiuoti mes sužinome tik vykdymo metu, teko surasti galimybę formuoti lambda išraišką dinamiškai.




Aprašę praplėtimo metodą Page ir bazinę klasę ServiceBase, produktų serviso klasė atrodytų tiesiog taip:



Noriu pabrėžti, tokia paprasta klasė jau pilnai moka save puslapiuoti ir rūšiuoti!




Beliko tai panaudoti ASPX puslapyje kartu su ObjectDataSource:



Paprastumo dėlei aš naudosiu GridView komponentės automatinį stulpelių generavimą. Kaip gi įvyksta visas užpildymas? ObjectDataSource komponente mes nurodome tipą, kurio metodai bus kviečiami. SelectCountMethod ir SelectMethod atributuose nurodome metodus, kurie bus naudojami bendram įrašų skaičiui gauti ir išrinkti visus duomenis reikalinga tvarka. Kadangi mes nurodome EnablePaging=true, tai į GetPage automatiškai bus perduodami du argumentai: pradinės eilutės indeksas ir kiek įrašų rodyti nuo tos eilutės (puslapio dydis). Per atributus StartRowIndexParameterName ir MaximumRowsParameterName mes juos ir susiejame. Panaši situacija ir su rūšiavimu. Į mūsų GetPage metodą bus perduodama rūšiavimo išraiška, o susiejimas su argumentu vyksta per atributo SortParameterName reikšmę.



Paskutinis dalykas, tai įvykio OnObjectCreating apdorojimas. Jeigu įvykis nėra apdorojamas, tai ASP.NET automatiškai bando sukurti objektą, kurio tipas nurodytas atribute TypeName. Kadangi ProductSerice klasė neturi tuščio konstruktoriaus (tik konstruktorius, kuris priima IRepository objektą), tai reikia tokį pridėti ir jo viduje sukurti numatyto tipo repositorijų; arba apdoroti įvykį OnObjectCreating ir jame patiems sukurti reikalingo tipo objektą. Aš parodysiu būtent antrą variantą, nes vėliau aš parodysiu kaip tai galima automatizuoti panaudojant priklausomybės injekciją ir Unity. Šiam kartui apsiribokime tokia realizacija:



Rezultate mes gauname SQL užklausas, panašias į žemiau pateiktą:


Taigi taip paprastai mes ne tik galime atvaizduoti visus reikalingus įrašus ASPX puslapyje, bet išlaikydami visą verslo logiką Service bibliotekoje, mes galime ją gerai ištestuoti unit testų pagalba.



Kitoje dalyje, aš parodysiu kaip praplėsti šį pavyzdį filtravimo funkcionalumu su minimaliu kodo pakeitimu!

Rodyk draugams

LINQ to SQL testuojamumo pagerinimas - 4 dalis (IoC, DI)

Parašė Sergejus | 2008-06-26 00:01

Praeitose keturiose dalyse mes pilnai įgyvendinome LINQ to SQL testuojamumo pagerinimo prototipą (kodas prieinamas čia).


Kontrolės inversija ir priklausomybės injekcija


Prisiminkime, LINQ to SQL repositorijuje nėra nurodytas konkretus duomenų kontekstas, jį mes perduodame per konstruktorių. Tas pats liečia ir ProductService klasę, verslo logikai mes galime nurodyti bet kokį repozitorijų, įskaitant menamą. Šis principas vadinasi kontrolės inversija (Inversion of Control, IoC) ir padeda sumažinti klasių sukibimą.


Nepaisant fakto, kad mūsų klasės mažai sukibusios, testinė programa glaudžiai rišasi su NorthwindDataContext, SqlRepository ir ProductService. Jeigu mes nagrinėtume web aplikacija – ten toks prisirišimas būtų kiekviename puslapyje, kas nėra gerai iš dizaino pusės. Norint pakeisti repozitorijų (pavyzdžiui iš LINQ to SQL į Entity Framework), mums tektų keisti kodą kiekvienoje vietoje, kur naudojamas repozitorijus arba verslo logikos servisas.



Kas esate susipažinę su kontrolės inversijos principu žinote, kad kartu nagrinėjama ir kita sąvoka – priklausomybės injekcija (Dependency Injection, DI). Jos pagalba galima nusakyti,  kad ten kur bus prašomas IRepository tipo objektas, turi būti sukurtas ir pateiktas SqlRepository objektas. Kadangi toks susiejimas aprašomas vienoje vietoje, programos kodo prisirišimas prie konkrečių klasių žymiai sumažėja, kas ilgainiui sutaupo nemažai laiko.


Šiuo metu egzistuoja nemažai .NET Framework skirtų priklausomybės injekcijos karkasų: Castle Windsor, StructureMap, Spring.NET, Ninject, Autofac ir daugelis kitų. Ne taip seniai pasirodė naujas Microsoft sukurtas priklausomybės injekcijos karkasas Unity. Juo mes ir pasinaudosime.


Unity


Unity leidžia tipų susiejimus aprašyti tiek XML faile, tiek kode. Aš parodysiu kaip tai daroma iš kodo, tam apibrėžkime statinę klasę UnityConfig:



Pagrindinė Unity klasė yra UnityContainer. Metodo RegisterType pagalba galima susieti bendresnį tipą su labiau specifiniu. Taip, pavyzdžiui, visur kur bus prašoma Unity gauti IProductService objektą, bus grąžinamas naujas ProductService objektas. Už tokių objektų grąžinimą yra atsakintas metodas Resolve.


Atnaujinta mūsų programa atrodys taip:



Bandant paleisti šią programą, bus išmesta tokia klaida



Tai susiję su tuo, kad kuriant nurodyto tipo objektą, Unity bando pasinaudoti daugiausiai parametrų priimančiu konstruktoriumi. Kadangi NorthwindDataContext turi du konstruktorius su dviem argumentais, Unity negali nuspręsti kuriuo iš jų pasinaudoti. Tarkime, mes norime kad naujas NorthwindDataContext būtų kuriamas naudojant konstruktorių be parametrų. Vienas iš būdų tai pasiekti, nurodyti atributą InjectionConstructor virš tuščio konstruktoriaus:



Toks sprendimas tinka nuosavoms klasėms, bet ne automatiškai generuojamoms (tokioms kaip LINQ to SQL klasės). Kitas būdas – atnaujinti UnityConfig klasės konstruktorių:



Metodas Configure leidžia nurodyti, kad NorthwindDataContext tipo objektas turi būti kuriamas naudojant tuščią konstruktorių.


Grįžtant prie metodo Resolve, verta parodyti kaip bus vykdomas IProductService objekto kūrimas:



  • Pirma Unity norės sukurti ProductService objektą, bet tam reikia į konstruktorių perduoti IRepository objektą.

  • Tada Unity peržiūrės susiejimus ir norės sukurti SqlRepository objektą, bet ir jam reikia į konstruktorių perduoti DataContext objektą.

  • Pagal susiejimą ir konfigūraciją Unity sukurs NortwindDataContext objektą, tada užbaigs SqlRepository objekto kūrimą ir galiausiai grąžins sukurtą ProductService objektą.

Kaip matyti iš veiksmų eiliškumo, mūsų testiniai programai duomenų kontekstą išreikštinai net nereikia kurti, todėl galutinis variantas atrodo taip:



Matome, kad nuo šiol mūsų programa nepriklauso nuo jokios konkrečios klasės (išskyrus Product), todėl ir sukibimas yra minimalus.


Pabaiga


Su šia dalimi aš baigiu LINQ to SQL testuojamumo pagerinimo straipsnių ciklą ir LABAI laukiu jūsų atsiliepimų bei pageidavimų komentaruose!

Rodyk draugams

LINQ to SQL testuojamumo pagerinimas - 3 dalis (Mocking)

Parašė Sergejus | 2008-06-25 00:00

Praeitoje dalyje mes realizavome verslo logikos klasės ProductService funkcionalumą (išeities tekstai prieinami čia). Kaip ir žadėjau, šiandiem mes užsiimsime jos testavimu, bet prieš tai prisiminkime klasių diagramą:



ProductService testavimas


Kaip ir SqlRepository atveju, ProductService klasei aš pasirašiau keletą testavimo atvejų:



Pateiktame kode jus turėtų sudominti metodas Init. Atsimenate, mūsų pagrindinis tikslas buvo atskirti verslo sluoksnį nuo duomenų sluoksnio. Kadangi duomenų sluoksnį mes ištestavome ir žinome, kad jis veikia gerai, verslo sluoksnio testavimui nėra prasmės naudoti duomenų bazę. Tai be reikalo apkrautų serverį bei ženkliai prailgintų testų vykdymą.


Vienas iš galimų sprendimų – sukurti klasę, kuri realizuoja interfeisą IRepository ir tvarko įrašus atmintyje. Pagrindinė problema – kiekvienam verslo servisui reikėtų rašyti po savo klasę: ProductMemoryRepository, CategoryMemoryRepositorty ir pan. Šiuo atveju, aš pasinaudojau vienu iš sparčiai populiarėjančių Mock karkasų – MoQ.


MoQ panaudojimas testavime


Mock karkasų paskirtis – leisti sukurti netikrą (menamą) nurodyto tipo objektą bei aprašyti metodų reakciją į tam tikrus duomenis. Išanalizuokime pateiktą kodą:



  • Pirma sukuriamas netikras objektas, tenkinantis interfeisą IRepository ir konkretizuotas klasei Product. Svarbu suprasti, kad sukurtas objektas tik realizuoja interfeisą, bet nepateikia jokios realizacijos.

  • Toliau aprašome testinius duomenis, kuriuos turi grąžinti menamas repozitorijus (metodas aprašytas skiltyje Helpers).

  • Kadangi testavimui reikalingi ne visi, o tik du IRepository metodai GetAll ir IsNew, toliau aprašomi norai kiekvienam iš metodų.

  • Pirmas noras nusako, kad kviečiant GetAll, metodas grąžintų prieš tai sukurtus produktus. Labai patogu, kad metodų (ir savybių) pavadinimai išreiškiami ne tekstu, bet panaudojant Lambda išraiškas (ateinančiuose straipsniuose aš parodysiu kaip tai apsirašoma).

  • Antras noras nurodo, kad kviečiant IsNew su bet kuriuo Product tipo objektu, metodas grąžins teigiamą reikšmę tik esant produkto ID lygiam 0.

  • Paskutiniu žingsniu tiesiog perduodame aprašytą menamą repozitorijų į ProductService konstruktorių.

Matome kaip parastai ir efektyviai galime testuoti verslo logiką, atskirę ją nuo LINQ to SQL duomenų konteksto! Kitoje dalyje aš parodysiu kaip galima dar labiau sumažinti klasių sukibimą, panaudojant kontrolės inversijos ir priklausomybės injekcijos principus bei naują Microsoft priklausomybės injekcijos karkasą Unity.

Rodyk draugams

LINQ to SQL testuojamumo pagerinimas - 2 dalis

Parašė Sergejus | 2008-06-24 00:08

Praeitoje dalyje mes įgyvendinome SqlRepository klasę (kodas prieinamas čia) ir dabar klasių diagrama atrodo taip:



ProductService įgyvendinimas


Atskirę ir ištestavę duomenų sluoksnį, laikas pereiti prie verslo logikos. Šiame prototipe aš apsiribosiu dviem metodais: GetProduct ir ProductsInStock. Tam pirma aprašysime IProductService interfeisą




Ir vėl matome, ProductsInStock metodas grąžiną IQueryable, tam kad šio metodo rezultatu galima būtų ir toliau operuoti. Interfeiso IProductService realizacija yra pakankamai paprasta:



Metodas GetProduct grąžina egzistuojantį produktą jeigu toks egzistuoja repozitorijuje (nebūtinai duomenų bazėje) arba naują – priešingu atveju. Šių metodų panaudojimas galėtų būtų toks:



Kadangi metodas ProductsInStock grąžina IQueryable, mes galime taikyti jam standartinius išplėtimo metodus. Tiek šiam kartui, kitoje dalyje aš aptarsiu MoQ karkaso panaudojimą testuojant šią klasę.

Rodyk draugams

LINQ to SQL testuojamumo pagerinimas - 1 dalis

Parašė Sergejus | 2008-06-23 00:01

Praeitoje dalyje mes aptarėme repozitorijaus projektavimo šabloną bei aptarėme esybes, su kuriomis dirbsime toliau. Kodas prieinamas čia.


SqlRepository įgyvendimas


Pats laikas realizuoti repozitorijų, skirtą darbui su LINQ to SQL:



SqlRepository klasės konstruktorius priima duomenų kontekstą, su kuriuo vėliau ir bus dirbama. Tai leis atrišti verslo logikos ir duomenų sluoksnius. Kintamasis identity – tai nuoroda į LINQ to SQL esybės savybę, kuri susieta su pirminiu raktu. Vėliau pasinaudojant šiuo kintamuoju mes galėsime nuskaityti esybių pirminius raktus. Toliau realizuojame visus interfeiso IRepository metodus iš eilės:



Šalia to, SqlRepository klasėje naudinga realizuoti interfeisą IDisposable, kas leis užtikrinti duomenų konteksto atlaisvinimą:



SqlRepository testavimas


Tam, kad įsitikinti mano repozitorijaus šablonas veikia gerai, aš aprašiau 14 testavimo atvejų:



Tenka pripažinti, aš nesu labai patyręs testavimo atvejų rašyme, bet tikrai malonu žiūrėti į “žalius” rezultatus



Aprašyto repozitorijaus panaudojimas galėtų būti toks:



Kaip matyti, duomenų kontekstas yra panaudojamas tik vieną kartą, perduodant jį repozitorijaus konstruktoriui. Kitoje dalyje mes įgyvendinsime ProductService klasę…

Rodyk draugams