Goodbye Zine. Powrót na stare śmieci.

Po kilkumiesięcznych (sic!) rozważaniach postanowiłem, że uciekam z Zine’a. Od tej chwili wracam na stare śmieci. Reaktywuję możliwość dodawania komentarzy na swoim prywatnym blogu, nowe posty będą pojawiać się również tylko tam. Zainteresowanych zachęcam do regularnego odwiedzania. Godne uwagi adresy:

Nadal będzie ciekawie (o ile było do tej pory) :).
Jeżeli zauważysz błąd w działaniu bloga (wprowadzałem tam ostatnio kilka zmian) to daj mi, proszę, znać (kontakt).

Tak więc, do – mam nadzieję – przeczytania pod moim własnym adresem!

opublikowano 13 lipca 09 06:18 przez Procent | 5 komentarzy   
Alternatywa dla Convert.ChangeType()

Drugi raz w ciągu kilku dni przytrafiły mi się kłopoty podczas wykorzystania metody Convert.ChangeType(). Scenariusz jest bardzo prostu: mam wartość pobraną skądś-tam (baza danych, http request czy cokolwiek innego) reprezentującą znany mi typ, jednak przechowywaną w postaci stringa. Wszystko śmigało jak trzeba dopóki traktowałem w ten sposób zwykłe liczby i daty. Jakiś czas temu wpadł mi tam Guid, co skończyło się wyjątkiem InvalidCastException. Teraz z kolei to samo przytrafiło się dla TimeSpan. To samo zresztą tyczy się "typów nullowalnych".

Wystarczyło zajrzeć Reflectorem w trzewia .NETa, aby poznać tego przyczynę. Wspomniana metoda potrafi sobie poradzić tylko z kilkoma na sztywno zdefiniowanymi typami:

Kilka kliknięć dzieli nas od podejrzenia jakie to są typy; inicjalizacja tablicy je zawierającej odbywa się w statycznym konstruktorze klasy Convert:

Cóż nam pozostaje? Najpierw pomyślałem, że jedyne wyjście to rozszerzenie tej metody i stworzenie takiego oto monstera oraz rozwijanie go w razie wystąpienia kolejnych nieprzewidzianych typów:

  1:  public static object ConvertEx(object value, Type conversionType)
  2:  {
  3:  	string str = value.ToString();
  4:  
  5:  	if (conversionType == typeof(Guid))
  6:  		return new Guid(str);
  7:  	if (conversionType == typeof (TimeSpan))
  8:  		return TimeSpan.Parse(str);
  9:  
 10:  	return Convert.ChangeType(value, conversionType);
 11:  }

Chwilę potem jednak dotarło do mnie, że przecież te typy JAKOŚ muszą być dynamicznie wewnątrz .NET tworzone z napisowej reprezentacji. Chociażby zwykłe ustawienia aplikacji w pliku .config mogą mieć takie typy i klasy odpowiedzialne za parsowanie konfiguracji radzą sobie z nimi doskonale. Strzał Reflectorem w klasę ConfigurationElement, która przecież ma tą funkcjonalność, okazał się dobrym początkiem poszukiwań. Oszczędzę omawiania całej drogi, w tym przypadku ważny jest sam rezultat. Rozwiązaniem okazała się klasa TypeDescriptor, z wykorzystaniem której działające rozwiązanie wygląda tak (działa dla TimeSpan, dla Guidów, dla typów nullowalnych...):

  1:  TimeSpan result = (TimeSpan)TypeDescriptor.GetConverter(typeof (TimeSpan)).ConvertFromInvariantString(str);

Jeden problem z głowy. Oczywiście od razu przychodzi na myśl napisanie fajnej metody generycznej, ale to już każdy może sobie sam zaimplementować.

opublikowano 09 lipca 09 06:43 przez Procent | 5 komentarzy   
Zarejestrowano w kategorii: ,
Pantoflowo-aspektowe sko_dev_jarzenie

Niedawno podczas koleżeńskiej konserwacji wypłynął nam temat pantoflarstwa, czyli bezwarunkowej podległości osobnika płci męskiej pod swego żeńskiego w życiu partnera. Swoją Panią.
I nieuchronnie już wówczas do głowy napłynęła mi myśl, jak taką sytuację można doskonale zamodelować przy użyciu PostSharp – godnego uwagi darmowego narzędzia do programowania aspektowego w .NET. Przysiadłem na chwilę, ściągnąłem Post#, zaimplementowałem minidemko i je niniejszym prezentuję:

Zaczynamy od naszego MĘŻCZYZNY, który robić może co chce i dumny jest z tego że ŁO-HO-HO:

  1:  public class Man
  2:  {
  3:  	public void DrinkBeer()
  4:  	{
  5:  		Console.WriteLine("Drinking beer, yummy!");
  6:  	}
  7:  
  8:  	public void BuyFlowers()
  9:  	{
 10:  		Console.WriteLine("Giving flowers for my valentine...");
 11:  	}
 12:  
 13:  	public void WatchTv()
 14:  	{
 15:  		Console.WriteLine("Relaxing with tv.");
 16:  	}
 17:  }

Stwórzmy takiego kola i zobaczmy jak mu życie płynie:

  1:  static void Main(string[] args)
  2:  {
  3:  	Man freeMan = new Man();
  4:  
  5:  	freeMan.DrinkBeer();
  6:  	Console.WriteLine("------------------");
  7:  	freeMan.BuyFlowers();
  8:  	Console.WriteLine("------------------");
  9:  	freeMan.WatchTv();
 10:  }

Skutek:

Taa...
Dodajmy jednak łyżkę dziegciu do tej beczki miodu, przerysowując nieco rzeczywistość przy pomocy postsharpowego atrybutu:

  1:  [Serializable, MulticastAttributeUsage(MulticastTargets.Method)]
  2:  public class PantofelAttribute : OnMethodBoundaryAspect
  3:  {
  4:  	private readonly Girlfriend _girlfriend = new Girlfriend();
  5:  
  6:  	public override void OnEntry(MethodExecutionEventArgs eventArgs)
  7:  	{
  8:  		var actionName = eventArgs.Method.Name;
  9:  		try
 10:  		{
 11:  			_girlfriend.ConsiderPermission(actionName);
 12:  		}
 13:  		catch (Exception exc)
 14:  		{
 15:  			Console.WriteLine(
 16:  @"Sorry, permission for {0} was not granted.
 17:  {1} was thrown with the following message from your dearest beloveth:
 18:  ""{2}""",
 19:  actionName, exc.GetType().Name, exc.Message);
 20:
 21:  			eventArgs.FlowBehavior = FlowBehavior.Return;
 22:  		}
 23:  	}
 24:  }

Jak to bywa – przed wykonaniem jakiejkolwiek akcji będzie teraz biedak musiał o pozwolenie swojej białogłowy się starać. A że w takiej sytuacji logika zbyt skomplikowana nie jest, i ją można zamodelować bez większego kłopotu:

  1:  [Serializable]
  2:  public class Girlfriend
  3:  {
  4:  	public void ConsiderPermission(string actionName)
  5:  	{
  6:  		// no way!
  7:  		if (actionName.Contains("Beer"))
  8:  		{
  9:  			throw new Foch("You've got to be kidding me!");
 10:  		}
 11:  
 12:  		// sure, go ahead
 13:  		if (actionName.Contains("Flowers"))
 14:  			return;
 15:  
 16:  		// default, rather strict, permission-rejecting rule
 17:  		throw new IDontThinkSoException("Not really sure, but better not.");
 18:  	}
 19:  }

Można zauważyć, że do sterowania przepływem użyłem w tym przypadku wyjątków, co nie jest praktyką zalecaną. Doświadczeni przyznać jednak mogą rację, że zwykłe zwrócenie true/false mogłoby nie oddać realizmu sytuacji w wystarczającym stopniu :).
No dobra, zmodyfikujmy lekko naszego cwaniaczka:

  1:  [Pantofel]
  2:  public class Man
  3:  {

i zobaczmy jak jego poczynania będą wyglądały teraz:

I cóż, nic dodać, nic ująć...

A jak to jest czytelniku u ciebie? Nałożono ci ów atrybut z bezlitosną konsekwencją, czy też może zmodyfikowałeś go lekko przekazując w konstruktorze enum ObedienceLevel starając się w miarę możliwości ustawiać jakąś akceptowalną wartość?:)

opublikowano 06 lipca 09 06:21 przez Procent | 5 komentarzy   
Zarejestrowano w kategorii: ,
ASP.NET MVC - Reflector vs DropDownList

Kolejny odcinek o Reflectorze i MVC, tym razem opowieść spod znaku "przecież to NIE MOŻE nie działać!". Oczywiście bezlitosna rzeczywistość twierdziła inaczej i jak zwykle w takich wypadkach bywa – to ona była górą. Zobaczmy cóż takiego się stało...

Jak zwykle dla uproszczenia stworzę bezsensowny projekcik specjalnie pod ten scenariusz, aby każdy mógł w prosty sposób odtworzyć cały proces. Praktyki stosowane podczas implementacji wcale nie muszą być godne naśladowania, po prostu chodzi o jak najprostszą demonstrację problemu.
Zadaniem moim było wyświetlenie rozwijanej listy elementów oraz zaznaczenie tego, którego ID zostało przekazane do akcji kontrolera. Wydaje się, że nic prostszego, zatem zróbmy to:
1) stwórzmy model, który pomoże nam w silnie typowany sposób przekazać niezbędne dane do widoku:

  1:  public class HomeIndexModel
  2:  {
  3:  	public int? SelectedUserId { get; set; }
  4:  	public ICollection<User> Users { get; set; }
  5:  
  6:  	public HomeIndexModel(int? selectedUserId, ICollection<User> users)
  7:  	{
  8:  		SelectedUserId = selectedUserId;
  9:  		Users = users;
 10:  	}
 11:  }

2) zmodyfikujmy akcję Home/Index tak, aby była w stanie przyjąć odpowiedni parametr i przekazała dane do widoku:

  1:  public class HomeController : Controller
  2:  {
  3:  	public ActionResult Index(int? id)
  4:  	{
  5:  		var users = new List<User> {new User(1, "User1"), new User(2, "User2")};
  6:  		ViewData.Model = new HomeIndexModel(id, users);
  7:  
  8:  		return View();
  9:  	}
 10:  }

3) klasa User jest właściwie oczywista, ale dla pełnej jawności oto ona:

  1:  public class User
  2:  {
  3:  	public int Id { get; set; }
  4:  	public string Name { get; set; }
  5:  
  6:  	public User(int id, string name)
  7:  	{
  8:  		Id = id;
  9:  		Name = name;
 10:  	}
 11:  }

4) wykorzystując projekt MVC Contrib tworzymy listę w bardzo wygodny sposób:

  1:  Users: <%= Html.DropDownList("users", Model.Users.ToSelectList(x => x.Id, x => x.Name, x => x.Id == Model.SelectedUserId)) %>

Dla niezorientowanych: metoda ToSelectList tworzy gotowe do wykorzystania elementy listy przy pomocy trzech wyrażeń lambda. Pierwsze służy do pobrania wartości elementu (value), drugie – wyświetlanej wartości (text), trzecie z kolei definiuje czy dany element ma być aktualnie wybrany czy też nie. Prosto i przyjemnie.

OKej, skoro mamy takie rozwiązanie, to je przetestujmy wchodząc na stronę Home/Index/2. I zonk:

Po kilku minutach zdumienia i tarcia brody (jak to inteligentnie musi wyglądać!) tknięty durnym impulsem zmieniłem przekazaną w pierwszym parametrze docelową nazwę listy z "users" na "x", i... zadziałało:

Na czas, że tak to brzydko określę, "płatny" to w zupełności wystarczyło. Jednak w czasie "wolnym" nie dawało mi to spokoju. Odpaliłem więc wieczorem Reflectora, załadowałem dllkę System.Web.Mvc i zabrałem się do "inwestygacji":

1) Wszystko zaczyna się w metodzie DropDownList, zatem do niej udamy się najsampierw:

2) Po kilku klikach i szybkich nawigacjach docieramy do sedna generacji tagu <select>, czyli prywatnej metody System.Web.Mvc.Html.SelectExtensions.SelectInternal. Od razu widać, że to jakaś skomplikowana potwora. Wiemy jednak jakie przekazujemy parametry, zatem dość łatwo możemy zidentyfikować potencjalnie interesujące źródła tego dziwnego zachowania:

Trochę ten rysunek pomazałem, ale już tłumaczę. Fragmenty 1, 2 oraz 3 wyeliminujemy: pierwsze dwa odpadają z powodu naszych parametrów, a trzeci dotyczy już plucia napisami, gdzie z pewnością wszystko jest w porządku (w końcu po małej modyfikacji strona działa jak trzeba). Zaznaczony fragment nr 4 wydaje się niezmiernie interesujący: ktoś zmienia wartość właściwości Selected dla elementów, które my tak pieczołowicie przygotowaliśmy w naszym aspx! Ma to jakiś związek ze zmienną set, która z kolei tworzona jest z source otrzymanego z obj2 uzyskanego przez wywołanie metody GetModelStateValue lub Eval. Nie jest źle, na pewno zbliżamy się do celu niczym łowczy do łani.
3) Klikamy losowo w jedną z nich, mi akurat trafiło się GetModelStateValue. Rzut oka wystarczył jednak, aby upewnić się, że to nie tu tkwi problem:

4) Przechodzimy zatem do Eval, i okazuje się że tu jest pies pogrzebany:

Dochodzimy do miejsca, gdzie pobierana jest wartość właściwości MODELU (czyli instancji naszej klasy HomeIndexModel) o nazwie odpowiadającej nazwie listy, do tego case-insensitive! W tym przypadku będzie to kolekcja użytkowników o nazwie Users typu ICollection<User>. Oznacza to ni mniej ni więcej, że zgodnie z tą logiką (z fragmentu "4!" z obrazka pod punktem 3) zaznaczone zostaną elementy spełniające warunek: item.Value == <anyUser>.ToString(). Łatwo się domyślić, że nie zostanie zaznaczone NIC, tak jak dało się zaobserwować.

Puenta: tworząc listę rozwijaną przy pomocy metody Html.DropDown i nadając jej nazwę występującą we właściwościach modelu możesz zostać zaskoczony niedziałaniem "serwerowego" zaznaczania elementów na liście. Ja rozwiązałem to zmieniając nazwę właściwości w modelu, jednak ten sam skutek odniosłaby zmiana nazwy listy.


Uff, jak teraz na to patrzę to dochodzę do wniosku że trochę przesadziłem... bo i co to kogo obchodzi?? Jeżeli dotarłeś aż do tego miejsca to gratuluję zawziętości, bo to chyba najnudniejszy post w mojej karierze:).
Bottom-line: reflector rulez, gdyż pozwala zaspokoić ciekawość dociekliwego programisty. Tutaj z pewnością prościej byłoby ściągnąć kod źródłowy MVC, ale kto by się spodziewał że zagrzebiemy się tak głęboko...

opublikowano 02 lipca 09 06:19 przez Procent | 1 komentarzy   
Zarejestrowano w kategorii: , , ,
Visual Studio 2010: Zwijanie regionów

Regiony w Visual Studio to byt bardzo przydatny. Biorąc je za pewnik – bardzo łatwo lekceważyć ich wkład w organizację i przejrzystość kodu. Szczególnie mocno docenia się je, gdy przyjdzie korzystać z IDE które ich nie oferuje. Kilka lat temu musiałem napisać projekt w Javie i, nie interesując się zbytnio milionem dodatków do Eclipse, po prostu odpaliłem środowisko i zacząłem pisać. Staram się nie nadużywać regionów, bo często są one oznaką złego designu aplikacji, ale wtedy przekonałem się że ich całkowity brak potrafi dać się we znaki.
Tym bardziej bolałem nad dość marnymi możliwościami zarządzania regionami w Visual Studio. Resharper jako tako przychodzi z pomocą, dostępne są też darmowe narzędzia stworzone w tym celu (jak chociażby Regionarate), ale i tak najczęściej wykorzystywaną funkcjonalnością związaną z regionami jest ich... zwijanie i rozwijanie! Niestety w VS 2008 i wcześniejszych w celu zwinięcia regionu trzeba było kliknąć w magiczny mikroskopijny krzyżyk bądź, mając kursor na linii regionu, wcisnąć megaprzydatną kombinację klawiszy ctrl+m, ctrl+m. Trochę trwało zanim dowiedziałem się o jej istnieniu i zdecydowanie uprościła mi życie:).

Ale do rzeczy. W nowym Visualu zwijanie regionów będzie dużo prostsze. Wystarczy bowiem najechać kursorem na brzeg marginesu znajdującego się w obrębie regionu, a cały region zostanie podświetlony. Dwukllik z kolei spowoduje zwinięcie regionu, bez celowania w durny krzyżyk:

To samo tyczy się wszystkich bloków kodu (jak np. metody), nie tylko regionów. Mała rzecz a cieszy.

opublikowano 30 czerwca 09 06:23 przez Procent | 4 komentarzy   
Zarejestrowano w kategorii:
Strongly-typed DisplayValue i DisplayMember / DataValueField i DataTextField

Ileż to razy zmuszeni jesteśmy pisać kod temu podobny:

  1:  list.ValueMember = "Id";
  2:  list.DisplayMember = "Name";

Na CodeGuru niejednokrotnie pytano o jakiś sposób na rozwiązanie tego problemu. Podawanie stringów jest ZŁE, niewygodne i bardzo podatne na błędy wszelakie. Zmiana nazwy właściwości rozwala UI, refactoring bez dodatkowych narzędzi jak Resharper potrafi napsuć sporo krwi (a i z pomocą R# wcale przyjemny nie jest)... Syf, kiła i mogiła.
Postanowiłem poeksperymentować w tym obszarze i oto co udało mi się osiągnąć. Mając klasę User:

  1:  public class User
  2:  {
  3:  	public int Id { get; set; }
  4:  	public string Name { get; set; }
  5:  }

Możemy zrobić coś takiego:

  1:  list.ValueMember<User>(u => u.Id);
  2:  list.DisplayMember<User>(u => u.Name);

Fajnie, prawda? Zobaczmy jak taka magia wygląda pod spodem.
Wykonanie tych instrukcji bez podawania nigdzie jawnie żadnego stringa możliwe jest dzięki LINQ, a dokładniej klasie Expression. O niej i wszystkim co się z nią wiąże napiszę być może kiedy indziej (a nawet jeśli nie to zachęcam do samodzielnego zapoznania się z tematem), dzisiaj natomiast ograniczę się do pokazania metod zademonstrowanych powyżej:

  1:  public static class ListControlExtensions
  2:  {
  3:  	public static void ValueMember<T>(this ListControl _this, Expression<Func<T, object>> retrieve)
  4:  	{
  5:  		_this.ValueMember = GetMemberName(retrieve);
  6:  	}
  7:  
  8:  	public static void DisplayMember<T>(this ListControl _this, Expression<Func<T, object>> retrieve)
  9:  	{
 10:  		_this.DisplayMember = GetMemberName(retrieve);
 11:  	}
 12:  
 13:  	private static string GetMemberName<T>(Expression<Func<T, object>> retrieve)
 14:  	{
 15:  		Expression body = retrieve.Body;
 16:  
 17:  		MemberExpression memberExpression;
 18:  
 19:  		// reference type -> no conversion needed to object type
 20:  		if (body is MemberExpression)
 21:  		{
 22:  			memberExpression = (MemberExpression)body;
 23:  		}
 24:  		// simple type => boxing/conversion needed to return object type
 25:  		else if (body.NodeType == ExpressionType.Convert && ((UnaryExpression)body).Operand is MemberExpression)
 26:  		{
 27:  			memberExpression = (MemberExpression)((UnaryExpression)body).Operand;
 28:  		}
 29:  		else
 30:  		{
 31:  			throw new ArgumentException(string.Format("This lambda: {0} is not supported!", retrieve.Body.ToString()));
 32:  		}
 33:  
 34:  		return BuildMemberPath(memberExpression);
 35:  	}
 36:  
 37:  	private static string BuildMemberPath(MemberExpression memberExpression)
 38:  	{
 39:  		string thisName = memberExpression.Member.Name;
 40:  
 41:  		if (memberExpression.Expression is MemberExpression)
 42:  			return BuildMemberPath((MemberExpression)memberExpression.Expression) + "." + thisName;
 43:  		return thisName;
 44:  	}
 45:  }

Jak widać kluczowym elementem jest metoda GetMemberName. Zwraca ona podane wyrażenie lambda w postaci tekstowej, w sam raz do przypisania do DisplayMember (lub, jeśli już przy tym jesteśmy, do DataTextField w kontekście aplikacji web).
Takie operacje sprawdzane w czasie kompilacji są nam udostępnione, ponieważ jako parametr tych metod przyjmujemy Expression<Func<...>> a nie samo Func<...>.  Dzięki temu cała instrukcja rozbita na części pierwsze stoi przed nami (p)otworem. Logika przedstawionych operacji jest bardzo prosta:
1) sprawdzamy, czy całe wyrażenie jest pobraniem wartości jakiejś składowej klasy –> jeśli tak, budujemy z niego tekst
2) jeśli nie, sprawdzamy, czy mamy przypadkiem do czynienia z rzutowaniem jako główną operacją wyrażenia; do mechanizmu możemy przekazać wyrażenie zwracające object (dokładniej: Func<T, object>), więc w przypadku typów prostych (jak np. int) musi zajść tzw. boxing; w tym przypadku zakładamy, że "podrzędne" do rzutowania wyrażenie jest pobraniem wartości ze składowej klasy i na nim opieramy budowanie tekstu
3) jeżeli żaden z powyższych scenariuszy nie jest spełniony, wyrzucamy wyjątek, na przykład dla wywołania

  1:  list.ValueMember<User>(u => u.Id + 666);

otrzymamy komunikat: 

Budowanie tekstu to prosta rekurencja wędrująca po wywołaniach kolejnych składowych i sklejająca poszczególne części. Dzięki temu wywołanie:

  1:  list.ValueMember<User>(u => u.UserAddress.AddressCity.Name);

wygeneruje tekst: "UserAddress.AddressCity.Name".

ALE HOLA HOLA! Oczywiście takie wywołanie na niewiele się zda w przedstawianym scenariuszu. Jak wiadomo, zarówno Windows Forms ze swoimi ValueMember i DisplayMember jak i ASP.NET z DataValueField i DataTextField nie wspierają wywoływania zagnieżdżonych właściwości. Dlatego też pomimo faktu, że mamy mechanizm potrafiący spisać dowolnie skomplikowany łańcuch zagnieżdżeń, i tak wykorzystać możemy jedynie jego najprostsze działanie w postaci u => u.Id czy u => u.Age. Ale w końcu lepsze to niż to co mieliśmy wcześniej.

Tak czy siak zachęcam to eksplorowania tej działki .NETa, ponieważ można natknąć się na naprawdę interesujące zastosowania eliminujące dręczące nas problemy.

opublikowano 18 czerwca 09 07:27 przez Procent | 3 komentarzy   
Zarejestrowano w kategorii: , , , ,
Zawód - programista. Rozmowa kwalifikacyjna.

Jakiś czas temu Gutek opublikował posta z poradami dotyczącymi pisania CV. Ja zapowiadałem z kolei, że przeleję kiedyś w zerojedynkową przestrzeń swoje refleksje na temat rozmów kwalifikacyjnych na stanowisko programisty. Kilka razy zmieniałem pracę, zatem siłą rzeczy kilka*X rozmów mam za sobą. Popędzony niedawno mailem przez jednego z Czytelników:) postanowiłem obietnice te wprowadzić w życie.
Przedstawione tu porady mogą komuś pomóc, ale jednocześnie nie należy ich traktować jak biblijnych wytycznych. Po prostu "mi tak się wydaje", i tyle. Mam jednocześnie nadzieję, że jak zwykle w takich sytuacjach pod postami pojawią się komentarze prezentujące doświadczenia i wskazówki innych osób, podnosząc wartość owych wypocin.


1) Naucz się mówić o sobie

Jeżeli przeszedłeś wstępną rekrutację, czyli Twoje CV nie poszło do kosza i otrzymałeś zaproszenie na rozmowę kwalifikacyjną - czas na dalsze przedstawienie siebie. Na rozmowę idziesz po to, aby potencjalny przyszły pracodawca mógł poznać kim jesteś. Zastanów się jakie masz cechy charakteru ważne pod kątem zawodu programisty. Bardzo ważne jest, abyś na pytania "co sprawia ci największą satysfakcję?", "dlaczego zostałeś programistą?", "kim chcesz być za X lat?" miał gotową sensowną odpowiedź. Czasami takie rzeczy wydają się oczywiste i banalne, ale przed rozmową sam zadaj sobie kilka takich pytań i zobacz, czy umiesz natychmiast na nie odpowiedzieć.
Bycie bezpośrednim tematem rozmowy nie powinno cię krępować czy sprawiać, że czujesz się niezręcznie - w końcu dokładnie po to tam jesteś. Nie jest to też pora na kokieteryjną skromność - chwal się swoimi zaletami, osiągnięciami i pokazuj swoje dobre strony. Przed rozmową postaraj się przez kilka minut mówić o sobie do lustra, zobacz czy przychodzi ci to z trudem. Poćwicz w razie potrzeby. Jeśli na pytanie "co jest twoim największym osiągnięciem?" rozdziawisz japę, wybałuszysz oczy i powiesz "eee... nie wiem" to zmarnujesz czas i swój, i rozmówcy. Nie chodzi o to żebyś był mega-pewny siebie czy zgrywał nie wiadomo kogo, tylko o to żebyś potrafił w sensowny sposób pokazać pracodawcy kim naprawdę jesteś.

2) Naucz się mówić o swoich projektach

Możesz być pewny, że na rozmowie padnie pytanie o twoją przeszłość, wykonane projekty i inne osiągnięcia. Sama rozmowa to nie jest dobry moment na przypominanie sobie szczegółów dotyczących danych okresów w twoim życiu. Szanuj czas rozmówcy i przygotuj się na taki temat. Zobacz co masz zawarte w CV i spróbuj sam sobie opowiedzieć o każdej informacji z nim zawartej.
Masz tam jakiś projekt? Postaraj się w minutę sklecić opowiastkę o nim, niech otrzyma od ciebie trochę emocji i stanie się częścią twojej historii. Po co go realizowałeś, z kim to robiłeś? Jakich narzędzi użyliście, co ci się w nim podobało a co nie? Na jakie problemy się natknęliście? Co dało ci tworzenie tego rozwiązania - może inne spojrzenie na jakiś framework, może nowe doświadczenie w pracy grupowej, może wreszcie przekonałeś się do takiego a nie innego stylu kodowania? Cokolwiek. Żaden projekt nie powinien być pustym wpisem w CV.

3) Naucz się mówić o swoich oczekiwaniach

Bądź świadom tego, czego oczekujesz od swojej przyszłej pracy - nie tylko finansowo. I mów o tym, jasno i klarownie. Postaraj się, aby rozmówca dokładnie zrozumiał co jest dla ciebie niezbędne do zawodowego zadowolenia. U mnie osobiście rzeczą absolutnie najważniejszą był całkowicie uregulowany czas pracy i wydaje mi się, że na każdej rozmowie bardzo wyraźnie dawałem to do zrozumienia. Nie akceptuję czegoś takiego jak nieoficjalne "nadgodziny", moim zdaniem równo o ustalonej godzinie powinno się wyłączać komputer i wychodzić z biura.
Mów o tym co jest dla ciebie najważniejsze i obserwuj reakcje - jeżeli któryś z kluczowych dla ciebie warunków prawdopodobnie nie będzie spełniony to szczerze radzę - poszukaj sobie innego miejsca.

4) Mów "prawdę i tylko prawdę"

Pisanie naciąganej nieprawdy w CV to idiotyzm. Mówienie tego na rozmowie prosto w oczy potencjalnemu pracodawcy to idiotyzm do kwadratu. Jeżeli coś umiesz - chwal się tym. Jeżeli czegoś nie umiesz - przyznaj się do tego. Pójście inną drogą jest po prostu głupie. W końcu idziesz na rozmowę żeby przekonać do siebie pracodawcę. A jeżeli uda ci się to przy pomocy kłamstewek i robienia z siebie fałśzywego megaboga - prawda i tak wyjdzie na jaw już na początku pracy. A wtedy... wstyd i zębów zgrzytanie.

5) Zadawaj pytania

Pamiętaj o tym, że "rozmowa kwalifikacyjna" to nie jednostronny wywiad. Ja zawsze traktowałem takie spotkanie jako egzamin obu stron - zarówno ja muszę być zaakceptowany przez firmę, jak i firma przeze mnie. Pytaj o wszystko, co ma dla ciebie jakąkolwiek wartość. Pisałem kiedyś o typach pracy - każdy przez tą samą nazwę może rozumieć trochę co innego. Mimo to każdy ma jakieś swoje wyobrażenie "pracy idealnej". O której zaczyna się i kończy praca? Którego dnia miesiąca pensja wpływa na konto? Czy zespół spotyka się po pracy? Osobę techniczną można pomęczyć bardziej. Jakie narzędzia poza Visual Studio są wykorzystywane? Czy odbywają się wewnętrzne szkolenia? Czy piszą testy jednostkowe, jeśli tak to czego do tego celu używają? Czy uczestniczą w konferencjach, czytają blogi, prenumerują techniczne pisma? W jaki sposób tworzą warstwę dostępu do danych, jakich frameworków wykorzystują w warstwie prezentacji?
Nawet jeśli pytanie wydaje ci się głupie, ale mimo to ciekawi cię odpowiedź na nie - ZADAJ JE. Po wyjściu z rozmowy powinieneś mieć wszystkie informacje potrzebne do postanowienia "czy chcę tam pracować". Po podpisaniu umowy za późno jest na odkrywanie przykrych niespodzianek w stylu "od dziś przez najbliższy miesiąc będziesz klepał procedury składowane".

6) Rozglądaj się

Zwracaj uwagę na miejsce, które niedługo może być twoim drugim domem. Czy pracujący tam ludzie wyglądają na zadowolonych czy tylko czekają na fajrant? W miarę możliwości zerknij do pokoju programistów - panuje tam grobowa cisza czy może gra muzyczka? Rozmawiają ze sobą czy każdy siedzi nieruchomo wpatrzony we własny ekran? Zobacz też jak wygląda praca w kontekście aktualnej pory dnia. Ja kiedyś byłem na rozmowie o godzinie 18 i widziałem, że praca idzie pełną parą. Od razu wiedziałem, że to nie miejsce dla mnie - o tej porze chcę być już od dawna w domu.

7) Bądź sobą

To jest chyba najważniejszy ze wszystkich punktów. Pracodawca musi wiedzieć, jaki naprawdę jesteś. W ten sposób będzie potrafił zaplanować twoją rolę w zespole, jeśli postanowi cię wybrać. Będzie także w stanie odrzucić twoją kandydaturę, jeżeli uzna że absolutnie się do tego konkretnego zespołu nie nadajesz, i będzie to z korzyścią również dla ciebie.
Jesteś ponurym mrukiem, który najchętniej nałoży słuchawki na uszy i zajmie się pracą, a wspólne obiadki nie są mu potrzebne - jak ja? Bądź taki na rozmowie! Przecież już w pierwszych dniach ewentualnej pracy wyjdzie szydło z worka. Nie staraj się na siłę prowadzić zabawnej konwersacji, jeżeli nie leży to w twojej naturze - będzie to wyglądało żałośnie, a i tak łatwo jest poznać czy zachowujesz się naturalnie, czy też jakieś aspekty twojego zachowania są wymuszone.
Pamiętaj, że oprócz kwalifikacji technicznych ważne jest również usposobienie i charakter. ALE - tak samo jak w kwestiach technicznych, tak i w personalnych powinno się być szczerym i uczciwym. Chcesz przecież, aby pracodawca wybrał CIEBIE. CIEBIE, a nie kukłę która po prostu wygląda jak ty, a powstała na potrzeby tej jednej rozmowy.

opublikowano 15 czerwca 09 06:34 przez Procent | 3 komentarzy   
Zarejestrowano w kategorii: ,
ASP.NET MVC i Unity

Nadejszła wiekopomna chwila – ASP.NET MVC już od jakiegoś czasu egzystuje jako oficjalny produkt, więc oto najwyższa pora na zapoznanie się z nim. Cierpliwie przeczekałem wszystkie ochy i achy, pokonałem chęć bycia "trendy";) i zabieram się za to dopiero teraz.

Na pierwszy ogień poszło wpasowanie w cały mechanizm kontenera Dependency Injection tak, aby kontrolery brały się właśnie z niego. Wklejenie gotowych kilku linijek kodu byłoby dość nudne, więc postanowiłem tym razem przedstawić swoje poszczególne kroki i myśli lęgnące się w mózgu podczas poznawania zupełnie nieznanego wcześniej kodu (co wcale, uprzedzam, nie oznacza, że będzie ciekawie!). Poniżej zatem wierne odzwierciedlenie mojej ścieżki od koncepcji do rozwiązania:

1) Najpierw należało zidentyfikować SKĄD te kontrolery się biorą. Bystre moje, niczym woda wiadomo gdzie, oczy, zauważyły, że każdy kontroler dziedziczy z klasy bazowej o niespodziewanej i zaskakującej nazwie... Controller. I to tyle czego oczekuję od Visual Studio, czas na przesiadkę do Reflectora. Po uruchomieniu tego cuda i wciśnięciu ctrl+o (czyli Open File) odszukałem dllkę zawierającą wszystkie tajemnice ASP.NET MVC, czyli:

(zawczasu dodałem ją do repozytorium projektu wiedząc, że mój wybór tym razem padnie na tą technologię)
2) Załadowawszy żądany zestaw (jakie megamądre stwierdzenia z mych rąk dziś płyną, a niech mnie!) zabrałem się za jego przeszukiwanie. Klawisz F3 otwierający panel wyszukiwania był w tym wypadku niezastąpiony. Niezwłocznie przystąpiłem do poszukiwań:

3) Jakże gładko poszło! Dwuklik na tym interesującym wierszu przeniósł mnie w panel nawigacji po bibliotekach i typach. Z ciekawości należało zanalizować pochodzenie klasy Controller, bardzo prawdopodobne było że w infrastrukturze ASP.NET MVC jest jakaś bardziej generalna klasa czy interfejs. A jakże:

Nie dość, że mamy ControllerBase, to jeszcze implementuje ona najwyższy w hierarchii interfejs IController. To musiało być to, byłem święcie przekonany że cokolwiek nie tworzyłoby instancji poszczególnych kontrolerów, zwracałoby je właśnie w postaci implementacji tego interfejsu. Kolejny dwuklik scentralizował mój świat na tym interfejsie, w którym mogłem się zagrzebać.
4) Zawartość interfejsu interesowała mnie co najwyżej średnio, co nie przeszkodziło jednak w chwilowym zboczeniu z drogi w celu dowiedzenia się, że jedyna odpowiedzialność kontrolera to "zrobienie czegoś" (w metodzie Execute()) mając do dyspozycji informacje o bieżącym żądaniu http oraz coścośtam związanego z url rewritingiem. Na szczęście Reflector posiada przycisk BACK, który pozwoli wygrzebać się z kodowego bagna i wrócić do odpowiedniego miejsca nawet jeśli bardzo damy się ponieść duchowi eksploratora:

Po powrocie do interfejsu kliknąłem prawym przyciskiem, a następnie wybrałem Analyze... zobaczymy gdzież to ów interfejs jest wykorzystywany.
5) W oknie analizatora rozwinąłem odpowiednie grupy i cóż się okazało? A-HA, mamy cię! To już nawet Gosiu by się domyślił, że IControllerFactory tworzy kontrolery:

Przeszedłem zatem w to miejsce aby kontynuować badania.
6) Po wciśnięciu ctrl+r, równoważnym z wybraniem Analyze z menu kontekstowego, ponownie znalazłem się w oknie analizatora. I jakież to ciekawe rzeczy ukazały się moim astygmatycznym ślepiom:

BINGO! Co prawda od razu pojawiła się myśl "hmm, dwa tak mocno powiązane ze sobą typy nazywające się - IControllerFactory oraz ControllerBuilder - tak, jakby robiły dokładnie to samo... ciekawe dlaczego?", ale tym razem poskromiłem ciekawość i zostawiłem inwestygację na kiedy indziej.
Czyli już wiem co trzeba wywołać, aby podmienić standardowy sposób tworzenia kontrolerów. Zostały pytania: GDZIE to zrobić, JAK się do owej metody dostać i CO jej przekazać?
7) Kolejne sekundy z Reflectorem przyniosły odpowiedzi na dwa pierwsze pytania:

W statycznym konstruktorze tworzona jest domyślna instancja Buildera, a właściwość Current właśnie ją zwraca. Nie mamy co prawda do czynienia z Singletonem, ale już znam odpowiedź na pytanie "JAK?". O tak: ControllerBuilder.Current.SetControllerFactory(...). A "GDZIE"? Najpewniej najstosowniejszym miejscem będzie kod wykonywany podczas uruchomienia aplikacji, więc domniemana lokalizacja to Application_Start() w Global.asax.cs.
8) Czas zmierzyć się z pytaniem trzecim: CO tam wrzucić? Aby na nie sensownie odpowiedzieć, należy przyjrzeć się istniejącym implementacjom IControllerFactory. A w tym przypadku - jedynej istniejącej implementacji.

Krótki rzut oka na zawartość klasy DefaultControllerFactory wystarczył do podjęcia decyzji: oto będzie właśnie matka mojej implementacji; za dużo mądrych rzeczy tam się musi dziać w tych wszystkich prywatnych składowych, aby wymyślać koło na nowo i próbować przekodować to samo w swoim, notabene wolnym, czasie. Zawęziłem zatem swoją analizę jedynie do metod zwracających IController – jako kandydatów do ewentualnego nadpisania. Idealna do tego celu okazała się metoda GetControllerInstance():

Jedyne co tak naprawdę robi oprócz sprawdzania argumentów i rzucania wyjątków to wywołanie Activator.CreateInstance(Type), czyli jest to perfekcyjnie zlokalizowany kawałek kodu do podmiany! Zauważyć warto, że oznaczona jest modyfikatorami "protected internal", których występowanie czasami BŁĘDNIE tłumaczone jest jako: "metoda widoczna jedynie dla klas zadeklarowanych w tym samym assembly i dziedziczących z jej właściciela”. Zapamiętać należy, że tak naprawdę stawiany tam jest operator OR, a nie AND! Poprawne wyjaśnienie: "metoda widoczna dla klas zadeklarowanych w tym samym assembly ORAZ dla klas dziedziczących z jej właściciela”. Tak więc możemy ją sobie spokojnie nadpisać.
9) OKej, posiadając te wszystkie informacje można zakodować co następuje:

  1:  public class UnityControllerFactory : DefaultControllerFactory
  2:  {
  3:  	private readonly IUnityContainer _container;
  4:  
  5:  	public UnityControllerFactory(IUnityContainer container)
  6:  	{
  7:  		_container = container;
  8:  	}
  9:  
 10:  	protected override IController GetControllerInstance(Type controllerType)
 11:  	{
 12:  		return (IController)_container.Resolve(controllerType);
 13:  	}
 14:  }

Tyle zabiegów sprowadziło się do właściwie jednej linijki, uzupełnionej o:

  1:  protected void Application_Start()
  2:  {
  3:  	ControllerBuilder.Current.SetControllerFactory(new UnityControllerFactory(Unity.Container));
  4:  	RegisterRoutes(RouteTable.Routes);
  5:  }

, gdzie statyczna klasa Unity to mój własny helper.
I bardzo dobrze. Tym lepiej, że działa bez zarzutu!


To już koniec wycieczki, proszę wysiadać. Mam nadzieję, że przekonałem choć jedną "niewierzącą" osobę jak potrzebnym i wspaniałym narzędziem jest Reflector. Okazało się również, że o ile Google jest zdecydowanie najszybszym sposobem na znalezienie odpowiedzi, jednocześnie nie jest to najbardziej satysfakcjonująca i niosąca najwięcej wartości edukacyjnych droga.
Dopowiem jeszcze, że w rzeczywistości podróż ta trwała około kwadransa. Dopowiem też, że po tym wszystkim z ciekawości poprzeglądałem internet w poszukiwaniu lepszych rozwiązań i... zdziwiłem się jak bardzo można nakombinować, żeby skomplikować sobie życie. Znalazłem sporo alternatyw (bardzo zresztą podobnych), każda jednak jednak miała więcej linii kodu. Po co?

Najczęściej the simpler => the better.

opublikowano 10 czerwca 09 06:28 przez Procent | 7 komentarzy   
Zarejestrowano w kategorii: , , ,
LLBLGen Pro – płatny O/R Mapper. Czy warto?

A cóż to i dlaczegóż to

Niedawno Szymon napisał posta o O/R Mapperach. Korzystając z impulsu postanowiłem także popełnić co nieco w tym temacie, tym bardziej że tą konkretną notkę zaplanowałem przynajmniej 2 miesiące temu. Może ona być uzupełnieniem dla projektów wspomnianych przez Szymona, czyli NHibernate, Linq2Sql i Entity Framework.
Słowem wprowadzenia...
LLBLGen Pro to płatny O/R Mapper od firmy Solutions Design. Najważniejszym człowiekiem za nim stojącym jest Frans Bouma – na to nazwisko natykałem się w internecie jeszcze przed moim kontaktem z LLBLGen.
"Hola hola, płacić za O/R Mapper??" – wykrzyknąć ktoś może w niezmiernym zdumieniu. No i fakt, cena narzędzia (249 jełro) może być dla "samodzielnych" programistów nieco odstraszająca. Warto jednak pamiętać o tym, że fakt "darmowości" bądź "OpenSourcowości" jakiegoś produktu w wielu firmach automatycznie go dyskwalifikuje. W takich sytuacjach programistów w nich pracujących można podzielić na cztery grupy pod względem implementacji dostępu do danych:
1) ręczne pisanie procedur / zapytań... wszyscy byliśmy w tym miejscu i każdy się chyba zgodzi, że nie jest to wymarzony sposób na pracę z danymi, nawet jeśli większość kodu sobie wygenerujemy jakimś pisanym domowymi sposobami generatorem
2) wykorzystanie propozycji z Redmond, czyli Linq2Sql (który jest technologią umierającą), Entity Framework (o którym można przeczytać wiele, z czego póki co 90% to narzekania i wytykanie błędnych rozwiązań; co się pewnie trochę zmieni po wypuszczeniu kolejnej wersji) czy DataSety (o których pisać nawet nie będę, bo traktowanie ich jako technologii dostępu do bazy danych w "normalnej aplikacji" to trochę naciągane rozumowanie)
3) pisanie "własnego O/R Mappera" – tworzyłem kiedyś w pracy coś takiego i nawet zgrabnie tłumaczyło zapytania obiektowe do postaci SQL; dla mnie osobiście to zadanie się podobało, ponieważ było fajnym wyzwaniem architektoniczno-programistycznym; patrząc jednak z punktu widzenia pracodawcy (który chyba sobie z tego do końca sprawy nie zdawał), to pieniądze wydane na pensje pracowników biorących udział w tworzeniu tego projektu (bo nie robiłem wszystkiego tylko i wyłącznie sam) można było wydać na licencje chociażby takiego LLBLGen dla całego działu dev i wyszłoby się na plus: kasy by jeszcze zostało, a firma miałaby gotowy do użycia, przetestowany przez setki klientów, niewymagający ciągłych modyfikacji, supportowany na zewnątrz, dopieszczony produkt, oferujący 100x więcej funkcjonalności niż to co udało mi się wypracować
4) kupno narzędzia, za którego poprawne działanie, poprzez pobranie opłaty za licencję, odpowiedzialna jest firma zewnętrzna

Nie wiem czy się zgodzicie (podejrzewam że tak), ale wg mnie rozwiązanie 4) jest rozwiązaniem najlepszym. Zobaczmy zatem co oferuje nam najbardziej znany produkt z kategorii "płatnych ORM dla .NET". Dobrze jest wiedzieć, że coś takiego istnieje, a z moich obserwacji wynika że w Polsce stosunkowo niewiele osób o nim słyszało.


Cały poniższy tekst nie ma być tutorialem "jak zrobić sobie aplikację z LLBLGen" – od tego jest (całkiem dobra i dość wyczerpująca zresztą) dokumentacja. Nie ma być też dokładnym omówieniem założeń produktu (te z kolei znajdziemy w sekcji "Concepts" wspomnianej dokumentacji). Ma być raczej zbiorem przykładów, uwag i spostrzeżeń, które rzuciły mi sie w oczy podczas dwumiesięcznej pracy z LLBLGen na podstawowym poziomie "pobierz/dodaj/zaktualizuj/usuń". Po lekturze czytelnik powinien być w stanie ocenić czy w ogóle mógłby w przyszłości rozważyć zastosowanie takiego rozwiązania. 
Przykłady oparte będą na bardzo prostej bazie danych: użytkownik ma blogi, blog ma posty, a posty są połączone relacją wiele do wielu z tagami:

Kod C# wygenerujemy przy pomocy narzędzia pozwalającego na wizualną konfigurację tego procesu:
1) tworzymy nowy projekt LLBLGen:

2) wskazujemy źródłową bazę danych

3) wybieramy tabele do wygenerowania (LLBLGen oferuje dwa tryby generacji kodu: Adapter i Self Servicing; jedyny z którego korzystałem to Adapter, ponieważ Self Servicing jest implementacją wzorca Active Record, który to wzorzec nadaje się moim zdaniem jedynie do mikro-projektów)


4) efekt to rozpoznane kolumny, ich typy, relacje pomiędzy tabelami itd. – nic szczególnie interesującego; na tym etapie możemy zmodyfikować nazwy klas do wygenerowania (np. liczba mnoga na pojedynczą), kolumn, poukrywać niepożądane kolumny etc.:

5) na koniec wciskamy F7, podajemy katalog docelowy dla wygenerowanych projektów Visual Studio i wsio, generację mamy za sobą, możemy zacząć operować na naszej bazie danych


Zaczniemy od zniechęcania, czyli...

Wady

1) "rozdęte" klasy

Tak, to prawda. Zapomnij o POCO.

Przygotuj się na pisanie mapowań wygenerowanych klas na domenę, ponieważ to co otrzymujesz z generatora nie nadaje się na "obiekt biznesowy"... a już na pewno nie na transport w środowisku rozproszonym! Ale z drugiej strony jakiekolwiek generowanie klas na podstawie bazy danych w celu zawarcia w nich mechanizmów pozwalających na proste zarządzanie kontaktem z bazą kończy się w ten sposób, więc nie jest to żadne zaskoczenie. Przy Linq2Sql czy Entity Framework mamy to samo.

2) momentami skomplikowane zapytania

Półtorej godziny spędziłem kiedyś nad sformułowaniem za pomocą obiektów LLBLGen zapytania, które można przetłumaczyć na nasz przykład jako "pobierz mi posty z bloga o ID = 1 lub takie które nie mają tagów" (bezsens takiego zapytania w aktualnym scenariuszu pomińmy milczeniem). Teraz zajęłoby mi to chwilę, ale momentami można natrafić na banalny z pozoru problem, który zmusi nas do przewertowania dokumentacji, przeszukania forum i eksperymentowania przez bliżej nieokreśloną ilość czasu. Na szczęście nie są to częste przypadki. Dla zainteresowanych, problem ten rozwiązuje średnio opisany w dokumentacji FieldCompareSetPredicate:

3) "eksplorowalność": minus jeden

Korzystając z LLBLGen musimy przyzwyczaić się do ciągłego odwoływania się do typów wyliczalnych (enumów), klas statycznych, zgadywania "co też robi z tym obiektem przeciążany operator?" itd. Można się do tego dość szybko przyzwyczaić, jednak na początku odnosi się wrażenie, że można było całość ładniej... zaprojektować. Oto przykładowy kod pobierający bloga o zadanym ID wraz z przypisanymi do niego postami:

Na czerwono zaznaczyłem klasy i właściwości, o których istnieniu musimy wiedzieć, zanim uda się nam coś osiągnąć. Jest tego sporo i nie odkrywa się tego zbyt intuicyjne, niestety na początku bez otwartej nieustannie dokumentacji ciężko jest zrobić co_kol_wiek. Wystarczyłoby podczas generacji utworzyć jeden centralny punkt dla każdego Entity zbierający właściwości/klasy umożliwiające wykonanie wszystkich operacji, dobrze to opisać, i świat byłby lepszy.


Czas przyjrzeć się produktowi z drugiej strony:

Zalety / uwagi

1) Wzrost wydajności programisty od pierwszych chwil

To co widzieliśmy na początku artykułu – zaznaczenie checkboxów przy odpowiednich tabelach – jest wszystkim, co musi umieć programista aby rozpocząć pracę. Tak naprawdę może rozpocząć ją więc natychmiast. Dodanie LLBLGen do projektu nie wiąże się z dużym kosztem "edukacyjnym". Jest to po prostu warstwa tłumacząca zapytania do obiektów na SQL, i tyle. Nie trzeba przejmować się nauką polityki cache stosowanego w LLBLGen, ponieważ takiego cache po prostu nie ma. Nie trzeba zastanawiać się nad mapowaniem kolumn na właściwości – robi się to samo. Oczywiście przez takie podejście możemy stracić możliwość uszycia sobie warstwy DAL idealnie dopasowanej do naszej aplikacji, ale wielokrotnie nie jest to niezbędne (poza tym do LLBLGen można się oczywiście w wielu miejscach "podpiąć" własnym pluginem i dostosować jego działanie do własnych wysublimowanych potrzeb, ale tego nie robiłem). Na podaną w wadach konieczność orientowania się w wewnętrznych klasach lekiem są przykłady, przykłady i jeszcze raz przykłady. Wystarczy że jedna osoba w zespole poświęci pół dnia na dokładne zapoznanie się z tymi mechanizmami i już od dnia następnego będzie w stanie pomóc reszcie stworzyć większość zapytań.
Przykłady pojawiające się w tym poście, jak i te zawarte w dokumentacji, aż kłują w oczy możliwością utworzenia jakiegoś helpera redukującego liczbę linii kodu.

2) Pobieranie wybranych kolumn

Wielokrotnie podczas rozmów o OR Mapperach jako argument przeciwko nim słyszałem takie coś: "po co mi ściągać cały wiersz z tabeli, jeśli potrzebuję tylko jedną czy dwie kolumny? a nie będę przecież na każdy osobny taki przypadek tworzył kolejnej klasy, kolejnego mapowania". No i co racja to racja, dlatego poniżej przykład pobrania wszystkich postów, jednak pobrane obiekty zawierać będą jedynie ID i tytuł:

3) Dokumentacja, support

Płatny produkt powinien mieć godną dokumentację i wsparcie producenta. Tak jest w tym przypadku, w dołączonych plikach można znaleźć naprawdę ogrom informacji. Dodatkowo na forum również znajduje się masa rozwiązanych problemów. No i oczywiście jeżeli nie znajdziesz odpowiedzi na dręczące cię pytanie, masz pełne prawo oczekiwać, że ktoś z zespołu odpowie ci tak szybko jak to możliwe.

4) CommandLine – błyskawiczne odzwierciedlanie w kodzie zmian z bazy

Narzędzie CliGenerator.exe pozwala na automatyzację całego procesu generacji kodu. Dlatego też dobrą praktyką byłoby przechowywanie w repozytorium samego pliku projektu LLBLGen, a generacja kodu następowałaby na maszynie programisty bądź na build serverze.

5) Wsparcie dla LINQ

W najnowszej wersji softu wprowadzono możliwość pisania zapytań LINQ i przekazywania ich do infrastruktury LLBLGen. W dokumentacji wygląda to przyjemnie, ale nie korzystałem z tego udogodnienia. Wcześniej alternatywą (wcale nie taką złą) dla LINQ było przekazywanie obiektów klasy RelationPredicateBucket z zawartymi kryteriami wyszukiwania.

6) Wsparcie dla wieeeelu baz danych

Nie powinno to specjalnie dziwić, ale LLBLGen oferuje wsparcie dla wielu silników baz danych. Niechaj poniższy screen pokaże co i jak:

Czy warto?

Celem tego posta nie była reklama czy dokładna prezentacja LLBLGen – tak naprawdę liznęliśmy tylko wierzchołek góry lodowej (ależ dwuznaczne!!). Chciałem tylko pokazać, że poza światem open source istnieją takie PRODUKTY, i wcale nie muszą one pochodzić z firmy Microsoft.

A odpowiedź na pytanie "czy warto?" jest bardzo prosta:
Jeżeli możesz korzystać z rozwiązań darmowych (NHibernate, Active Record z Castle Project, SubSonic...) – go for it, Luke!
Jeżeli nie możesz korzystać z rozwiązań darmowych bądź zależy ci na zrzuceniu części odpowiedzialności za powodzenie twojego projektu na kogoś innego, oczekując supportu z prawdziwego zdarzenia i "pochylania się" nad każdym twoim problemem – LLBLGen Pro jest rozwiązaniem na dzień dzisiejszy po prostu najlepszym. A jeśli twoja firma zapiera się, że jest kryzys i nie stać jej na nowe inwestycje, postaraj się przemówić im do rozsądku. Przerzucenie się na coś takiego jest naprawdę rozwiązaniem win-win, gdzie oni są szczęśliwi (zarabiają na licencjach) a korzysta również twoja firma (pieniądze wydane na licencje z pewnością zwrócą się bardzo szybko, bo tak errorogenne:) i nieprzyjemne pisanie kodu dotykającego bazy danych zostaje z prac nad projektami całkowicie usunięte). Ściągnij trial i zobacz sam. Ściągnij specjalnie przygotowane materiały i pokaż managerowi. Po prostu: zrób wszystko, aby nie tracić życia na ręczne pisanie banalnego, powtarzalnego SQLa!

Powodzenia.

opublikowano 08 czerwca 09 06:45 przez Procent | 16 komentarzy   
Zarejestrowano w kategorii: , ,
Instalacja(SQL Server 2008 + Visual Studio 2008) = Irytacja

Wymyśliłem sobie, że rozpoczynając właśnie nowy projekt wykorzystam Sql Server Compact Edition, przynajmniej na samym początku. Ściągnąłem, zainstalowałem (okazało się w trakcie że już to miałem – zainstalowało się kiedyś razem z VS, ale to nieważne), dodałem lokalną bazę do projektu za pomocą Visual Studio, kliknąłem dwa razy – pięknie, śmiga.
Kolejny pomysł – a czemu by nie pobawić się tą bazą ze znienawidzonego Management Studio (którego notabene już nie nienawidzę; dzięki wirtualnemu środowisku i odseparowaniu wszystkich projektów od siebie da się z nim jednak żyć)? No to odpalam, wybieram "Compact Edition", dogrzebuję się do pliku i... ZONCZYSKO! Komunikat, że "chcę otworzyć zbyt starą wersję pliku" okazał się debilny, ponieważ to SSMS (2005) było za stare na Compact 3.5.
ALE, skoro już sobie postanowiłem że za pomocą SSMS się do tej bazy dobiorę, to nie wypada w połowie drogi rezygnować. Raz/dwa do napędu płytka z SQL Server 2008 i jedziemy. Next –> next –> next, po drodze jakiś reset, weryfikacje, cuda na kiju... prawie godzina zmarnowana. Ale trudno, próbujemy dalej.
W końcu instalator pozwolił mi na wybranie jakichś opcji, z których interesowało mnie jedynie nowe Management Studio. OK –> Next –> cośtamcośtam, kolejna weryfikacja i... FIGA! Oto owej weryfikacji wynik:

Ale WTF? Przecież mam VS 2008, mam Service Packa (z którym zresztą wcześniej zainstalował mi się Compact)... ale na wszelki wypadek uruchamiam instalację SP1 jeszcze raz. Może coś przeoczyłem?…
Po kolejnych minutach ZMARNOWANYCH na czekanie aż instalator ServicePacka powie mi, że produkt jest już zaktualizowany i ponownym uruchomieniu instalatora SqlServer 2008 z podobnym rezultatem rency mi opadli, a żuchwa zbielała.
Ale co robić, po takiej ilości czasu bezecnie wywalonego na śmietnik informatycznej historii nie wypada się po prostu poddać. Cóż się okazało...?
Dla siebie i dla potomności: instalator Sql Server 2008 sprawdza, czy WSZYSTKIE wersje Visual Studio 2008 mają zaaplikowany SP1. WSZYSTKIE, włączając w to Visual C# Express Edition, który mi się zaplątał na wirtualce, a którego nie używam, więc i nie aktualizuję. Ehh...
I teraz się zastanawiam – to moja wina i głupota, czy jednak twórcy instalatora mogli się pokusić o więcej mówiący komunikat? A może któryś z nich siedzi za oknem na swojej miotle i obserwuje, i czeka na pierwszy siwy włos na mojej skroni?
P.S. "Poprawna" instalacja trwa już od dobrych 40 minut... na razie jest ok. 80%... mam nadzieję, że przy 99% nie wyskoczy okno "sorry, your minesweeper high score is too low".

opublikowano 03 czerwca 09 06:29 przez Procent | 7 komentarzy   
Zarejestrowano w kategorii: , ,
Reklamowo-porządkowe sko_dev_jarzenie

Reklamy środków czystości są niepowtarzalnie wręcz obleśne. Czy nikt tym wstrząśniętym stanem swojego kibla czy zlewu ”gospodyniom” nie uświadomił, że zbierać brud i syf powinno się częściej niż raz na rok? I piękny blask powstały po przejechaniu czystą szmatą po megabrudnej powierzchni niekoniecznie musi być wynikiem wylania na tąże szmatę litra reklamowanego właśnie detergentu.

To tak jakby z C# usunąć interfejs IDisposable oraz konstrukcję using (coby o zbieraniu brudów nie było tak łatwo pamiętać). A “rekwizyty reklamowe” powinny dziedziczyć z klasy:

  1:  public abstract class KitchenItem
  2:  {
  3:  	private readonly Timer _timer;
  4:  
  5:  	protected KitchenItem()
  6:  	{
  7:  		GC.KeepAlive(this); // no unwanted cleaning!
  8:  
  9:  		_timer = new Timer { AutoReset = true };
 10:  		_timer.Interval = TimeSpan.FromDays(365).TotalMilliseconds; // wait till I am rrrrrealy dirty
 11:  		_timer.Elapsed += (s, e) => YearlyCleaning();
 12:  		_timer.Start();
 13:  	}
 14:  
 15:  	protected abstract void YearlyCleaning();
 16:  
 17:  	public void Dispose()
 18:  	{
 19:  		throw new UnintendedCleaningException("Wait until I am really dirty, you ***!");
 20:  	}
 21:  }
opublikowano 02 czerwca 09 06:33 przez Procent | 3 komentarzy   
Zarejestrowano w kategorii:
Visual Studio 2010: GenerateFromUsage

Poprzednie edycje Visual Studio potrafiły uprzykrzyć życie, jeżeli ktoś próbował stosować Test Driven Development, czyli: najpierw napisz kod korzystający z klasy, a dopiero potem samą klasę; najpierw zaimplementuj wywołanie metody, a dopiero potem samą metodę. Bez dodatków takich jak Resharper podobne scenariusze były niczym innym jak wielkim pain in the... neck.
Teraz to się zmieni i nawet użytkownicy gołego VS będą mogli cieszyć się generacją kodu na podstawie kontekstu jego wykorzystania. Oto typowy przykład:

1) Definiujemy stworzenie nowej instancji nieistniejącej klasy (wraz z parametrem konstruktora) oraz wywołanie jej metody

2) Generujemy klasę

3) Generujemy konstruktor

4) Generujemy metodę

5) Dopieszczamy wygenerowany kod

Uwagi... Feature taki jest z pewnością niesamowicie przydatny, jednak przydałaby się większa kontrola nad generowanym kodem. Co jeśli chcę klasę publiczną – czy muszę ręcznie dodawać modyfiaktor widoczności (zmiana template w folderze program files/visual studio/costamcostam/templates/class.zip nic nie dala)? Albo metodę nie-internal? Nie znalazłem nigdzie ustawień pozwalających zmienić domyślne zachowanie, co nie oznacza jednak że nie zostaną takie dodane (ani też że już nie są gdzieś ukryte).

opublikowano 30 maja 09 01:45 przez Procent | 6 komentarzy   
Zarejestrowano w kategorii:
Relacja z CodeCamp Warszawa 2009

W sobotę, 23 maja, w siedzibie Microsoft Polska odbyło się techniczne spotkanie CodeCamp Warszawa 2009. Wstęp, pizza i wiedza za darmo. Oto me wrażenia jako biernego uczestnika i obserwatora:

Tomasz Kopacz "Programowanie równoległe i rozproszone"

Rozpoczęcie dnia wystąpieniem Tomasza Kopacza wróżyć może tylko dobrze. Mający apetyt na zwykłe dla tego prelegenta wycieczki w najgłębsze otchłanie szerokich wód technologii nie mogli być zawiedzeni. Tym razem mieliśmy okazję posłuchać o programistycznym zmuszaniu komputera do wykonywania wielu czynności jednocześnie – i dlaczego jest to takie trudne. "Why bother?"... Na samym początku ujrzeliśmy slajd z wykresem obrazującym procentowy wzrost obecności procesorów wielordzeniowych w zwykłych PCtach. W ciągu ostatnich 4 lat wartość ta wzrosła z 30% do prawie 100%! Od równoległości w programowaniu nie ma po prostu ucieczki. Ten jeden czynnik – ignorowanie mocy obliczeniowych więcej niż jednego rdzenia - może być powodem porażki naszej aplikacji na rynku.
Po krótkim wstępie obrazującym nam stan aktualny posłuchaliśmy trochę o teorii programowania równoległego. Poznaliśmy największe wyzwania czekające na programistów, przyczyny powstania i cele wielu rdzeni oraz jak je można wykorzystać już teraz. Kilka demonstracji kodu pisanego w dniu dzisiejszym w celu osiągnięcia "równoległej nirwany" mogło wydać się wszystkim aż nazbyt dobrze znane z własnych doświadczeń. Nawet najprostsze operacje takie jak asynchroniczne pobranie danych dla strony ASP.NET czy rozdzielenie banalnego algorytmu pomiędzy kilka wątków tak bardzo redukują czytelność kodu i pierwotny zamiar programisty, że ciężko jest się w tym wszystkim połapać. Jeśli dodamy do tego spory narzut wydajnościowy związany z używaniem mechanizmów takich jak Semafor, Mutex, EventWaitHandle czy lock okaże się, że smutne z nas, programistów, misie i że zdecydowanie jest to obszar dający pole do popisu twórcom nowych rozwiązań i koncepcji.
A jeśli takie mechanizmy wrzucić bezpośrednio do klas .NET? Zarządzać wątkami, zasobami, mocą komputera bez ciągłego kontaktu z systemem operacyjnym? Uzyskalibyśmy dwie rzeczy: po pierwsze lepszą wydajność, po drugie szansę na zaimplementowanie tego w prosty do wykorzystania sposób. I tak to właśnie wkrótce będzie wyglądać. W Visual Studio 2010 pojawi się wiele udogodnień dla pracy z wielowątkowym kodem i jego debuggowaniem. Sam .NET Framework z kolei udostępni nową przestrzeń nazw (System.Threading.Tasks) zawierającą całkiem nowe podejście do pisania takich projektów. Zamiast Thread będziemy używać Task, zadania zrównoleglimy wywołaniem jednej metody AsParallel() z PLINQ, opóźnioną inicjalizację zmiennych zapewni nam klasa Lazy<T>, a dostępu do zmiennej oczekującej na wykonanie operacji w tle ustrzeże wywołanie Task<int>.Factory.StartNew(). Cóż... będzie fajnie:).
Po dość szczegółowym i popartym demonstracjami omówieniu powyższych koncepcji skończyła się ciekawa część sesji. Z programowania równoległego na jednej maszynie przeszliśmy do tematów rozpraszania systemów na wiele maszyn oraz do tak ostatnio popularnej chmury. Niestety te ostatnie kilkanaście minut uważam za zbyt rozbite pomiędzy wiele różnych tematów. Zamiast po kilka minut poświęcić na CCR (Concurrency and Coordination Runtime), DSS (Decentralized Software Services), HPC Server i Windows Azure (które to tematy same w sobie są z pewnością bardzo zajmujące), można było jeszcze trochę poopowiadać o tym co nas, programistów ze zwykłej gliny, kręciło najbardziej, czyli nowy .NET.

Moja ocena: 7/9 (za fachowe i napawające optymizmem przedstawienie kierunku rozwoju .NET; nie więcej jednak, bo zbyt dużo materiału "na doczepkę")

Piotr Czekała i Krzysztof Bartkowski "Media w Silverlight - dostarczanie niezapomnianych wrażeń"

Po krótkiej przerwie na scenę wkroczyło dwóch prelegentów z firmy Insys. Z Silverlightem nie miałem póki co zbyt wiele do czynienia i z zainteresowaniem słuchałem tego, co mieli nam do przekazania. A mieli do przekazania sporo – i to na bardzo satysfakcjonującym poziomie szczegółowości. Sesję podzielono na trzy części.
Pierwsza z nich to wstęp. Przedstawienie siebie, firmy oraz tego czym się zajmują na co dzień. Historyjki o LechTV, zmaganiach z kolejnymi wersjami SL i próbami przekonania klienta i użytkowników że "naprawdę warto doinstalować ten plugin" były dość interesujące i momentami zabawne, ale jak na mój gust – za długo "reklamowano" firmę Insys. Choć z drugiej strony – nie było to bardzo irytujące i w sumie odrobina autopromocji nikomu zbytnio przeszkadzać nie powinna:).
Główną część sesji stanowiły opowieści o prezentowaniu audio i wideo za pomocą Silverlight. Dowiedzieliśmy się co to jest Progressive Download – i dlaczego w miarę możliwości należy stosować bardziej zaawansowane rozwiązania. Przeszliśmy do Streamingu i posłuchaliśmy o jego wyższości nad progresywnym ściąganiem. Oba tematy okraszono demonstracjami na żywo, więc mogliśmy momentalnie przekonać się na własne oczy co autor ma na myśli. Demonstracje przygotowano starannie, więc nic się nie wysypało i uniknięto tak często spotykanego efektu "dziwne, przed kwadransem działało...". Następnie dużo czasu poświęcono na zaprezentowanie Smooth Streaming, możliwościami bijącego na głowę dwie wcześniej przedstawione technologie. Odtwarzanie wideo bez ciągłego oczekiwania na buforowanie, skakanie po całym filmie bez zacięcia, nieprzerwane odtwarzanie i adaptowanie jakości wideo do aktualnego stanu łącza == absolutny komfort użytkownika. Zobaczyliśmy również jak za pomocą Expression Encoder przygotować media gotowe do wykorzystania przez Silverlight. Ogólnie ta część niosła za sobą spore walory edukacyjne i pomimo kilku nużących elementów i momentami usypiającego głosu prelegenta – podobało mi się.
Następnie nadszedł czas na feature, który u po wyjściu SL2 u wielu osób mógł powodować opad szczęki i był tzw. WOW-factorem tej technologii. Chodzi oczywiście o DeepZoom. Jako wiodący motyw służący za demo dla omawianych funkcjonalności potraktowano witrynę Sunrise Festival, gdzie można bawić się w przybliżanie mozaiki złożonej z ponad 1000 zdjęć. Dodatkowo zaimplementowano filtrowanie prezentowanych obrazów wzbogacone o animacje każdego foto z osobna. Całość robiła wrażenie, tym bardziej że wydajność rozwiązania autentycznie nie pozostawia nic do życzenia. Przy okazji prelegent podzielił się wiedzą zdobytą podczas implementacji tego projektu – widać, że technologia nie ma przed nim tajemnic. Omówiono dokładnie strukturę plików, jakiej wymaga DeepZoom i zaprezentowano narzędzie Deep Zoom Composer służące do jej generowania. Jednocześnie wskazano wady i niedociągnięcia tej aplikacji, jak również powiedziano kilka słów o autorskim programie zastępującym DZC z ominięciem jego wpadek. Imponować mogła również trójwymiarowa panorama pola golfowego stworzona przy użyciu Photosynth oraz demonstracje oprogramowania kontroli nad wyświetlanym obrazem za pomocą udostępnianego przez SL API.
Z sesji wychodziło się z wrażeniem, że Silverlight nie ma przed prelegentami tajemnic i ze spokojem można zaufać przekazanym przez nich informacjom. Wystąpienie z kategorii "przez pasjonatów dla pasjonatów".

Moja ocena: 7/9 (za dogłębne przedstawienie tematu)

Bartłomiej Legiędź "Wyjątki kontrolowane w C#"

O idei checked exceptions, ich wadach, zaletach i możliwych implementacjach opowiedział nam kolega z Zine’a, będący jednocześnie zwycięzcą łódzkiego Speaker Idola. Wystąpienie sprawiało wrażenie bardzo przemyślanego, doskonale zorganizowanego i poprowadzonego z wprawą godną starego scenicznego wyjadacza. Temat był dla mnie dość ciekawy, ponieważ swego czasu sam zastanawiałem się nad omawianą kwestią. Przemyślenia te zakończyłem usypaniem kopczyka wdzięczności w mej programistycznej duszy na cześć Andersa Hejlsberga, twórcy C#, za wspaniałą decyzję nieimplementowania tego mechanizmu w moim ulubionym języku.
Z zainteresowaniem wysłuchałem historii wyjątków kontrolowanych: od CLU, przez C++ aż do Javy. Zaprezentowane przykłady implementacji zostały profesjonalnie omówione, a ich wady bezlitośnie wytknięte. Zgadzam się z prelegentem, że implementacja z Javy byłaby całkowicie do zaakceptowania, gdyby istniała możliwość zrezygnowania z sygnalizowania BŁĘDU podczas KOMPILACJI programu w razie niestosowania się do tych zaleceń. W obecnej postaci kończyć się to musi brzydkimi obejściami obowiązku deklarowania rzucanych wyjątków poprzez nagminne ciche łapanie klasy bazowej bądź deklarowanie "throws Exception". Takie zycie...
Bartek doszedł do wniosku, że taki mechanizm jest jednak przydatny i postanowił zrobić coś z aktualnym stanem rzeczy. Efektem tego postanowienia jest plugin do Resharpera o nazwie Exceptional. Jego zadanie jest dość proste - wynajduje on w naszym kodzie niełapane wyjątki i proponuje dwa rozwiązania: złap wyjątek bądź udokumentuj możliwość jego wystąpienia. W ten sposób otrzymujemy nienachalną informację o potencjalnym przeoczeniu czegoś ważnego oraz możliwość automatycznego tego naprawienia kilkoma kliknięciami wywołującymi odpowiednią akcję z listy sugestii R#. Dla mnie bomba, a następujące po części teoretycznej demo skłoniło mnie do ściągnięcia i instalacji tego narzędzia. Wymaga ono jeszcze oczywiście dopracowania, ale nawet w obecnej postaci jest naprawdę godne polecenia.
Lubię słuchać kogoś wysławiającego moje własne przekonania w uporządkowany sposób, więc słuchało mi się bardzo przyjemnie:). Skupiono się na jednym, bardzo ściśle określonym i w miarę istotnym problemie, następnie dokładnie go przeanalizowano, zbadano istniejące rozwiązania, wytknięto ich wady, zaproponowano własną propozycję a na koniec ją zrealizowano. Brawo! Dla mnie najlepsza sesja na całej imprezie.

Moja ocena: 8/9 (za bardzo przemyślaną agendę, świetną treść, doskonałą propozycję rozwiązania problemu i w końcu – jej realizację)

Michał Żyliński "Ile cukru w cukrze - IronPython i jego zastosowania"

O wprowadzeniu języków dynamicznych do .NET Framework słyszy się od dawna, a sztandarowymi przykładami w tym temacie są IronRuby i IronPython. Z czym to się jednak je, jak się w tym pisze i do czego można to wykorzystać? Na te pytania miał nam odpowiedzieć pracownik polskiego oddziału Microsoftu, Michał Żyliński.
Przyznać muszę, że mój kontakt z językami dynamicznymi był do tej pory raczej symboliczny. Ot, parę przeczytanych artykułów, obejrzanych prezentacji, kilka godzin luźnych eksperymentów. Minimalny poziom wiedzy wystarczył jednak do bezproblemowego zrozumienia wstępu, podczas którego posłuchaliśmy trochę o historii i strukturze Pythona oraz zobaczyliśmy przykładowy kod. Co ciekawe, prelegent w celu podkreślenia faktu, że mówi o Pythonie, a nie jego konkretnej implementacji dla .NET, korzystał ze środowiska PythonWin a nie VS.
Jako możliwe zastosowania Pythona zaproponowano nam ORMapping, Aspect-Oriented Programming (chociaż nie wiem jaką rolę miałby tam spełniać) czy prosty serwer www. Ciekawie wyglądało demko otwierające Worda i wypisujące jakiś tekst do nowoutworzonego dokumentu – zajmowało jedynie 5 linii! To właśnie zwięzłość i skondensowanie instrukcji przy jednoczesnym zachowaniu czytelności kodu jest jedną z największych zalet Pythona.
Po przejściu do właściwej części prezentacji, czyli włączenia Pythona do świata .NET, zaprezentowano nam różnice pomiędzy jego standardowym środowiskiem a implementacją rodem z Microsoft. Mostem łączącym świat statyczny z dynamicznym jest DLR (Dynamic Language Runtime), o którym usłyszeliśmy kilka słów i poznaliśmy zasady jego działania z bardzo wysokiego lotu ptaka.
Później nastąpiła próba odpowiedzi na najważniejsze chyba pytanie: po co nam Python w .NET? Przy tej okazji obejrzeliśmy kilka dem kończących wystąpienie. Były to między innymi kombinacje wykorzystania IronPython w środowisku WinForms, w ASP.NET, w Silverlight... Niestety, im dłużej na to patrzyłem, tym mniej byłem do całej idei zaprzęgania dynamicznych języków do takich celów przekonany. W takich scenariuszach kodu wcale nie będzie strasznie mniej, a włączanie dodatkowego języka programowania do projektu tylko dlatego że jest "fancy" i wszyscy o tym mówią wydaje mi się nie do końca odpowiedzialną decyzją. Tak naprawdę jedyne sensowne (w kontekście aplikacji .NET) zastosowanie, jakie wyniosłem z sesji, to udostępnienie użytkownikom aplikacji możliwości pisania skryptów modyfikujących jej zachowanie – i jeśli kiedykolwiek pojawi się u mnie taka potrzeba, wówczas z pewnością zwrócę swe oczęta w odpowiednią stronę (ostatnio pisał też o tym Bartek Szafko).

Moja ocena: 6/9 (tak naprawdę nie jestem wiele mądrzejszy po sesji niż przed nią – i nadal nie do końca wiem, gdzie w codziennym programowaniu w .NET jest miejsce na IronPythona)

Andrzej Piotrowski "Designer + Programista = Produkt, Problem + Pomysł = Aplikacja"

Na koniec wystąpił przed nami zwycięzca (a właściwie jedyny uczestnik) warszawskiego Speaker Idola. Temat mówiący niewiele i... do dziś nie wiem o co tak naprawdę chodziło. Od początku widać było, że prelegenta zżera trema – ale to jest całkowicie zrozumiałe i nikt nie może mieć o to pretensji. Próby nawiązywania kontaktu z publicznością poprzez organizowanie minikonkursów z nagrodami – jak najbardziej godne pochwały. I na tym niestety owe pochwały muszę zakończyć.
Nie potrafię nawet teraz poukładać sobie w głowie z czym po kolei mieliśmy do czynienia. Najpierw zobaczyliśmy na slajdzie definicje "strategii 4P oraz 4C". Chwilę potem nastąpiła wzmianka o Strategii Błękitnego Oceanu. W międzyczasie w połowie omawiania slajdu przechodziliśmy bez słowa wyjaśnienia do kolejnego tematu, co powodowało u mnie w głowie niemały zamęt. O co chodzi?? Podczas obserwacji prelegenta przypominały mi się momentami czasy liceum, gdy na lekcji polskiego trzeba było recytować jakiś durny wiersz i chcąc zrobić to najszybciej jak to możliwe – plątałem się i myliłem. Musiałem wtedy wyglądać właśnie tak. Rozumiem, że można nie urodzić się z naturalnym talentem do prezentowania czegokolwiek, ale w takim razie po co się zgłaszać na taką imprezę bez uprzedniego zdobycia jakiegokolwiek doświadczenia, chociażby na lokalnej grupie .NET? A jeśli takie doświadczenie jest to... może po prostu trzeba lepiej się przygotować?
Po kilku pierwszych slajdach zostałem absolutnie zaskoczony tym co zdarzyło się dalej. Otóż zaprezentowano nam założenia projektu z konkursu Imagine Cup, z roku 2007, w którym prezenter brał udział. A po prezentacji założeń... kilkuminutowy filmik pokazujący jego działanie? Dwa razy?? WTF?
Ale to nie koniec, bo... za chwilę zostaliśmy uraczeni dokładnie tym samym, tylko że z IC 2008. Potem puszczono nam jakąś "wizjonerską reklamę". Potem kolejny filmik, nie pamiętam juz o czym. A następnie... a jakże, filmik o projekcie z IC 2009!!
Na koniec kilka słów o tym, że "wizja jest ważniejsza od możliwości jej realizacji" (tekst może dobry na Imagine Cup, ale nie tutaj), że trzeba zmieniać świat i że bez dobrego designu nie mamy szans. Po drodze poplątano design w sensie "interfejs użytkownika" z designem w sensie "architektura rozwiązania". I... to tyle.
Może opis ten wygląda złośliwie, jednak nie takie jest moje zamierzenie. Przez cały czas bowiem zastanawiałem się "o co chodzi?", i nadal nie mam pojęcia. Proponuję po prostu przed następnym wystąpieniem pokazać je uprzednio komuś dobrej woli i skonfrontować jego odczucia z zamierzonym przekazem. Przed prezentacją byłem bardzo ciekaw co ujrzę, bo czytając od czasu do czasu bloga autora można odnieść wrażenie, że jest megamastawymiataczem (szczególnie utkwiła mi w pamięci bardzo protekcjonalna i nie na miejscu chęć "wypunktowania" Artura Paluszyńskiego w relacji z C2C). I cóż... było trochę inaczej.
I na koniec jeszcze jedna uwaga do samych slajdów: pakowanie 10 kolorów na jednym ekranie oraz trzykrotna zmiana motywu prezentacji nie wypada korzystnie.

Moja ocena: <brak> (bo naprawdę nie wiem co miałbym wystawić – mimo chęci i starań prelegenta był to po prostu niezrozumiały chaotyczny bełkot; albo jestem głupi i tylko ja tak to odebrałem)

Podsumowanie

Jak na takie "niszowe" weekendowe spotkanie, bez "KONFERENCYJNEGO" nadęcia i pompy – zdecydowanie udane wykonanie. Podziękowania tym, którym się chce.

Ocena ogólna konferencji: 8/9

opublikowano 27 maja 09 07:02 przez Procent | 8 komentarzy   
Zarejestrowano w kategorii: , ,
Jedna z największych wad Virtual PC wyeliminowana

Niedawno moje życie stało się lepsze. Nie jestem może w raju, ale przybliżyłem się o jeden kroczek. A wszystko za sprawą małego hotfixa. Ale od początku:
Kilka miesięcy temu pisałem o megairytującej cesze Virtual PC, która uniemożliwiała pracę w rozdzielczościach wyższych niż 1600x1200. Co prawda w poście podałem pewien łorkaraund, lecz w moim przypadku nie do końca się on sprawdzał. VS w mojej konfiguracji współpracował z połączeniem przez Remote Desktop mniej niż idealnie (pojawiały się pewne problemy z kolorystyką w edytorze), zatem pozostało mi rozciąganie wirtualki w okienku do 1600x1200 – a na monitorze zostawało miejsce akurat na Winampa:). Pewnego ranka coś mnie tknęło i dałem nura w internet w poszukiwaniu informacji o następcy VPC 2007 – czy także będzie miał ta wadę? Dzięki chęci zaspokojenia swojej niepohamowanej ciekawości znalazłem coś, co spowodowało prawdziwego banana na mojej twarzy. Mianowicie w lutym roku bieżącego wypuszczono hotfixa do VPC, który pozwala na pracę w wyższych rozdzielczościach! Nareszcie full screen na 24" pojawił się w zasięgu mej chciwej ręki, jea, i to bez uciekania się do płatnego VMWare. Instalka do ściągnięcia oczywiście ze stron Microsoftu: http://support.microsoft.com/?id=958162.

Kilka uwag:
1) działa jak złoto:)
2) maszyny wirtualne z zachowanym stanem wymagają restartu – czyli zachowany stan zostaje utracony; radzę dlatego zastanowić się przed instalacją czy któraś z wirtualek nie znajduje się w stanie bardzo nam bliskim
3) pomimo komunikatu:

"Restart Requirement
You do not have to restart the computer after you apply this hotfix.
"

pierwszym okienkiem, które pojawiło się po zaaplikowaniu hotfixa, był monit o restart komputera:). Świnie.

Użytkownikom VPC gorąco polecam. Jeszcze niechaj tylko w kolejnej wersji pojawi się możliwość korzystania przez wirtualkę z wielu rdzeni (pomarzyć można...) i produkt będzie w stanie zmierzyć się z płatną konkurencją.

opublikowano 26 maja 09 07:18 przez Procent | 1 komentarzy   
Zarejestrowano w kategorii: , ,
Visual Studio 2010: Zoom

Bardzo przydatną konsekwencją przeprojektowania wyglądu Visuala 2010 z wykorzystaniem WPF jest ZOOOOM. Nie trzeba będzie już zwiększać czcionki na potrzeby prezentacji czy przyzwyczajać do niewygodnej konfiguracji ekranu.
Przed:

Po (ctrl + wheel):

Jedyne co mi trochę przeszkadza to brak listy z możliwością wyboru powiększenia (np od 10 do 1000%) z domyślnie wybranym 100%. Aktualnie jedynym znalezionym przeze mnie sposobem na powrócenie do standardowego powiększenia (oprócz ręcznego manipulowania kółkiem myszki) jest zamknięcie i otwarcie zakładki.
I jak już przy zakładkach jesteśmy: powiększenie działa per zakładka, a nie globalnie. I tak powinno być.

I tyle, o tej akurat nowości ciężej cokolwiek więcej napisać:).

opublikowano 24 maja 09 04:14 przez Procent | 4 komentarzy   
Zarejestrowano w kategorii:
Więcej wypowiedzi Następna strona »