Kiedy nie warto używać DataSet Designera

Tipsy   |    2 wrzesień 2008 1:55

Każdy kto oglądał chociaż jedną prezentację na temat Visual Studio, wie jak efektowny jest DataSet Designer dołączony do tego narzędzia. Pozwala on na szybkie zmapowanie struktur bazy do struktur w kodzie przez co otrzymujemy silnie typowanego DataSeta. Dzięki temu można odwoływać się do elementów DataSeta “po kropce”, czyli np. DataSet.Tabela1.Wiersze[5].KolumnaA.

Jest to dosyć wygodne narzędzie i na początku wydaje się, że dzięki niemu będzie można podbić świat i okoliczne galaktyki. Jednak rzeczywistość wygląda tak, że to narzędzie może służyć do podbicia podwórka ew. osiedla albo miasta, ale już kraju to się tym nie podbije.

Kreator DataSeta jest przydatny jeżeli masz do napisania prostą aplikację. Powiedzmy 3-4 tabele i mało relacji, mało obliczeń. Przydatny jest także do programów dla nauki. Wtedy możesz szybko przygotować widoki na dane, ekrany edycyjne, szybko zrobić obliczenia statystyczne. Jednak jeżeli piszesz komercyjny program, który ma działać dobrze i chcesz zapewnić sobie spokój w nocy, po prostu zrezygnuj z kreatora DataSeta.

Oczywiście ten post nie ma na celu zniechęcać całkowicie do kreatora, ale dać szerszy obraz tego, jakie przygody czekają na podróżnika, który odwiedzi świat kreatora DataSeta.

Problemy przedstawione w tym poście dotyczą zarówno wersji Visual Studio 2005 jak i VS 2008, w której to nie zmieniło się dużo jeżeli chodzi o architekturę kodu generowanego przez kreator. A zaczniemy od niego. Cała zabawa w pisanie dobrego kreatora polega na tym, żeby kod przez niego wygenerowany był łatwo debuggowalny jeżeli coś pójdzie nie tak. W końcu błędów w zaprojektowaniu kreatora nie da się uniknąć. Chcemy mieć pewność, że jeżeli kreator zadziała nie tak, będziemy mogli ręcznie naprawić jego błędy.

Skąd błędy w kreatorze DataSeta

Tak jest w przypadku kreatora okien (designera okien), ale nie w przypadku kreatora DataSeta.  Kod generowany jest w sposób chaotyczny, a elementy, które powinny być ze sobą jasno powiązane, nie są. Spójrz na ten kawałek wygenerowanego kodu:

[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
private void InitCommandCollection() {
this._commandCollection = new global::System.Data.SqlClient.SqlCommand[2];
this._commandCollection[0] = new global::System.Data.SqlClient.SqlCommand();
this._commandCollection[0].Connection = this.Connection;
this._commandCollection[0].CommandText = “SELECT ID, NAME from tabTabela”;
this._commandCollection[0].CommandType = global::System.Data.CommandType.Text;
this._commandCollection[1] = new global::System.Data.SqlClient.SqlCommand();
this._commandCollection[1].Connection = this.Connection;
this._commandCollection[1].CommandText = “SELECT ID, TELEFON from tabUser”;
this._commandCollection[1].CommandType = global::System.Data.CommandType.Text;
}

[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.ComponentModel.Design.HelpKeywordAttribute("vs.data.TableAdapter")]
[global::System.ComponentModel.DataObjectMethodAttribute(global::System.ComponentModel.DataObjectMethodType.Fill, true)]
public virtual int Fill(MyDataSet.MyDataTable dataTable) {
this.Adapter.SelectCommand = this.CommandCollection[0];
if ((this.ClearBeforeFill == true)) {
dataTable.Clear();
}
int returnValue = this.Adapter.Fill(dataTable);
return returnValue;
}
Co z tego przydługiego kodu wynika? Otóż to, że w kodzie wygenerowanym przez kreatora SqlCommand nie jest powiązane z metodami. Po prostu ktoś kto pisał tego kreatora, przyjął, że zapytania do bazy danych będą trzymane w  kolekcji _commandCollection, a w momencie wywołania konkretnej metody, odpowiednie zapytanie zostanie wstawione do SqlDataAdapter tego kreatora (czyli this.Adapter). W ten sposób nie można określić z jakiego zapytania korzysta na przykład metoda Fill, albo inna, własna, którą możesz przecież dodać z poziomu kreatora. Czyli reasumując: widzisz zapytanie w kreatorze, ale z poziomu kodu nie wiesz jakie zapytanie (PobierzUnikalneImiona, PobierzSumeVat itp.) korzysta z jakiego zapytania (’Select distinct First_Name’, “Select SUM(VAT)’).

Zazwyczaj wiedza ta nie jest potrzebna. Jednak sposób zaprojektowania tego rozwiązania jest przykładem wielu miejsc w kodzie generowanym przez kreatora gdzie trzeba wróżyć z fusów. Jest to o tyle ważne, że obsługa tego kodu działa w dwie strony: na podstawie tego co ustawiasz w kreatorze jest tworzony kod DataSeta, a z kodu DataSeta jest tworzone to co widzisz w kreatorze. A w tym miejscu twórca kreatora musi domyślać się jakie zapytanie jest powiązane z jaką metodą. Domyślanie się takich rzeczy jest niebezpieczne, bo może prowadzić do błędów i tak się właśnie dzieje czasem w kreatorze. Takie miejsca powodują, że czasem typowany DataSet po prostu się psuje. Dlatego proste poprawianie bugów w kodzie DataSeta tutaj nie wystarczy, trzeba by było zmienić sposób w jaki kreator serializuje ustawienia do kodu. Wtedy szanse na błędy podczas tego zamkniętego cyklu kod-kreator byłyby mniejsze.

Problem jest taki, że każdy taki błąd zniechęca użytkowników do używania kreatora i tak jest też w tym przypadku. Coś podobnego można było zaobserwować gdy rozwijał się rynek programów WYSIWYG do tworzenia stron internetowych. Po latach uciążliwości z FrontPage, który generował wielkie, nieoptymalne, nieczytelne i często wadliwe pliki HTML (wtedy jescze), pojawił się konkurencyjny program Dreamweaver, który generował piękny kod HTML. W miejscu gdzie była spacja była spacja a nie dziesiątki niepotrzebnych znaczników z których każdy zwiększał ryzyko tego, że strona nie będzie chodzić poprawnie nigdzie poza FrontPage. Dreamweaver pokazał, że można generować i interpretować przejrzysty kod i że to jest droga do osiągnięcia sukcesu z kreatorami. Szkoda, że w tym przypadku team Visual Studio nie uczy się z innych obszarów działalności swojej firmy.

O tym problemie pisałem już przeszło 2 miesiące temu na Connect i bez odzewu. Możliwe, że już wystarczająco dużo osób zniechęciło się do tego designera, żeby team VS planował go dalej rozwijać.

Jak zapobiec błędom

Tutaj pojawia się problem. Ponieważ jeżeli kreator DataSeta popełni błąd tak że nie będzie się uruchamiał znowu to jedyne co zostaje to otworzyć ręcznie kod i znaleźć błąd i go usunąć. Niestety w tym momencie twórcy DataSeta typowanego nie dają żadnej pomocy. Zakładają przy tym pewnie, że wygenerowany kod będzie działający w 100%. Niestety tak nie jest i czasem dzieje się tak, że za którymś razem kreator po prostu już się nie otworzy. Na przykład przez taki błąd:

Column requires a valid DataType.

Oprócz informacji o tym, że któraś kolumna nie ma poprawnego typu nic więcej się nie dowiemy. Podpięcie drugiego Visuala tutaj nic nie da. Tak samo Stack Trace, który wcale nie zaskakująco wygląda tak:

at System.Data.DataColumn.set_DataType(Type value)
at System.Data.XSDSchema.SetProperties(Object instance, XmlAttribute[] attrs)
at System.Data.XSDSchema.HandleElementColumn(XmlSchemaElement elem, DataTable table, Boolean isBase)
at System.Data.XSDSchema.HandleParticle(XmlSchemaParticle pt, DataTable table, ArrayList tableChildren, Boolean isBase)
at System.Data.XSDSchema.HandleComplexType(XmlSchemaComplexType ct, DataTable table, ArrayList tableChildren, Boolean isNillable)
at System.Data.XSDSchema.InstantiateTable(XmlSchemaElement node, XmlSchemaComplexType typeNode, Boolean isRef)

(dalsza część pominięta dla przejrzystości postu)

Nie pozostaje w tym momencie nic jak szukanie na oślep w kodzie wygenerowanym przez kreator. A wystarczyłoby dodać odpowiednią informację jakiej kolumny dotyczy błąd. Wtedy znalezienie problemu na własną rękę byłoby możliwe. No bo nie oszukujmy się, że przy zastosowanej architekturze kodu ręczne naprawy są nieuniknione.

Podsumowanie

Podsumowując staraj się używać kreatora DataSeta z głową i nie zawsze. Jest to wygodne narzędzie - owszem. Ale tylko do pewnego momentu. Zwłaszcza nie polecam korzystania z DataSeta bez robienia kopii zapasowych albo posiadania kontroli wersji. A jaka będzie przyszłość tego narzędzia? Czy będzie poprawiane? Nie sądzę i raczej myślę, że w końcu zostanie wprowadzony zamiennik, który będzie to wszystko realizwał zupełnie inaczej, bo na podstawie tego co jest nie da się dużo naprawić w zachowaniach kreatora DataSeta. Pozostaje mieć nadzieję, że team VS uczy się na błędach i nie popełni tych samych w przypadku kolejnych kreatorów, które są nieuniknione w środowiskach RAD.


Microsoft naprawił buga!

Tipsy   |    3 czerwiec 2008 10:06

W trakcie używania DataSet designera natrafiłem na pewnego buga, który istniał od wersji 2005 Visual Studio. Wygląda on w ten sposób, że dodając do tabeli kolumnę z ustawionym Expression nie można rekonfigurować ponownie DataSeta.

Przeczytaj całość >>

 


5 zasad użytkowości (ang. usability)

Tipsy   |    1 maj 2008 11:17

Użytkowość, czyli po angielsku usability. W tym wpisie przedstawię krótko 5 zasad użyteczności, których przestrzeganie może ulepszyć Twoją stronę internetową. Spróbuję w tym zestawieniu nawiązać też trochę do aplikacji, ponieważ zasady te można przenieść na projektowanie programów komputerowych.

Jeżeli uznasz poniższe obrazki za przydatne na swojej stronie, skorzystaj z nich, ale nie zapomnij dodać linku zwrotnego do Polishwords (ps. nie hotlinkuj - serwer by tego nie wytrzymał!)

Przeczytaj całość >>


Standardowe snipety w Visual Studio 2008

Tipsy   |    9 marzec 2008 7:33

Snipety to kawałki kodu, które można wygenerować wpisując słowo kluczowe i naciskając dwa razy przycisk tabulatora [TAB]. Po wygenerowaniu kodu niektóre jego fragmenty są zielone, wpisanie w te pola wartości powoduje aktualizację innych pól kodu wygenerowanego za pomocą snipeta (patrz np. snippety właściwości).

Poniżej przedstawiam listę standardowych snipetów dostępnych w Visual Studio 2008 Express i kod jaki generują. Przeczytaj całość >>


Narzędzia programistyczne cz. 2

Tipsy   |    18 styczeń 2008 3:27

W drugiej i ostatniej częsci prezentuję zestaw programów (także w wiekszości darmowych), które mogą, ale nie muszą być przydatne dla programisty. Zależnie od tego czym się zajmuje. Przeczytaj całość >>


Narzędzia programistyczne cz. 1

Tipsy   |    16 styczeń 2008 1:05

Wymienione niżej narzędzia i programy przydały mi się, albo przydają. Część z nich jest za darmo, a jeżeli nie to są bardzo tanie. Polecam do zapoznania się z nimi i dzielenia się spostrzeżeniami. Przeczytaj całość >>


Problem z przeciążaniem Text

Tipsy   |    26 listopad 2007 10:30

Powiedzmy, że chcę mieć nową kontrolkę. Dziedziczę z np. klasy Panel i piszę

[Kod C#]
[Browsable(true)]
/// <summary>
/// Text to display
/// </summary>
public new string Text
{
get { return _text; }
set { if (_text != value) { _text = value; Invalidate(); } }
}

wrzucam z Toolbara tą nową kontrolkę z właściwością Text. Można teraz edytować pole Text we właściwościach kontrolki w czasie projektowania.
Ale właściwość Text nie jest serializowana do InitializeComponents.

Jak się okazało, ludzie radzą, aby dodać jeszcze jeden atrybut:

[Kod C#]
[Browsable(true)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
/// <summary>
/// Text to display
/// </summary>
public new string Text
{
get { return _text; }
set { if (_text != value) { _text = value; Invalidate(); } }
}

Atrybut DesignerSerializationVisibility określa jak ma być serializowana właściwość. Wydawałoby się, że teraz właściwość powinna działać dobrze.
Ale niestety tak nie jest. Zachowanie jest nadal to samo: można ustawić właściwość, ale nie jest ona zapisywana do InitializeComponents czyli za pierwszym
razem kiedy Visual Studio będzie serializowało właściwości klasy, właściwość Text zostanie porzucona.

Kilka wątków na ten temat na różnych forach albo kończy się rozwiązaniem powyższym, które wygląda na to, że czasem wystarczy (możliwe, że
został dodany inny atrybut, który powoduje poprawne zachowanie tej właściwości). Moje próby znalezenia takiej właściwości zakończyły się
niestety niepowodzeniem.

Porzuciłem już nadzieję na to, żeby zachować normalne nazewnictwo tej właściwości, dodałem jeszcze tylko z ciekawości jeden atrybut:

[Kod C#]
[Browsable(true)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
[DefaultValue("")]
/// <summary>
/// Text to display
/// </summary>
public new string Text
{
get { return _text; }
set { if (_text != value) { _text = value; Invalidate(); } }
}

No i okazało się, że dodanie atrybutu DefaultValue powoduje, że serializacja działa już normalnie.
Może ktoś jest w stanie wyjaśnić, dlaczego po dodaniu DefaultValue serializacja  w tym bardzo szczególnym wypadku działa dobrze?


Jak szukać odpowiedzi na forum CodeGuru

Tipsy   |    21 październik 2007 11:42

Wyszukiwarka forum działa jak działa. No i w związku z tym niektórzy wolą zadać pytanie na forum ponownie zamiast próbować szukać odpowiedzi, która gdzieś tam jest.

Może na ITCore.pl będzie wyszukiwarka działać lepiej, ale póki co trzeba sobie radzić. Jeżeli szukasz odpowiedzi na swoje pytanie na forum, jest duża szansa na to, że ktoś już je zadał i ktoś odpowiedział. Zatem jak szukać odpowiedzi na pytania na codeguru? Prosto:

Wchodzimy na google.pl, wpisujemy swoje pytanie spacja codeguru.pl
np. richtextbox kolor codeguru.pl i szukamy.

Pojawią się wyniki.
Przy drugim wpisie z codeguru pojawi się szary napis:

[ Więcej wyników z www.codeguru.pl ]

Klikamy w niego i w ten oto sposób uzyskamy wyniki na stronie CodeGuru.
Można to też zrobić od razu wpisując: site:www.codeguru.pl zamiast codeguru.pl


Splitter bug we Frameworku 1.1

Tipsy   |    1 lipiec 2007 11:46

Jezeli na formatce umiesci się powiedzmy: dwa panele rozdzielone splitterem (jeden z dokowaniem do lewej, a drugi na fill). A nad nimi przycisk, to czasem przesuwanie splittera spowoduje rozszerzenie / zwezenie przycisku. Trzeba na to uwazac.


Podstawy - Parsowanie

Tipsy   |    1 lipiec 2007 11:45

Podczas nauki nowego języka, środowiska można utkąć na jakimś drobnym szczególe. Później jak już przebrnie się przez to, wszystkie te kruczki i triki stają się ważną częścią wiedzy programistycznej. Swego czasu notowałem rzeczy, które jakoś przykuły moją uwagę w C# i .NET. Wydają się one czasem oczywiste, ale może się komuś przydadzą. Zapraszam do lektury tego tematu.

A teraz do tematu. Parsowanie czegokolwiek do stringa można osiągnąć za pomocą metody ToString():

int a = 5;
string b = a.ToString();

można napisać nawet:

string b = 5.ToString();

natomiast ze stringa na liczbę:

int c = int.Parse(b);

Oprócz tego istnieje jeszcze klasa Convert w przestrzeni nazw System



« Starsze posty · Nowsze posty »

Programming Blogs - BlogCatalog Blog Directory
WordPress, Pool Theme - Borja Fernandez - mod