Projektowanie, Programowanie, Codzienność – BeniaminZaborski.com

30 listopada 2009

Error Provider

Filed under: How Can I Take ... — Tagi: , , , — Beniamin Zaborski @ 19:51

W poniższym artykule chciałbym poruszyć kwestię związana z walidacją obiektów. Oczywiste jest, że walidacja jest niezbędnym elementem każdej dobrze zaprojektowanej aplikacji biznesowej. Jednak nie sam sposób walidowania obiektów jest przewodnim tematem tego artykułu. Chciałbym przedstawić pewien dość oczywisty sposób informowania użytkownika o błędach. Do napisania tego artykułu zainspirowało mnie pytanie mojego znajomego … właśnie na podobny temat. Okazuje się, że nie wszystko jest tak oczywiste jak sądzimy. Na wstępie chciałbym zaznaczyć, iż artykuł dotyczy aplikacji Window Forms.
Większość z nas pewnie widziała w aplikacjach ASP.NET ikonki przy polach edycyjnych informujące o wymagalności danego pola lub zbyt dużej ilość znaków – ogólnie o niespełnionych regułach walidacyjnych. Podobny efekt możemy uzyskać w aplikacjach Window Forms. Tu kolejna dobra wiadomość – za pomocą standardowych kontrolek i to w łatwy sposób. Odpowiedzialna za to jest kontrolka o nazwie ErrorProvider. Poniższy zrzut ekranu przedstawia wspomnianą kontrolkę w akcji.

Niespełniona reguła walidacyjna na wskazanym property obiektu powoduje wyświetlenie ikony przy odpowiedniej kontrolce. Szczegóły niespełnionych reguł są wyświetlane w tooltipie. Wiemy zatem co chcemy uzyskać, teraz pytanie jak?

Zacznę może od samego obiektu który podlega walidacji. W aplikacjach biznesowych jest to z reguły jakiś DataTransferObject. Kontrolka ErrorProvider potrzebuje, aby dostarczyć do niej informacji o niespełnionych regułach walidacyjnych. Ta informacja to zwykły string czyli komunikat który ma się wyświetlić użytkownikowi. Odpowiedzialny za to jest interfejs IDataErrorInfo.

Interfejs jest dość prosty bo posiada tylko dwie właściwości:

  • string Error { get; }
  • string Item[string columnName] { get; }

Jak się okazuje dalej implementacja tego interfejsu w naszej klasie DataTransferObject również nie jest trudna. Zakładam, że nasz DataTransferObject posiada już sam mechanizm walidowania np. wykorzystujący całkiem poręczny Application Validation Block pochodzący z Enterprise Library. Nie pozostaje teraz nic innego jak zaimplementować IDataErrorInfo w naszej klasie DataTransferObject.

public abstract class DataTransferObject : IDataTransferObject, IValidatable, IDataErrorInfo
{
// pominięta część kodu klasy nieistotna w omawianym kontekście
string IDataErrorInfo.Error
{
get
{
if (!IsValid)
return BrokenValidationRules.GetDescriptionBrokenRules();
else
return String.Empty;
}
}

string IDataErrorInfo.this[string columnName]
{
get
{
string result = string.Empty;
if (!IsValid)
{
IBrokenValidationRule brokenRule = BrokenValidationRules.GetBrokenRule(columnName);
if (brokenRule != null)
result = brokenRule.Description;
}
return result;
}
}
}

Właściwość Error zwraca nam listę komunikatów o niespełnionych regułach na wskazanym obiekcie. Indekser zwraca nam natomiast listę komunikatów niespełnionych reguł dla wskazanej właściwości obiektu. Parametr columnName określa nazwę właściwości obiektu.

Przedstawiona klasa nie posiada pełnej implementacji, a jedynie te elementy które są istotne w omawianym kontekście. Oczywiste jest, że klasa musi mieć w tym wypadku kolekcję niespełnionych reguł walidacyjnych, która jest odpowiednio uzupełniana podczas wywołania walidacji. Na tej kolekcji zresztą bazują zaimplementowane właściwości z interfejsu IDataErrorInfo. W ten sposób chciałem pokazać, iż sam mechanizm walidacji jest zupełnie niezależny od implementacji IDataErrorInfo i po wprowadzeniu kilku własnych interfejsów może być łatwo wymieniany na inny.

Teraz możemy zająć się już interfejsem użytkownika. Pierwsze co należy zrobić to położyć na widoku kontrolkę ErrorProvider, np. przeciągając z toolboxa. Kolejnym krokiem jest zbindowanie naszego obiektu (dziedziczącego z DataTransferObject) z kontrolkami i przypisanie tego obiektu jako źródła danych do właściwości DataSource kontrolki ErrorProvider. Jeśli dodajemy kontrolkę ErrorProvider nie poprzez designera warto pamiętać o ustawieniu właściwości ContainerControl na odpowiednią formę/kontrolkę.

Sama kontrolka ErrorProvider posiada jeszcze kilka ciekawych właściwości jak: BlinkRate (częstotliwość pulsowania), BlinkStyle (kiedy ma pulsować ikonka) oraz Icon.

Powodzenia w implementacji wizualizacji wyników walidacji we własnej aplikacji w ten jakże prosty sposób.

Reklamy

16 listopada 2009

O2O Mapping

Filed under: How Can I Take ... — Tagi: , , , — Beniamin Zaborski @ 23:46

Zainspirowany jednym z postów na blogu Macieja Aniserowicza, postanowiłem bliżej przyjrzeć się zagadnieniu mapowania object-to-object. Wydaje się to być idealny lek na „głupie” mapowanie property poprzez przepisywanie każdego po kolei. A i owszem jest. Szczególnie przydatne przy mapowaniu obiektów domeny do obiektów DTO. Nie wiem ilu z Was robi/robiło to właśnie w ten tzw. „głupi” sposób, ale ja muszę przyznać że … mhm. Przykre, jak czasem nie zauważamy tak prostych rozwiązań pozwalających zaoszczędzić sporo czasu i zbędnych linii kodu. AutoMapper bo to o nim właściwie pisał Maciej to porządne narzędzie, które powinno zainteresować każdego programistę .NET. Na pierwszy rzut oka wygląda zachwycająco, ale to chyba bardziej idea tego typu mapowania jest bardziej zachwycająca niż sam w sobie produkt. Narzekam? Nie w sumie to nie. AutoMapper jest ok, ale niektóre rozwiązania w nim mnie nie odpowiadały. Sprawdziłem inne narzędzia tego typu jak choćby Glue i … ja potrzebowałem czegoś naprawdę trywialnego, bo sama idea jest trywialna. Długo nie myśląc stwierdziłem, że te kilka klas to napisze sam w taki sposób, aby mnie odpowiadały w 100%. I stało się – od pomysłu, po implementację. Czego potrzebowałem? Niczego więcej jak mapowania ad-hoc z możliwością zdefiniowania własnych reguł ustawiania wartości właściwości w obiekcie docelowym.
Oto przykład mówiący wszystko za siebie:


User user = new User();
user.Id = Guid.NewGuid();
user.Login = „Jan”;
user.Password = „Secret”;
user.FirstName = „Jan”;
user.LastName = „Kowalski”;
user.RoleName = „Admins”;

var mapper = new ObjectMapper<User, UserDTO>();
mapper
  .AddRule(„FullName”, s => s.FirstName + ” ” + s.LastName)
  .AddRule(„Login”, s => s.Login)
  .AddRule(„Password”, s=>SecurityUtils.Md5(s.Password));
mapper.Ignore(„RoleName”);
UserDTO userDTO = mapper.Map(user);

Kod źródłowy do wglądu zamieszczam tutaj. Tak na marginesie cały pomysł jak i jego implementacja powstała podczas jednego z programów rozrywkowych z tępym żartem granego w pewnej stacji na trzy litery w poniedziałkowe wieczory. A więc jak sądzę wszystko to trwało nie więcej jak 60 minut (z reklamami) – tłumaczę się na zaś jakby jednak coś nie działało;).

Blog na WordPress.com.