TeamNet Data File Export - Librarie Open Source

by adrian.tosca 5. May 2009 12:03

Exportul de date in fisiere poate fi o parte importanta a unei aplicatii mai ales atunci cand este nevoie de interoperabilitate cu aplicatii mai vechi. TeamNet Data File Export este o librarie folosita intern de TeamNet care exporta in mod foarte simplu fisiere DBF si CSV dintr-o sursa de date existenta cum ar fi un DataSet sau o lista de obiecte. Aceasta librarie este acum open source si poate fi obtinuta de pe site-ul CodePlex.

Libraria este scrisa in c# si a fost conceputa pentru a nu avea nici un fel de dependinte externe. Design-ul librariei este orientat pentru exportul cu usurinta a datelor si extensibilitate. Pe pagina proiectului http://datafileexport.codeplex.com/ se gaseste si un exemplu de utilizare pentru exportul intr-un fisier DBF. Alte exemple de folosire se pot gasi in sursa proiectului de teste. 

Tags:

How to load an assembly from a location on disk at runtime

by adrian.tosca 27. September 2008 09:49

Let's say there is an assembly in a certain location on disk and should be loaded at run time. Possible uses for these are: loading external addins for an application, meta information tools etc. How to do this? Well the first idea is to use Assembly.Load(fileName). Unfortunately this overload of the Load method take only an assembly name string, not a file path and can be used only if the assembly is in the same path as the calling assembly. The following will load the TestAddIn.dll assembly from the same location as the calling assembly:

Assembly testAddIn = Assembly.Load("TestAddIn");

 

If one tries to pass the full file name to the method, for example:

Assembly testAddIn = Assembly.Load(
    @"c:\AssemblyLoadSolution\AssemblyLoad\AddIns\TestAddIn.dll");

 

The following FileLoadException will be thrown: "Could not load file or assembly 'file' or one of its dependencies. The given assembly name or codebase was invalid."

The solution is to use another overload of the method Assembly.Load(AssemblyName assemblyRef). The AssemblyName describes the assembly in full including version and strong name. But for a simple use you only need to use the Name and CodeBase properties:

AssemblyName name = new AssemblyName("TestAddIn");
name.CodeBase = @"c:\AssemblyLoadSolution\AssemblyLoad
    \AddIns\TestAddIn.dll";
Assembly testAddIn = Assembly.Load(name);

 

The CodeBase property needs to be set to the full file path of the assembly. The only catch is that the application will need permissions to the folder where the application is loaded. This might be an additional issue on web applications.

Tags: , , ,

Programming

How to abuse exception handling

by adrian.tosca 2. August 2008 12:09

1.

Exceptions are silly. Just do a:

try {
	/* do stuff */
} catch {}

and you will never get any exceptions. Do this for each block of code. Randomly let some lines of code outside the try catch block to appear that you carefully considered which blocks of code to handle and which not.

2.

If the users report they receive an error about some weird exception, don't spend too much time to identify the reason the exception appear. Just find the place where the exception is thrown and do a:

try {
	/* stuff that caused the exception*/ }
catch (WeirdException) {}

Make absolutely sure you do not log the exception. Also very important, don't write any meaningful comment about the reason the exception is catch.

3.

Catch exceptions and throw them again without any change:

try {
	/* do stuff */
} catch (Exception ex) {
	throw ex;
}

This way you can not be accused that you didn't handle exceptions. The loosing of the exception stack trace is an added bonus.

4.

Put a lot of exception handling and make sure it is intricate with the code logic:

Service s; 
try {
	s = new Service();
	if (s.IsOnline) {
		try {
			s.IsOnline = false;
		} catch (Exception ex2) {
			s.Link = s.DefaultlLink;
		}
	} else {
		try {
			s.ResetDefaultLink();
		} catch (Exception ex3) {
			s.IsOnline = true;
		}
	}
} catch(Exception ex1) {
	s = new Service(true);
	s.IsOnline = s.Link == s.DefaultLink ? true : false;
}

Anyone who looks over this code will be amazed to see the great care you took for exception handling. Do not add any comment as this would clutter the code even more than already is. No one will understand a thing of that code, nor even you, but this is expected as everyone knows exception handling is hard to code.

5.

Make your own exception class and use it every time to re-throw exceptions:

public class MyApplicationException : Exception {
	public MyApplicationException(string m) : base(m) {
	}
}

try {
	/* do stuff */
} catch (Exception ex) {
	throw new MyApplicationException("Error message here.");
}

Make sure the error message is as unintelligible or as useless as possible. "An error occured" is a good candidate and should be amoung your favorites. Also very important is to make sure you do not include the original exception as inner exception.

Tags: , , ,

Programming

The Specification Pattern and c# 3.5

by adrian.tosca 30. July 2008 13:37

The specification pattern popularized by Eric Evans and Martin Fowler can prove a powerful tool to make rules scattered all around the code explicit and part of the model. The specification pattern, in its simplest form is no more than Boolean test implemented in terms of objects.

Test methods like productStock.IsEmpty() are part of any application and as long as they are simple they can remain as Boolean tests. But tests often depend on complex object state. The tests can be combined to create even more complex rules. The rules are often scattered in every corner of the application and the same tests can appear in more than one place, making modifications difficult and error prone. In such cases it is often better to refactor the rules in classes and make the tests explicit specifications.

In the simple case of the productStock.IsEmpty() the initial code:

class ProductStock {
	public int ProductCount {
		get {
			return _productCount;
		}
	}

	public bool IsEmpty() {
		return ProductCount == 0;
	}
}

Can be refactored into:

class ProductStock {
	public int ProductCount {
		get {
			return _productCount;
		}
	}
}

class EmptyStockSpecification {
	bool IsSatisfiedBy(ProductStock candidate) {
		return candidate.ProductCount == 0;
	}
}

The new class EmptyStockSpecification is a "specification" that states a constraint on the state of another object. The simplest use of this new class is to test if an object satisfy the criteria. It doesn't look like a big improvement but the usefulness became apparent when the simple specifications can be combined to provide complex rules the same way the predicates are combined in logic expressions. The pattern remain simple but allow complex rules without a complex model. There are three main uses of specification pattern:

Validation - To see if an object fulfill some conditions or is ready for some use

if (emptyStockSpec.IsSatifiedBy(prodStock)) {
    ...
}

Selection - To select a subset of objects from a collection based on some criteria

foreach(ProductStock prodStock in allProdStocks) {
    if (emptyStockSpec.IsSatifiedBy(prodStock))
        results.Add(prodStock);
}

Generation - To allow creation of new objects that fulfill some need

ProductStockView view = new ProductStockView();
foreach(ProductStock prodStock in allProdStocks) {
    if (emptyStockSpec.IsSatifiedBy(prodStock))
        view.ProductStocks.Add(prodStock);
}

The three uses are the same on conceptual level. The selection is one of the most used pattern. In c# 2.0 there was possible to use the composite pattern to build complex specifications out of simple ones as can be seen in this very good presentation of the specification pattern. In c# 3.5 the possibility to use lambda expressions and LINQ to Entities for combining specifications makes the pattern much simpler to use.

For example,

struct ProductStock {
	public int ProductCount;
	public int MinLimit;
	public DateTime NextSupplyDate;
}

interface ISpecification
{
	bool IsSatisfiedBy(T candidate);
}

class CloseToMinLimitStockSpecification : ISpecification {
	decimal _variationPercent;
	public CloseToMinLimitStockSpecification(decimal variationPercent) {
		_variationPercent = variationPercent;
	}
	public bool IsSatisfiedBy(ProductStock candidate) {
		return candidate.ProductCount < 
			candidate.MinLimit * (1 + _variationPercent);
	}
}

defines the CloseToMinLimitStockSpecification specification that allows selection of product stocks that are close to the minimum stock limit. The new class can be used to select from a list of stocks the ones that are close to the minimum limit like this:

CloseToMinLimitStockSpecification closeToMinLimit =
	new CloseToMinLimitStockSpecification(0.1M);
IList stocksCloseToLimit =
	all.Where(p => closeToMinLimit.IsSatisfiedBy(p)).ToList();

The possibility to combine specifications in complex conditions is very powerful and simple to use. If another specification that allows selection of products stocks for which the supply date is not far away is defined like this,

class FarToSupplyDateStockSpecification : ISpecification {
	TimeSpan _span;
	public FarToSupplyDateStockSpecification(TimeSpan span) {
		_span = span;
	}
	public bool IsSatisfiedBy(ProductStock candidate) {
		return DateTime.Now.Add(_span) <= candidate.NextSupplyDate;
	}
}

the two specifications can be used to make a complex selection:

FarToSupplyDateStockSpecification farToSupplyDate =
	new FarToSupplyDateStockSpecification(new TimeSpan(60, 0, 0, 0));
IList stocksLikelyToGoUnderLimit =
	all.Where(p => closeToMinLimit.IsSatisfiedBy(p)
	&& farToSupplyDate.IsSatisfiedBy(p)).ToList();

However if it is necessary to combine specifications to make complex rule explicit classes, the composite pattern is to be used. The same classes can be even used with database objects but of course there are performance issues to consider in this case that require a more complex approach.

Download the complete example SpecificationPattern.zip (3.76 kb) containing the code listed above.

Tags:

Programming

&#42;-pedia

by adrian.tosca 25. July 2008 11:27

Wikipedia cu toate neajunsurile ei a fost clar un succes. Da are o multime de greseli in articole (dar si Britannica are desi este editata numai de experti asa cum se poate vedea de aici), da are mereu probleme de vandalism insa este din ce in ce mai populara si a reusit sa reziste desi i s-a prevazut de multe ori sfarsitul. 

De curand google a lansat un nou serviciu (da inca unul) care are ca tinta cam aceeasi nisa ca wikipedia. Nu, nu se numeste googlepedia, se numeste knol. Deocamdata are pagini create de experti insa tocmai a fost deschisa catre public si oricine va putea scrie articole. Noutate consta in faptul ca autorii vor primi ceva bani pentru articolele postate (prin adsense) si va exista si un sistem de ranking.

Acum, de ce are nevoie google sa lanseze un wikipedia killer? Pai unul din motive ar fi ca multe pagini din wikipedia au rank mai mare decat propriile pagini. Cea mai buna dovata? Pai, chiar pagina knol apare in cautari mai sus in wikipedia decat pagina de start knol. Si la urma urmei de ce nu, google a facut o afacere din vanzarea de publictate si acum o extinde pe oriunde poate, chiar si prin Yahoo dupa cate s-a auzit recent. Ramane de vazut daca calitatea articolelor va fi sau nu mai buna decat cea din wikipedia, si daca posibilitatea de a castiga bani nu se va transforma intr-un scop in sine (cateva click-uri pe ratingul articolelor concurente poate aduce un articol mai slab calitativ in top).

Ceea ce e clar e ca viitorul *pediilor e online mai ales ca recent chiar si britannica si-a deschis portile pentru creearea de articole de catre comunitate.

Tags:

General

Misconcepţii despre tuningul de performanţă

by adrian.tosca 24. July 2008 10:16

„Imbunătaţirea performanţei se face prin scrierea de cod rapid”

Această părere este foarte populară printre programatori dar se dovedeşte falsă în majoritatea cazurilor. Cele mai dramatice imbunătăţiri de performanţă se pot face din faza de proiectare iar mici modificări de arhitectură poat face diferenţa între un program atât de lent că e de nefolosit si unul acceptabil. Modificările la nivel de cod au de cele mai multe ori efecte locale şi chiar dacă se alege această soluţie, cele mai mari imbunătăţiri se obţin prin schimbarea algoritmilor si stucturilor de date folosite.

„Performanţa este o cerinţă funcţională”

Performanţa este specificată ca cerinţă funcţională de foarte multe ori fară a fi neaparăt necesară. Am citit despre un sistem ale cărui cerinţe specificau foarte clar că timpul de răspuns pentru utilizatorul final trebuia să fie mai mic de 1 secundă [1]. După terminarea analizei, s-a făcut o propunere de arhitectură estimată la aproximativ 100 milioane $. S-a considerat că sistemul este mult prea scump şi s-au căutat alternative. Dupa o analiză s-a ajuns la concluzia că utilizatorii finali pot folosi foarte bine sistemul şi cu un timp de raspuns < 3 secunde. După refacerea arhitecturii, costul estimat s-a redus la 30 milioane $.

Înainte de a investi timp pentru a rezolva problemele de performanţă asigură-te că rezolvi o problemă care chiar trebuie rezolvată.

„Reducerea numărului de linii de cod îmbunătaţeşte viteza”

Mulţi programatori cred cu tenacitate că dacă scriu secvenţe de cod în 1 sau 2 linii fac codul cât se poate de eficient. Un caz oarecum limită poate arăta dacă asta este sau nu adevărat.

Să considerăm următorul cod care iniţializează un array cu 10 elemete :

  for (int i = 0; i != 10; i++)
    a[i] = i;

Codul va fi mai rapid sau mai încet decât următoarele 10 linii de cod care fac acelaşi lucru?

  a[0] = 0;
  a[1] = 1;
  ...
  a[9] = 9;

Dacă se urmăreşte principiul cu "mai puţine linii înseamnă cod mai rapid" se concluzionează că primul fragment de cod e mai rapid. Pentru c# testele arată însă că al doilea fragment este mai rapid cu aproximativ 8%. În cazul altor limbaje de programare diferenţele sunt chiar mai semnificative[2].

Bineînţeles asta nu înseamnă că creşterea numărului de linii de cod îmbunătaţeşte viteza. Înseamnă doar că nu există o relaţie predictibilă între numărul de linii de cod şi viteza de rulare.

„Probabil că anumite operaţii sunt mai rapide decât altele”

Nu există noţiunea de probabilitate atunci când se vorbeşte de performantă. Performanţa trebuie întodeauna măsurată pentru a ştii dacă modificările au îmbunătaţit sau nu viteza de execuţie. Regulile se schimba de fiecare dată când se schimbă limbajul de programare, compilatorul, versiunea compilatorului, librăriile folosite, versiunile librăriilor, procesorul, cantitatea de memorie, sistemul de operare, culoarea tricoului pe care îl porţi (OK, ăsta nu) şi aşa mai departe[2]. Ceea ce e adevărat pe o maşină cu un anumit set de librării poate fi fals pe altă maşină cu alt set de librării.

Acest comportament sugerează mai multe motive pentru a nu îmbunatăţii performanţa prin tuning de cod. Tehnicile care îmbunătaţesc performanţa într-un anumit mediu o pot degrada în alt mediu. Câteodata modificările de cod „deştepte” pot anula optimizări ale compilatorului care au fost gândite să funcţioneze pe cod simplu si clar.

„Optimizările trebuie făcute pe parcurs”

Există o teorie care spune că dacă te chinui să scrii cod rapid si eficient pe parcurs ce scrii fiecare rutină, programul per ansamblu va fi mai rapid şi mai eficient. Această abordare creeaza însă o situaţie de tip „pădurea care nu se vede de copaci” în care programatorii ignoră optimizări globale semnificative pentru că sunt prea ocupaţi cu micro-optimizările.

Există câteva probleme asociate cu optimizările făcute pe parcurs:

a) Este imposibil să se identifice gâtuirile[3] de performanţă înainte ca programul să fie complet. Există mai multe studii care arată ca majoritatea timpului de execuţie al unei aplicaţii se consumă intr-o zonă restrânsă de cod. Un studiu clasic arată ca aproximativ 50% din timpul de execuţie al unei aplicaţii se consumă în 4% din cod [4]. În acest studiu Knuth a reuşit să imbunătăţească performanţa unei aplicaţii (chiar programul de profiling pe care îl folosea) cu 50% prin schimbarea câtorva linii de cod în doar două rutine. Asta are ca implicaţie că trebuie căutate şi optimizate acele rutine care consumă cel mai mult timp. Programatorii ghicesc foarte rar care anume procent de 4% consuma cel mai mult timp de execuţie, deci programatorii care optimizează pe parcurs or sa petreacă în medie cu 96% mai mult timp pentru a optimiza cod care nu are nevoie de optimizări.

b) În rarele cazuri in care un programator indentifică corect locurile cu probleme de performanţă se fac prea multe modificări locale care pot afecta performanţa sistemului pe ansamblu. Optimizările făcute dupa ce s-a terminat implementarea pot identifica mai bine zonele cu probleme în tot sistemul precum şi importanţele relative între ele. Optimizărilor făcute pe parcurs suferă din cauza lipsei de perspectivă.

c) Focalizarea pe optimizare in faza de dezvoltare diminuează atenţia de la alte obiective importante. Apar dezbateri şi analizele de algoritmi în faze incipiente care nu aduc prea mare valoare pentru utilizatorul final. Mult mai importante în aceste faze sunt corectitudinea şi usurinţa de citire a codului iar acestea sunt mult mai greu de îmbunatăţit mai tarziu comparativ cu performanţa. Principala problemă cu care trebuie să se confrunte programatorii este complexitatea şi controlării acesteia trebuie să îi fie dedicată majoritatea timpului de dezvoltare. Ce ai face mai degraba într-o fază finală? Muncă pentru imbunătăţirea performanţei pe 4% din aplicaţie sau muncă pentru imbunătăţirea calităţii codului pe 100% din aplicaţie?

„Un program rapid e la fel de important ca unul corect”

Niciodată nu este adevarat că este mai important ca un program să fie rapid înainte de a fi corect. Următoarea relatare anectodică exemplifică foarte bine ideea.

Un programator senior care lucra pe post de consultant, a făcut o deplasare la o firmă pentru a rezolva o problemă cu care se confruntau cei de acolo. După o lungă discuţie însă, s-a ajuns la concluzia că rezolvarea este mult prea complicată si până la urmă s-a decis închiderea proiectului. A doua zi însă, consultantului i-a venit o idee simplă pentru a rezolva problema. S-a întors la firmă, a convins lumea să redeschidă proiectul şi a implementat soluţia respectivă. La discuţia care a urmat după terminarea implementării, unul din programatorii de la firmă a întrebat cât durează rularea programului în noua implementare.

— Cam 10 secunde pentru date de intrare obişnuite, răspunde consultantul.
— Hei, intervine din nou programatorul de la firmă, dar versiunea veche rula sub 1 secundă pentru acelaşi tip de date de intrare.
— Da, răspunde consultantul, dar versiunea veche nu funcţiona. Dacă programul meu nu trebuie să funcţioneze, pot sa îl fac să ruleze instant[2].

Pentru o anumită clasă de proiecte performanţa este foarte importantă, însă această clasă este mult mai mică decât cred cei mai mulţi. Pentru aceste proiecte performanţa trebuie planificată din primele faze printr-un design adecvat. Pentru celelalte tipuri de proiecte orice optimizare prematură este o ameninţare pentru calitatea generală, incluzand chiar perfomanţa.

----------
[1] Jon Louis Bentley, More Programming Pearls: Confessions of a Coder, 1988
[2] Steve McConnell, Code Complete 2nd edition, 2004
[3] Adica “bottlenecks” :)
[4] Donald Knuth, An empirical study of Fortran programs, 1971
[5] Edsger W. Dijkstra, The Humble Programmer, 1972

Tags: , ,

Performance & Scalability

Powered by BlogEngine.NET 1.5.0.7
Theme by Mads Kristensen

RecentComments

Comment RSS

Calendar

<<  March 2010  >>
MoTuWeThFrSaSu
22232425262728
1234567
891011121314
15161718192021
22232425262728
2930311234

View posts in large calendar