piątek, 2 sierpnia 2013

SSMScripter - "Go to definition" dla SQL Management Studio 2012

Tak się złożyło, że ostatnio dużo czasu w pracy spędzam nad MS SQL z wykorzystaniem SQL Management Studio 2012 i czuję mały niedosyt. Nie mówię, że edytor jest zły czy coś, ale przeglądanie informacji w oknie "Object Explorer" przy ~100 procedurach składowanych, ~50 funkcjach i 3-4 wyzwalaczach na każdą z tabel nie należy do przyjemności (nie wspominając już o straconym czasie). Tym bardziej jeżeli wchodzi się w głąb zależności pomiędzy obiektami, szaleństwo. Może gubią mnie moje przyzwyczajenia z Visual Studio i komendą "Go to definition" (F12), nie wiem. Wiem na pewno, że łącząc się bezpośrednio z serwerem MS SQL to okienko "Navigate To" niewiele pomaga. Szybciej jest z SQL Search, ale to też nie do końca to. Mimo swoich nieocenionych możliwości dalej trzeba używać "Modify..." z "Object Explorer". Istnieje natomiast takie cudo jak SSMSBoost posiadające odpowiednią funkcjonalność. Niestety nie mogłem zainstalować tego w pracy, ale skoro oni mogli to ja też mogę. Postanowiłem się nieco zabawić i napisać własny plugin do Management Studio.

Tak powstał SSMScripter. Prosty dodatek umożliwiający skryptowanie obiektów bezpośrednio z edytora tekstu do nowego okna z poziomu menu kontekstowego lub właśnie skrótu F12.


Co ważne dodatek nie wywołuje standardowych metod skryptujących w jakie wyposażone jest Management Studio tylko robi to po swojemu wyciągając informacje z tabel systemowych, tzn:

  1. Sprawdź tekst pod myszką
  2. Znajdź w tekście potencjalny obiekt
  3. Spróbuj dowiedzieć się o nim jak najwięcej (typ, schema, nazwa itp)
  4. Wyciągnij z bazy jego zawartość i otwórz w nowym oknie
Proste, szybkie i nawet działa. Wszystko co potrzebne do uruchomienia dodatku znajdziecie na stronie z projektem:
https://github.com/mkoscielniak/SSMScripter

PS. jeżeli po skopiowaniu plików w odpowiednie miejsce w Management Studio 2012 nic się nie zmieniło, najprawdopodobniej oznacza to, że Windows jest za bardzo troskliwy i trzeba odblokować SSMScripter.dll w właściwościach pliku ;-)

środa, 21 listopada 2012

VS2012 w świecie Embedded i .NET 3.9 CF

Tworząc na platformę Windows Embedded Compact (Windows CE) oraz Windows Embedded Handheld (Windows Mobile) programista .NET nie ma zbyt dużego wyboru. Komplet Visual Studio 2008 i .NET 2/3.5 CF jest standardem. Co prawda VS2008 to całkiem solidny kombajn z którym można żyć w zgodzie (zresztą robię to codziennie :)). Nie zmienia to jednak faktu, że nowsze wersje są po prostu lepsze.
Po 5 latach zaniedbywania programistów w końcu coś drgnęło. Microsoft zapowiedział wydanie Windows Embedded Compact 2013 w pierwszym kwartale przyszłego roku (zresztą nazwa 2013 jest dość wymowna ;)). Informacja nie była by taka elektryzująca gdyby nie fakt, iż wraz z system udostępnione zostanie wsparcie dla VS2012 oraz "nowy" .NET 3.9. Jako programista tego segmentu mogę powiedzieć jedno... W końcu!
Ciekawi mnie tylko, jak to wszytko będzie wyglądało. Jak wiele z elementów VS2012 będzie można wykorzystać, żeby usprawnić codzienną pracę. Oprócz kwestii podstawowych jak edytor formatek, kompilacja, debugowanie itp. będzie wsparcie dla TFS i narzędzi AML. Fajnie, ale ani z pierwszego, ani z drugich nie korzystam. Cóż trzeba poczekać, szczegółów jest niewiele.
Inaczej ma się sprawa z .NET 3.9 CF. Z udostępnionych informacji wynika, że API się nie zmieni. Tzn, kod z 3.5 będzie można bezboleśnie (jeszcze zobaczymy jak to będzie) przenieść na nowszą wersję. Bardziej znaczące są zmiany pod spodem. Wymieniane są 3 główne. Mają bezpośrednio wpłynąć na wydajność platformy. A wszystko to wzorowane na elementach Windows Phone. Otrzymamy zatem:
  • Lepszy GC
  • Wsparcie dla wielu rdzeni
  • Sharing Server - współdzielenie bibliotek pomiędzy różnymi aplikacjami
Pomimo całego mojego entuzjazmu martwi mnie jeden fakt. Jeżeli urządzenie miało preinstalowany .NET 2.0 CF to zawsze mogłem wziąć plik CAB od wersji 3.5 i zainstalować nowszą wersję. Czy teraz nie będzie inaczej? Czy .NET 3.9 CF nie będzie bardziej związany z samym systemem? Czy będzie go można zainstalować na CE 5 lub 6. Do tego jeszcze kwestia czy w VS2012 będzie można pisać pod .NET 3.5 CF? Jak na razie, mam więcej pytań niż odpowiedzi...

wtorek, 30 października 2012

Debugowanie usług windows w Visual Studio

Jak wiadomo debuger wielkim przyjacielem programisty jest, ale nie zawsze istnieje możliwość odpalenia go od tak sobie wciskając F5. W przypadku usług (serwisów) windows otrzymamy komunikat:
---------------------------
Błąd uruchomienia usługi systemu Windows
---------------------------
Nie można uruchomić usługi z wiersza polecenia lub z debugera.
Usługa systemu Windows musi być najpierw zainstalowana (przy użyciu pliku installutil.exe),
a następnie uruchomiona za pomocą Eksploratora serwera, 
Narzędzi administracyjnych usług systemu Windows lub polecenia NET START.
---------------------------
OK   
---------------------------
Pozostaje pytanie co z tym fantem zrobić? Jak na razie spotkałem się z kilkoma różnymi sposobami, np:
Nie są one jednak wygodne ponieważ w każdym przypadku wymuszają chwilowe dodawanie kodu, kompilację warunkową lub parametry lini komend sterujące przebiegiem uruchomienia serwisu. Jaki jest więc złoty środek? Otóż istnieje możliwość sprawdzenia czy uruchomiony proces umożliwia użytkownikowi interakcję. Służy do tego właściwość Environment.UserInteractive. Dzięki niej wystarczy prosty warunek w punkcie wejścia do aplikacji aby rozdzielić przebieg raz a skutecznie. Czyli:
  • Zmieniamy rodzaj aplikacji na konsolową (wartość Output Type, zakładki Application, w Properties danego projektu) dzięki czemu zyskujemy wgląd w konsolę
  • Dodajemy kod umożliwiający odpalenie implementacji OnStart i OnStop przeciążonych po ServiceBase:
    public partial class Service1 : ServiceBase
    {
        public Service1()
        {
            InitializeComponent();
        }
    
        internal void Start(string[] args)
        {
            OnStart(args);
        }
    
        internal void Stop()
        {
            OnStop();
        }
    
        protected override void OnStart(string[] args)
        {
            //...
        }
    
        protected override void OnStop()
        {
            //...
        }
    }
    
  • Podmieniamy standardową metodę Main na:
    static void Main(string[] args)
    {
        Service1 svc = new Service1();
    
        if (Environment.UserInteractive)
        {
            Console.WriteLine("Starting...");
            svc.Start(args);
            Console.WriteLine("Started");
            Console.ReadLine();
            Console.WriteLine("Stopping...");
            svc.Stop();
            Console.WriteLine("Stopped");
        }
        else
        {
            ServiceBase[] ServicesToRun;
            ServicesToRun = new ServiceBase[] 
            { 
                svc
            };
            ServiceBase.Run(ServicesToRun);
        }
    }
    
W ten sposób, kiedy wciśniemy F5 uruchomiony zostanie debugger z całym jego dobrodziejstwem. Natomiast po zainstalowaniu i uruchomieniu serwisu z poziomu services.msc odpali się druga klasyczna dla usług nitka kodu. Wilk syty i owca cała ;)

PS. Równie dobrze, zamiast konsoli można przygotować aplikację okienkową, nie ma tutaj ograniczeń, idea pozostaje niezmieniona.

wtorek, 7 czerwca 2011

Konsola zarządzania

Jakiś czas temu stwierdziłem, że w projektach jakie wykonuję brakuje mi takiego jednego centralnego czegoś... Konsoli w której szybko i bez problemu mógłbym wykonać przewidziane wcześniej zadania takie jak:

środa, 15 grudnia 2010

NHibernate 3 i QueryOver API

Całkiem niedawno pojawiła się nowa wersja NHibernate oznaczona numerem 3. Informacje co, gdzie i jak uległo zmianie można oczywiście znaleźć na głównej stronie projektu. Mi z tych wszystkich nowości najbardziej przypadło do gustu nowe API do wykonywania zapytań czyli QueryOver.

wtorek, 16 listopada 2010

Fluent Validation i dziedziczenie

FluentValidation jest całkiem przyjemną biblioteką, która jak wskazuje nazwa dostarcza narzędzie do walidacji klas poprzez interfejs typu fluent. Co czyni ją bardzo prostą w użyciu (przykłady w dokumentacji). Poza standardowym przypadkiem tzn mamy klasę, mamy walidator i gotowe, wspiera również zagnieżdżenia klas oraz walidację kolekcji. Do pewnego czasu wydawało mi się, że więcej do szczęścia nie będzie mi potrzebne. Zawsze jest jednak jakieś ale... Biblioteka ze względu na sposób budowania walidatorów (dziedziczenie po klasie AbstractValidator<T>) nie wspiera dziedziczenia ich samych, co jednak czasem może się przydać.

piątek, 29 października 2010

Operacje asynchroniczne, czyli C# 5.0

Każda kolejna wersja platformy .NET wprowadza coraz "fajniejsze" ułatwienia dla programisty. I chyba trudno się z tym stwierdzeniem nie zgodzić. Zaczęło się od anonimowych delegatów. Później bardziej funkcyjne podejście i LINQ. W .NET 4.0 wprowadzono słowo kluczowe dynamic. Teraz przyszedł czas na lepsze wsparcie dla operacji asynchronicznych. Od wczoraj dostępny jest do pobrania Visual Studio Async CTP, który do C# i VB wprowadza dwa nowe słowa kluczowe await oraz async.

środa, 20 października 2010

Debugowanie testów jednostkowych NUnit

Główną rolą testów jednostkowych jest wspomaganie wytwarzania oprogramowania poprzez testowanie element po elemencie (jednostce) całego rozwiązania. O ich zaletach nie trzeba się specjalnie rozwodzić ponieważ temat ten był i wciąż jest wałkowany na wielu blogach i serwisach tematycznych. Istotne jest, że po uruchomieniu testu wiemy co poszło nie tak, gdzie oraz jaki wynik powinniśmy otrzymać, a jaki otrzymaliśmy. W tym wszystkim zawsze brakowało mi jednak dokładnej wiedzy dlaczego coś poszło nie tak. Stąd możliwość debugowania testów jednostkowych wydaje się bardzo kusząca.

sobota, 21 sierpnia 2010

Problem PasswordBox przy wykorzystaniu wzorca MVVM

PasswordBox jak łatwo się domyślić jest standardową kontrolką WPF dzięki której użytkownik otrzymuje zamaskowane pole tekstowe do wpisywanie haseł itp. Kontrolka spisuje się wyśmienicie do czasu gdy przy budowaniu aplikacji nie wykorzystywany jest wzorzec MVVM (i inne pokrewne Model-View-* oparte o bindowaniu). Powód jest bardzo prosty. Ze względów bezpieczeństwa właściwość Password kontrolki nie jest uznawana jako Dependency Property co uniemożliwia bindowanie. Panowie z MS dokonali akurat takiego wyboru ponieważ wymusiłoby to ciągłe utrzymanie jawnego hasła w pamięci.

poniedziałek, 28 czerwca 2010

Problem zależności pomiędzy zewnątrznymi bibliotekami

Wykorzystując zewnętrzne biblioteki można bardzo łatwo wpaść w pułapkę niezgodności ich wersji. Dla przykładu nasza aplikacja wykorzystuje biblioteki A i B. Każda z nich do pracy potrzebuje biblioteki C. Ale A używa wersji 1.2, a B 1.5. Może to doprowadzić do następującego błędu kompilacji (i to w najlepszym przypadku):

Assembly 'B, Version=1.0.0.000, Culture=neutral, PublicKeyToken=17863af14b0044da' uses 'C, Version=1.5.0.0, Culture=neutral, PublicKeyToken=407dd0808d44fbdc' which has a higher version than referenced assembly 'C, Version=1.2.0.0, Culture=neutral, PublicKeyToken=407dd0808d44fbdc'

Ostatnio sam spotkałem się z tym problemem wykorzystując w jednym projekcie możliwości AOP kontenera Autofac i NHibernate. Obydwie biblioteki były zależne od Castle.Core.dll. Zostawiając jedynie jej nowszą wersję kontener działał bez zarzutu, ale NHibernate przy próbie stworzenia proxy do encji rzucał pięknym wyjątkiem:

System.IO.FileLoadException : Could not load file or assembly 'Castle.Core, Version=1.1.0.0, Culture=neutral, PublicKeyToken=407dd0808d44fbdc' or one of its dependencies. The located assembly's manifest definition does not match the assembly reference. (Exception from HRESULT: 0x80131040)

Najprostszym rozwiązaniem problemu okazało się zmuszenie NHibernate do wykorzystywanie nowszej wersji Castle.Core poprzez tzw Assembly Binding Redirection. Sprowadza się to do dodania paru linijek w pliku *.config
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <dependentAssembly>
        <assemblyIdentity name="Castle.Core" publicKeyToken="407DD0808D44FBDC" />
        <bindingRedirect oldVersion="0.0.0.0-1.2.0.0" newVersion="1.2.0.0"/>
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="Castle.DynamicProxy2" publicKeyToken="407DD0808D44FBDC" />
        <bindingRedirect oldVersion="0.0.0.0-2.2.0.0" newVersion="2.2.0.0"/>
      </dependentAssembly>
    </assemblyBinding>
  </runtime>
</configuration>
Dzięki temu działa zarówno Autofac jak i NHibernate, a co najważniejsze nie trzeba nic rekompilować podmieniając zależności u źródła. Należy jednak pamiętać, że sztuczka nie zawsze musi działać. W przypadku braku zgodności między kolejnymi wersjami biblioteki mogą pojawić się inne ciekawsze błędy ;).
Warto również zauważyć, że ta metoda wykorzystywana jest w ASP MVC 2, gdzie w standardowym szablonie dla Web.config istnieje wpis:
<runtime>
  <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
    <dependentAssembly>
      <assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35"/>
      <bindingRedirect oldVersion="1.0.0.0" newVersion="2.0.0.0"/>
    </dependentAssembly>  
  </assemblyBinding>
</runtime>

niedziela, 9 maja 2010

Menu użytkownika w pliku xml cz.1 - Podstawa

Pisząc aplikację w ASP.NET MVC na pewno będziemy borykać się z problemem sposobu przechowywania głównego menu jakie powinno zostać wyświetlone użytkownikowi. Jeżeli opieramy działanie na uniwersalnym menu to oczywiście najlepiej wpisać je w master page i zapomnieć o całej sprawie. Najczęściej jednak użytkownicy mają różne prawa/role, co często ciągnie za sobą brak autoryzacji do pewnych funkcjonalności. Wylądowaliśmy zatem w sytuacji gdzie w zależności praw dostępu użytkownika powinniśmy wygenerować mu inne menu.

Możliwe rozwiązania

Do dyspozycji mamy zatem następujące możliwości zapisania menu:
  • na stałe w kodzie
  • w pliku
  • w bazie danych
Zapis na stałe w kodzie jest oczywiście z definicji zły dla tego typu dynamicznych elementów. Kolejne dwa rozwiązania są już znacznie lepsze (choćby dlatego, że odpada potrzeba ponownej kompilacji i możemy dowolnie edytować informacje). Warto jednak zwrócić uwagę, że do obsługi menu niekoniecznie trzeba używać bazy danych (dużą rolę odgrywa tutaj wielkość aplikacji). Jeżeli dysponujemy stałym zestawem ról dla użytkowników można swobodnie do tego celu wykorzystać plik XML.

Docelowa struktura

Odpowiednik menu użytkownika w xml powinien zawierać elementy:
  • typ/prawa/rolę użytkownika
  • adres docelowy
  • opis wyświetlanego linku
W związku z tym wybrałem następującą strukturę zapewniającą powyższe założenia:
<!--menu.xml-->
<?xml version="1.0" encoding="utf-8" ?>
<menus>
  <menu for="Doctor">
    <item value="Dzisiejsze wizyty" url="/Doctor/Index" />
    <item value="Ostatnia wizyta" url="/Visit/Last" />
    <item value="Wyloguj" url="/User/Logout" />
  </menu>
  <menu for="Recepcionist">
    <item value="Nowy pacjent" url="/Patient/Create" />
    <item value="Wyloguj" url="/User/Logout" />
  </menu>
</menus>
Jak widać w przykładzie mamy dwa osobne menu. Jedno dla lekarza z trzema wpisami i drugie dla recepcjonisty z dwoma. Struktura jest przy okazji na tyle prosta, że bardzo łatwo da się ją odzwierciedlić w kodzie. A jak już o tym mowa, najpierw nieco abstrakcji:
[Flags]
public enum UserRole
{
    Doctor = 1 << 0,
    Recepcionist = 1 << 1,
}

public interface IMenu
{
    IEnumerable<IMenuItem> For(UserRole role);
}

public interface IMenuItem
{
    string Value { get; }
    string Url { get; }
}
Interfejs IMenu reprezentuje już wczytane menu dając dając dostęp do listy elementów IMenuItem czyli informacji opisujących link jaki później zostanie wygenerowany. Dodajmy zatem do tego wszystkiego obsługę xml i jesteśmy w domu.
[XmlRoot("menus")]
public class XmlMenu : IMenu
{
    [XmlElement("menu")]
    public List<XmlRoleMenu> RoleMenus { get; set; }

    public XmlMenu()
    {
        RoleMenus = new List<XmlRoleMenu>();
    }

    public IEnumerable<IMenuItem> For(UserRole role)
    {
        XmlRoleMenu roleMenu = RoleMenus
            .Where(m => m.Role == role)
            .Single();
        return roleMenu.Items;
    }

    public static IMenu LoadFrom(string filePath)
    {
        XmlSerializer serializer = new XmlSerializer(typeof(XmlMenu));
        XmlMenu menu = null;
        using (StreamReader reader = new StreamReader(filePath)) {
            menu = (XmlMenu)serializer.Deserialize(reader);
            reader.Close();
        }
        return menu;
    }
}

public class XmlRoleMenu
{
    [XmlElement("item")]
    public List<XmlMenuItem> MenuItems { get; set; }

    [XmlAttribute("for")]
    public UserRole Role { get; set; }

    public XmlRoleMenu()
    {
        MenuItems = new List<XmlMenuItem>();
    }

    public IEnumerable<IMenuItem> Items
    {
        get { return MenuItems.ConvertAll(item => (IMenuItem)item); }
    }
}

public class XmlMenuItem : IMenuItem
{
    [XmlAttribute("value")]
    public string Value { get; set; }
    [XmlAttribute("url")]
    public string Url { get; set; }
}
Na zakończenie jeszcze przykład użycia wyciągający menu dla doktora
static void Main(string[] args)
{
    IMenu menu = XmlMenu.LoadFrom("menu.xml");
    foreach (IMenuItem item in menu.For(UserRole.Doctor)) {
        Console.WriteLine("{0}\t{1}", item.Url, item.Value);
    }
    Console.ReadLine();
}
Prawda, że proste? Wczytywanie pliku to jednak nie wszystko czego potrzebujemy. Takie menu należy jeszcze osadzić w widoku asp.net mvc, ale o tym już niedługo.

piątek, 16 kwietnia 2010

ConfirmSubmit - HtmlHelper Extensions cz.1

Jedną z tzw dobrych praktyk projektowania aplikacji w ASP.NET MVC jest stosowanie HtmlHelper w przypadkach bardziej skomplikowanego kody widoku. Co można w skrócie nazwać "Keep your views simple". Obiekt Html oczywiście nie realizuje każdego z możliwych scenariuszy stąd powinien być w trakcie kodowania uzupełniany o dodatkowe funkcjonalności pisząc stosowne metody rozszerzające. Stąd też pomysł na niniejszy cykl postów w którym postaram zaprezentować parę przydatnych rozwiązań.

ConfirmSubmit czyli najpierw zapytaj

Tworząc różne formy do edycji/tworzenia/usuwania obiektów wypada czasem upewnić się czy użytkownik na pewno wie co robi ;] Istnieją różne metody, ale chyba najprostsza to wyświetlenie okienka z poziomu javascript:
<input  type="submit" value="Usuń" onclick="return confirm('Czy na pewno?')" />
Zakładając, że w przeglądarce nie mamy wyłączonej obsługi js (co w dobie aplikacji internetowych i Ajaxa jest rzadkością) otrzymamy coś na kształt:


Wszystko pięknie działa, ale jest hmm, niewygodne i do tego nie zgodne z zasadą tworzenia widoków o której wcześniej wspominałem. Stąd też proste rozszerzenie ConfirmSubmit:
public static  MvcHtmlString ConfirmSubmit(
    this HtmlHelper html,
    string value,
    string question)
{
    string submit = String.Format(
        "<input type=\"submit\" value=\"{0}\" onclick=\"return confirm('{1}');\"",
        value,
        question);
    return MvcHtmlString.Create(submit);           
}
Do kompletu jeszcze wywołanie i wszyscy są szczęśliwi:
<%=Html.ConfirmSubmit("Usuń", "Czy na pewno?") %>

poniedziałek, 29 marca 2010

KeyNotFoundException w NHibernate

Podczas pisania mapowań dla NHibernate w moim projekcie, natknąłem się na dość problematyczny błąd. Na samym początku informacje zwrotne nic specjalnego nie mówiły, a przynajmniej nic co od razu nasuwało źródło problemu:

System.Collections.Generic.KeyNotFoundException: Dany klucz nie był obecny w słowniku.
...

W śladzie stosu nie było też dużo lepszych informacji o genezie błędu. Ale do tego każdy programista powinien się do pewnego stopnia przyzwyczaić ;)
Po dokładniejszej analizie okazało się, że chociaż parsowania plików mapować zakończono bezbłędnie to już zbudowanie fabryki sesji powodowało wyżej wymieniony błąd. A wszystkiemu zawinił następujący fragment:

<one-to-one name="Worker" class="Worker" cascade="all" />

Wygląda całkiem niewinnie, z tym, że mapowanie do klasy Worker znajduje się w innym assembly. I stąd cały problem. NHibernate założył istnienie mapowania, którego później nie mógł znaleźć. Rozwiązanie całej sytuacji wydaje się wręcz trywialne:
<one-to-one
    name="Worker" 
    class="Full.Namespace.To.Class.Worker,Assembly.With.Worker"
    cascade="all" />

Pozostaje jeszcze pytanie jak zapobiegać takim sytuacjom w przyszłości? Można nie zrobić nic i przeklinać własną sklerozę, albo zrezygnować z mapowań xml i przerzucić się na Fluent NHibernate gdzie o taki błąd będzie trudniej (w związku z kompilowaną charakterystyką rozwiązania).

niedziela, 28 marca 2010

Let's fight

Kompletnie nie wiem czemu przez tak długi czas zwlekałem z założeniem tego bloga. Może potrzebowałem poczucia większej kompetencji? Trudno powiedzieć. W każdym razie ten właśnie moment można określić jako wielkie otwarcie. Najwyższy czas zacząć dzielić się własnymi przemyśleniami i innymi bzdurami o programowaniu, .NET, inżynierii oprogramowania i wszystkim tym co sprawia, że moje życie uważam za interesujące.
I tak na dobry początek musi się tutaj znaleźć mój ulubiony i jakże trafny życiowy cytat
Experience is the name that everyone gives to his mistakes - Oscar Wild