Typowe błędy programistów.

Pisałem już kiedyś o błędach popełnianych przez młodych programistów. Byłem wtedy na początku swojej programistycznej kariery i myślałem, że już dużo wiem. Teraz wiem ile jeszcze nie wiem 😉 Niemniej jednak z większością z tamtych punktów zgadzam się do dziś. Postanowiłem więc, po kilku latach jeszcze raz zebrać kilka najczęstszych błędów, które zauważyłem już w aplikacjach enterprise. Poniższe punkty nazwałem błędami architektonicznymi, czyli odnoszącymi się bezpośrednio do kodu programu. Jak wiadomo praca programisty składa się jeszcze z wielu innych aktywności, ale o nich napiszę w innym poście.

1. Nadużywanie statycznych metod

Statyczne metody nie powinny być zbyt często używane. Programiści często idą na skróty i dopisują funkcje statyczne, które zawierają logikę biznesową bądź zmieniają stan aplikacji. Logika biznesowa w metodach statycznych to najgorsze, co może być. Dlaczego? Dlatego, że dla takiej metody jest bardzo trudno napisać jakikolwiek test jednostkowy. Metody statyczne powinny być używane dla bardzo prostych helperów, służących tylko do prostych obliczeń, niewymagających testowania. Chodź i tak do tego lepiej nadają się extension methods. Przykład błędnego użycia metody statycznej znajdziecie poniżej.

var user = User.GetUserById(userId);

public class User
{
    public static User GetUserById(int userId)
    {
        //Implementation
    }
}

Zamiast tego powinno się używać serwisów, które mogą być wstrzyknięte poprzez DI i z łatwością zmockowane.

2. Niekorzystanie z interfejsów

Punkt ten często wiąże się z poprzednim, gdyż metod statycznych nie można umieścić w interfejsie. Często myślimy, że jeśli aktualnie do dostępu do bazy używamy ADO.NET, to tak będzie już zawsze i tworzymy metody pobierające/zapisujące dane w klasie, do której odwołujemy się bezpośrednio. Niestety takie podejście ma bardzo wiele wad. Przede wszystkim, tak samo jak w przypadku metod statycznych bardzo ciężko jest testować taki kod. Nie można stworzyć żadnego mocka, który symulowałby nam dostęp do bazy danych. Drugim problemem z tym związanym jest brak możliwości zmiany aktualnie używanego rozwiązania. Jeśli już w całym programie używa się bezpośrednich połączeń do metod z ADO.NET, to nie przepiszemy wszystkiego na Entity Framework w jeden tydzień.

To co powinno się robić, to używać interfejsów zawsze. Nawet jeśli sposób implementacji w danym momencie jest tylko jeden.

3. Wrzucanie funkcji do jednego worka

Jeśli myślimy zbyt obiektowo, nadchodzi czas gdy biznes przerasta nasze obiektowe pojęcie. Wtedy właśnie zdajemy sobie sprawę, że nasza klasa User trochę się rozrosła i każda operacja mająca cokolwiek wspólnego z użytkownikiem, nie powinna znajdować się w tej jednej klasie. Często osoby, które nie stosują żadnych wzorców architektonicznych, popadają w pułapkę klas Bogów. Zamiast myśleć o obiektach jako encjach bazodanowych, należy pomyśleć o obiektach w kontekście biznesu. Bardzo przydatna w zrozumieniu tego zagadnienia jest książka Domain Driven Design. Pomocne w rozdzieleniu funkcji mogą być również wzorce projektowe, takie jak CQRS.

4. Wykorzystywanie tych samych DTO w wielu, niepowiązanych miejscach.

Najtrudniejsze w programowaniu jest wymyślanie nazw. Dlatego często idziemy na łatwiznę i nadajemy nic nie znaczące nazwy. Załóżmy, że mamy klasę do edycji użytkownika, więc nazwiemy ją UserDto. Po kilku miesiącach zapominamy, piszemy inną funkcjonalność i dajmy na to, że musimy wyświetlić wszystkich użytkowników z daną rolą. Jakiej klasy użyjemy? Prawdopodobnie UserDto, pomijając, że klasa ta posiada znacznie więcej pól niż potrzeba przy prostym wyświetleniu użytkowników. Jest to kolejny błąd, który ciężko naprawić, bo im więcej odwołań do danej klasy tym ta klasa jest trudniej edytowalna.

5. Pisanie nieczytelnych funkcji

Temat pisania nieczytelnych funkcji, poruszany jest w wielu wątkach i opisywany w niejednej książce, mimo to myślę, że wciąż są problemy z czytelnym pisaniem funkcji. Przede wszystkim problemy występują w instrukcjach warunkowych, które potrafią być całkiem sporym kawałkiem logiki. Istnieje nawet kampania anty-ifowa, której członkowie namawiają do programowania bez użycia instrukcji if oraz switch. Nie jestem za popadaniem ze skrajności w skrajność i wiem, że czasami użycie if jest czytelniejsze niż przerobienie architektury, niemniej jednak należy zwrócić uwagę na to, czy inni programiści zrozumieją, kiedy kod wchodzi do bloku if.

Poniższy przykład pokazuje świetnie ewolucje oprogramowania. Pierwsza linijka jest akceptowalna, bo to tylko jeden warunek, do tego w miarę czytelny i logiczny- ma pieniądze, więc może kupić. Po dodaniu funkcjonalności ról, umieszczamy kolejny warunek i już zaczyna się robić mało czytelnie. W tym momencie powinniśmy opakować cały warunek w jakąś ładnie brzmiącą metodę. Niestety wiele razy widziałem, że ostatni krok jest pomijany i zostaje taki tasiemiec z instrukcji warunkowych, nad którym trzeba spędzić kilka minut, żeby go zrozumieć.

// this is readable
if (user.AccountBalance >= productCost) { }

// we add Roles to program and code is ugly
if (user.AccountBalance >= productCost && user.Roles.Any(r => r.AllowInAppPurchases)) { }

// after refactor it is better
if (user.CanBuyProduct(productCost)) { }

To tylko kilka uwag, które zamierzałem przedstawić, żeby przestrzec innych. Często w pośpiechu sam zapominam o niektórych zasadach, ale mam nadzieję, że dzięki opisaniu ich tutaj, sam zacznę się bardziej pilnować. 🙂

Czemu używać git-a nawet jeśli nie potrzebujesz zdalnego repozytorium?

Jestem przekonany, że wielu z was (programistów) posiada swoje własne projekty, rozwijane „po pracy”. Proces, w jakim nad nimi pracujecie jest prawdopodobnie zupełnie inny, niż ten wyuczony w pracy. Jeśli byłby taki sam, to nie byłoby frajdy z rozwijania czegoś własnego, prawda? Mimo, że istnieje wiele darmowych systemów (bitbucket, visualstudio) pozwalających na założenie swojego własnego systemu kontroli wersji i przechowywania swojej aplikacji w chmurze, to nie zawsze wydaje się warte zaśmiecanie internetu swoimi małymi projekcikami, a prawdopodobieństwo, że padnie nam dysk jest raczej niewielkie. Wtedy większość osób po prostu umieszcza swoją aplikację w folderze i pracuje nad nią, czasami nawet tygodniami. Zdarzają się czasami nawet chwile, że dodając nową funkcjonalność zapędzamy się na tyle w programowaniu, że czasami chcielibyśmy wrócić do wcześniejszej wersji, ale cóż, przecież Windows nie wersjonuje plików. Otóż dla tych, którzy spotkali się z taką sytuacją, mam wskazówkę, jak nie powtórzyć tego błędu w przyszłości. Tą wskazówką jest jedna komenda odpalona w wierszu poleceń.

git init

8 znaków (licząc spacje) i tworzymy własne repozytorium. Nie potrzebujemy konta, ani połączenia z internetem. Jedyne, co potrzebujemy to zainstalowany git na naszym komputerze. Dla Windowsa możecie go pobrać ze strony git-scm. Przed pierwszym commitem polecam dodać plik .gitignore, który wykluczy z repozytorium niepotrzebne pliki takie jak pliki exe, czy dll. Możecie pobrać już wygenerowany plik pod swoje własne środowisko na stronie gitignore.io. Po dodaniu tego pliku wpisujemy komendę:

git add .
git commit -a -m "this is init commit"

Możemy się już cieszyć swoim własnym prywatnym repozytorium. Więcej informacji, co możecie zrobić z takim repozytorium znajdziecie w darmowej książce Pro Git.

Podglądanie historii commitów za pomocą git log

Dodam tylko, że jeśli w pewnym momencie będziecie chcieli swoje prywatne repozytorium wrzucić na githuba lub inny serwer i podzielić się ze znajomymi, nie ma nic prostszego.

git remote add origin 'remote-repository-URL'
git remote -v
git push origin master

Pozdrawiam i życzę miłego korzystania z gita 🙂

Szybki sposób na Dependency Injection w ASP.NET WebAPI

Konfiguracja pakietu DI, takiego jak Ninject zawsze kojarzyła mi się z wieloma problemami, żmudną konfiguracją i tworzeniem DependencyResolvera. Tak było dopóki nie odkryłem wspaniałej paczki nugetowej, która załatwia wszystko za developera. Wystarczy zainstalować paczkę Ninject.WebApi.DependencyResolver.

Install-Package Ninject.WebApi.DependencyResolver

Paczka ta dociągnie wszystkie zależności w tym paczki Ninject-owe. Przed odpaleniem projektu należy jeszcze otworzyć plik: App_Start/NinjectWebCommon.cs i w metodzie CreateKernel(), tuż przed return dodać wpis:

System.Web.Http.GlobalConfiguration.Configuration.DependencyResolver = new Ninject.WebApi.DependencyResolver.NinjectDependencyResolver(kernel);

Zastanawiacie się co dalej? To wszystko, możecie już korzystać z wstrzykiwania zależności, poprzez konstruktor bądź poprzez atrybut Inject. Więcej o bibliotece Ninject znajdziecie tutaj.

TIP: Nie wiem czemu, ale ja po instalacji jeszcze błąd, że nie można znaleźć biblioteki Ninject.dll w wersji 3.2.0.0. Spojrzałem i rzeczywiście zainstalowała się wersja 3.0.0.0. Dlatego musiałem jeszcze zaktualizować tą bibliotekę:

Update-Package Ninject

Po tym już wszystko działało poprawnie.

Fixing linq typings to work with es6

I’m sure that everyone knows linq. It is library which is very useful and has version not only in C# but also in JavaScript. Using linq in javascript may be useless few years ago but now it’s common problem that we need do some buisiness logic operations in js. I worked on few projects where i used Linq in javascript but lately i wanted to use it in Aurelia which is using es6 (ecmascript 6). First i had a problem how to use npm library in Aurelia which use jspm. I asked about that in stackoverflow and get the answer. But then i started using typescript and everything f**ed up.

First i needed typings for linq, so i googled linq.d.ts and get first answer. I added this to my project and created first typescript class. What happened? Visual studio shows error that cannot find module ‚linq’.

I thought that this is something with npm package. But when i wrote the same import in javascript it worked fine. So i checked linq.d.ts file and tried to changed it to meet the import statement. I didn’t changed any interfaces to be sure that i will have correct typings, but what i changed was module declaration. Original module declaration was:

declare module linq

I changed that to:

declare module 'linq'

Error about module disappear but i got another error.

So i had to move Enumerable variable into module declaration and make this variable default export.

declare module 'linq' {
    
    //Interfaces...

    var Enumerable: EnumerableStatic;
    export default Enumerable;
}

That helped, there is no error anymore. It should work for most of you, but because of my clumsiness i didn’t noticed that linq in some version changed their functions names. In some older version it was PascalCase (with first letter is capital ex. SingleOrDefault()) and now it is the same as all javascript functions so camelCase (ex. singleOrDefault()). So again i had to change my typings. I found the newest one at linqjs codeplex page. Did the same changes (with module name and default export) again and now i can use linq in typescript with ecmascript 6 features.

If anyone interested here you can download linq.d.ts file.

Windows Store Apps – Typescript

Dzisiaj chciałbym przedstawić swój pierwszy videotutorial, w którym kontynuuje tematykę Windows Store Apps pisanych w HTML5 i JavaScript. Myślałem już jakiś czas temu, aby zamiast pisania artykułów nakręcić film, na którym można dużo więcej pokazać i w końcu mi się udało. Myślę, że jest to lepsza forma przekazywania wiedzy i liczba błędów, które można popełnić odtwarzając kroki jest również o wiele mniejsza. Zachęcam do obejrzenia i komentowania 🙂

Kod źródłowy projektu po modyfikacji znajdziecie tutaj.

MVVM dla aplikacji Windows Store pisanych w JavaScript

Sporo się mówi/pisze o zastosowaniu wzorca MVVM w aplikacjach Windows Store i Windows Phone (teraz to chyba jest pod jedną nazwą ;)). Istnieje kilka bibliotek, jak na przykład MVVMLight, które pomagają w implementacji tego wzorca w XAML-u i C#. Niewiele się jednak mówi o aplikacjach Windows Store pisanych w języku JavaScript. Wydaje mi się, że niewiele osób kiedykolwiek próbowało stworzyć aplikacje dla Windows Phone w tym języku. W zasadzie to się nie dziwię, JavaScript to dziwny język i można go kochać lub nienawidzić. Niemniej jednak chciałbym pokazać, że pisanie aplikacji Windows Store w JS nie jest takie złe a w tym poście przede wszystkim chciałbym pokazać, że zastosowanie wzorca MVVM jest możliwe i jest bardzo proste.

1. Zacznijmy od stworzenia nowego projektu, polecam przy tworzeniu wybrać Navigation App, ponieważ stworzy nam się prawie pusta aplikacja, ale ze wszystkimi potrzebnymi rzeczami. Jeśli zaczęlibyśmy od Blank App, musielibyśmy posiedzieć jeszcze trochę nad poukładaniem stron, tak, aby można było zacząć programować.

winjs1

2. Język XAML jest wręcz stworzony do stosowania wzorca MVVM, wbudowany w ten język mechanizm bindingu pozwala nam stosować MVVM bez potrzeby dodawania jakiekolwiek zewnętrznej biblioteki. HTML niestety nie jest taki wspaniały, dlatego musimy wesprzeć się dodatkową biblioteką. Istnieje kilka bibliotek, których możemy użyć, ja natomiast najpewniej czuje się z biblioteką knokcoutjs, dlatego to w niej pokażę przykład. Bibliotekę tą możemy dodać za pomocą Nugeta, wyszukujemy knockoutjs i klikamy Install. Po tej operacji w katalogu Scripts powinny się nam pojawić dwa pliki knockout-{nr.wersji}.js i knockout-{nr.wersji}.debug.js.

winjs2

 

3. Otwieramy plik default.html i dodajemy link do pliku knockout-x.x.x.js, najlepiej przeciągając plik z solution explorera, w odpowiednie miejsce w dokumencie, ewentualnie wpisując ręcznie:

 <script src="Scripts/knockout-3.2.0.js"></script>

4. Tworzymy sobie nowy katalog o nazwie viewmodels. W tym katalogu będziemy przechowywać nasze klasy ViewModel. Dodajemy pierwszy plik js o nazwie MainViewModel.js. W środku, zaimplementujemy bardzo prostą logikę, wraz z dwoma polami do odczytu oraz metodą wywołującą MessageBox.

var MainViewModel = function (firstName, lastName) {
    this.firstName = firstName;
    this.lastName = lastName;

    this.sayHello = function () {
        var msg = new Windows.UI.Popups.MessageDialog("Hi! my name is " + this.firstName + " " + this.lastName);
        msg.showAsync();
    }
}

5. Przechodzimy do pliku home.html, i w miejsce „Content goes here” wklejamy prosty binding imienia i nazwiska oraz binding funkcji sayHello.

Imie:  
Nazwisko:

6. Zostajemy jeszcze w tym samym pliku i w znaczniku head, dodajemy odwołanie do naszego skryptu z MainViewModel, tak samo jak w punkcie 3 przeciągając plik MainViewModel.js w odpowiednie miejsce.
7. Zostaje nam na sam koniec tylko stworzyć obiekt viewmodel i podbindować pod odpowiedni widok. Otwieramy więc plik home.js i w funkcji ready dodajemy dwie linijki:

var vm = new MainViewModel("Jan", "Kowalski");
ko.applyBindings(vm);

8. Efekt, jaki powinniśmy dostać to aplikacja wyświetlająca Jan Kowalski oraz przycisk, po naciśnięciu którego powinien pojawić się MessageBox.

winjs3

 

Jeśli ktoś wcześniej nie miał do czynienia z biblioteką Knockoutjs, to polecam stronę http://knockoutjs.com/ na której znajdziecie tutorial, dzięki któremu w bardzo szybki sposób można nauczyć się podstaw tej biblioteki. Oczywiście można tutaj stosować TwoWay binding, oraz wiele innych ciekawych rzeczy, które odkryjecie na pewno przechodząc tutorial.

Kod źródłowy do pobrania

Interceptor dla WCF-a

Ostatnio programując w javie (sic!) spodobała mi się jedna rzecz, którą chciałem wprowadzić do swoich projektów w .NET, a mianowicie Interceptor. Pozwala on w prosty sposób udekorować metodę własną logiką i to wszystko za pomocą jednej adnotacji. Podobną funkcję spełniają ActionFilter w ASP.NET MVC. Ja natomiast chciałem dodać warstwę logowania do metod WCF-a, więc zacząłem szukać. Niestety nie znalazłem nic co działałoby równie dobrze co Javowe interceptory, więc postanowiłem napisać coś samemu.

Przede wszystkim chciałem aby taki interceptor można było zakładać właśnie za pomocą atrybutu (albo na pojedynczą operację albo na cały serwis). Rezultatem mojej twórczości została taka oto klasa.

public class WCFInterceptor : Attribute, IOperationBehavior, IServiceBehavior
{
    private Type actionType;

    public WCFInterceptor(Type actionType)
    {
        if (actionType == null)
            throw new ArgumentException("Type cannot be null");

        if (!actionType.GetInterfaces().Contains(typeof(IParameterInspector)))
            throw new ArgumentException("Type must implement IParameterInspector");

        this.actionType = actionType;
    }

    #region IOperationBehavior Members

    //...

    void IOperationBehavior.ApplyDispatchBehavior(OperationDescription operationDescription, 
        DispatchOperation dispatchOperation)
    {
        try
        {
            IParameterInspector actionInstance = (IParameterInspector)Activator.CreateInstance(this.actionType);
            dispatchOperation.ParameterInspectors.Add(actionInstance);
        }
        catch
        {
            throw new ArgumentException(string.Format("Could not create instance of type: {0}", this.actionType.Name));
        }
    }

    #endregion

    #region IServiceBehavior Members

    // ... 

    void IServiceBehavior.ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
    {
        foreach (ServiceEndpoint endpoint in serviceDescription.Endpoints)
        {
            foreach (var operation in endpoint.Contract.Operations)
            {
                operation.Behaviors.Add(this);
            }
        }
    }

    #endregion
}

Jako parametr w konstruktorze przyjmuje on typ klasy, która musi implementować IParameterInspector, jest to prosty interfejs, który wymaga zaimplementowania dwóch metod: AfterCall oraz BeforeCall. Główną przyczyną, dla której chciałem stworzyć taki interceptor było logowanie wejścia i wyjścia z operacji, dlatego dodatkowo stworzyłem klasę LogInterceptorAction, która może zostać przesłana w konstruktorze atrybutu.

public class LogInterceptorAction : IParameterInspector
{
    private readonly ILog log = LogManager.GetLogger(typeof(LogInterceptorAction));

    public void AfterCall(string operationName, object[] outputs, object returnValue, object correlationState)
    {
        log.Debug(string.Format("EXIT {0} returns {1}", GetMethodnameWithParameters(operationName, outputs), returnValue));
    }

    public object BeforeCall(string operationName, object[] inputs)
    {
        log.Debug(string.Format("ENTER {0}", GetMethodnameWithParameters(operationName, inputs)));
        return null;
    }

    private string GetMethodnameWithParameters(string operationName, object[] outputs)
    {
        var sBuilder = new StringBuilder();
        sBuilder.Append(operationName).Append("(");
        if (outputs != null)
        {
            sBuilder.Append(string.Join(",", outputs));
        }
        sBuilder.Append(")");

        return sBuilder.ToString();
    }
}

Aby udekorować nasz serwis takim interceptorem wystarczy obok atrybutu OperationContract lub ServiceContract, dodać nasz własny WCFInterceptor

[ServiceContract]
[WCFInterceptor(typeof(LogInterceptorAction))]
public interface IService1
{
    //...
}

lub jeśli chcemy zastosować to tylko do jednej operacji

[OperationContract]
[WCFInterceptor(typeof(LogInterceptorAction))]
string GetData(int value);

Zaimplementowane podejście trochę różni się od tego co jest w javie, przede wszystkim nie mamy tutaj podejścia jak z metody szablonowej, gdzie możemy wykonać operacje WCF-a w dowolnym momencie. Możliwe, że rozwiązanie mojego problemu już istnieje i niepotrzebnie wymyślałem koło od nowa, tak więc jeśli znacie jakąś klasę/bibliotekę, która mogłaby się przydać w przyszłości, dajcie znać w komentarzach. 🙂

Autentykacja za pomocą Azure Mobile Services

Tworząc trzecią część artykułu o tworzeniu logiki współdzielonej stwierdziłem że materiału będzie trochę za dużo jak na jeden post, dlatego postanowiłem część zapisać w tym poście. O czym będzie dzisiaj? Przede wszystkim o tym jak rozszerzyć możliwość autentykacji jaką daje nam WAMS (Windows Azure Mobile Services). Czasami bowiem poza tym że chcemy wiedzieć czy użytkownik posiada konto w danym serwisie, chcemy również poznać jego imię, czy zapisać sobie jego avatar.

Pierwszym co musimy zrobić to wygenerować sobie odpowiednie klucze do serwisu z którego będziecmy chcieli skorzystać. Microsoft zapewnia nam instrukcje jak wygenerować te klucze dla:

* – mała wskazówka: po wygenerowaniu kluczy wchodzimy w zakładkę “Status & Review” i zaznaczamy checkbox “Do you want to make this app and all its live features available to the general public?” na Tak. Pozwoli to na logowanie innych użytkowników, nie tylko użytkownika, który stworzył aplikacje ;)

** – z powodu że api google się zmieniło, tutorial jest nieaktualny, możliwe że go zaktualizują w przyszłości.

Mając już wygenerowane klucze możemy dodać autentykacje do naszej aplikacji. Jeśli wszystko pójdzie dobrze i użytkownik zaloguje się na poprawne konto dostaniemy jego UserId. Możemy więc dodać go sobie do naszej bazy danych przy okazji wyciągając inne dane. Przechodzimy zatem do naszego panelu Azure Mobile Services, tworzymy nową tabelkę Users i dajemy pozwolenie na dodawanie tylko zalogowanym użytkownikom.

blog-wams-users-table

Następnie Dodajemy sobie kolumny Name oraz AvatarUrl (obie są stringami). Na koniec przechodzimy na zakładkę Script i podmieniamy skrypt Insert na przedstawiony poniżej.

function insert(item, user, request) {
    var req = require('request');

    var getFacebookPic = function()
    {
        var facebookId = user.userId.replace("Facebook:", "");
        var facebookReqOptions = {
                    uri: 'http://graph.facebook.com/' + facebookId + '?fields=picture.type(small)',
                    headers: { Accept: "application/json" }
                };
        req(facebookReqOptions, function(err, resp, body){
            var jsonBody = JSON.parse(body);
            item.AvatarUrl = jsonBody.picture.data.url;
        });
    }

    var getMicrosoftPic = function(userId){
        var link = 'https://cid-[userId].users.storage.live.com/users/0x[userId]/myprofile/expressionprofile/profilephoto:Win8Static,UserTileMedium,UserTileStatic';
        item.AvatarUrl = link.replace(/[userId]/g, userId);       
    }

    item.Nick = "<unknown>"; // default
    user.getIdentities({
        success: function (identities) {
            var msToken;
            var url = null;
            var oauth = null;
            if (identities.google) {
                var googleAccessToken = identities.google.accessToken;
                url = 'https://www.googleapis.com/oauth2/v3/userinfo?access_token=' + googleAccessToken;
            } else if (identities.facebook) {
                var fbAccessToken = identities.facebook.accessToken;
                url = 'https://graph.facebook.com/me?access_token=' + fbAccessToken;
            } else if (identities.microsoft) {
                var liveAccessToken = identities.microsoft.accessToken;
                url = 'https://apis.live.net/v5.0/me/?method=GET&access_token=' + liveAccessToken;
                msToken = liveAccessToken;
            } else if (identities.twitter) {
                var userId = user.userId;
                var twitterId = userId.substring(userId.indexOf(':') + 1);
                url = 'https://api.twitter.com/1.1/users/show.json?user_id=' + twitterId;
                var consumerKey = process.env.MS_TwitterConsumerKey;
                var consumerSecret = process.env.MS_TwitterConsumerSecret;
                oauth = {
                    consumer_key: consumerKey,
                    consumer_secret: consumerSecret,
                    token: identities.twitter.accessToken,
                    token_secret: identities.twitter.accessTokenSecret
                };
            }

            if (url) {
                var requestCallback = function (err, resp, body) {
                    if (err || resp.statusCode !== 200) {
                        console.error('Error sending data to the provider: ', err);
                        request.respond(statusCodes.INTERNAL_SERVER_ERROR, body);
                    } else {
                        try {
                            var userData = JSON.parse(body);
                            item.Nick = userData.name;
                            if (identities.twitter) {
                                item.AvatarUrl = userData.profile_image_url;
                            } else if (identities.facebook){
                               getFacebookPic();
                            } else if (identities.microsoft){
                               getMicrosoftPic(userData.id);
                            }
                            request.execute();
                        } catch (ex) {
                            console.error('Error parsing response from the provider API: ', ex);
                            request.respond(statusCodes.INTERNAL_SERVER_ERROR, ex);
                        }
                    }
                }

                var reqOptions = {
                    uri: url,
                    headers: { Accept: "application/json" }
                };
                if (oauth) {
                    reqOptions.oauth = oauth;
                }
                req(reqOptions, requestCallback);
            } else {
                // Insert with default user name
                request.execute();
            }
        }
    });

}

Skrypt ten zapisuje nazwę użytkownika w polu Name, oraz dla autentykacji poprzez Facebooka, Twittera oraz Microsoft Account zapisuje link do avatara. Niestety dla Google Account nie mogłem wygenerować kluczy więc nie miałem jak sprawdzić skąd wyciągnąć avatar.

Tworzenie logiki współdzielonej cz. 2 – MVVM

W poprzednim poście mówiłem o nowym rodzaju biblioteki, jakim jest Portable Class Library. Microsoft przedstawił nam takie rozwiązanie, jeśli chodzi o tworzenie logiki aplikacji mobilnych, do którego musimy się dostosować. W poniższym poście pokaże na przykładzie, że nie jest to takie straszne, a tworzenie logiki za pomocą PCL’a może być całkiem miłe.

Wspominałem wcześniej, że do PLC-a możemy przenieść część naszych ViewModeli, jeśli są one jednakowe w obu aplikacjach (Windows Phone, Windows 8). Możemy to zrobić w prosty sposób: dodać referencję w obu projektach do biblioteki PCL. Postaram się jednak pokazać na przykładzie jak dodawać ViewModele za pomocą prostego wzorca Inversion of Control, jakie daje nam biblioteka MVVMLight.

Na początek tworzymy sobie solucję, która zawierać będzie 3 projekty:

  • PCL – biblioteka  wspólna
  • PCL.WP8 – aplikacja Windows Phone
  • PCL.W8 – aplikacja Windows Store

W ostatnich dwóch projektach dodajemy referencję do pierwszego i od tej pory możemy zacząć dewelopować.

Części wspólne

Zacznijmy więc od projektu PCL. W poprzedniej części wkleiłem obrazek który pokazywał, że w tym rodzaju biblioteki powinniśmy przechowywać modele, viewmodele i interfejsy ujednolicające API Windows Store oraz Windows Phone. Dajmy na to, że chcemy w jednym z naszych ViewModeli zapisywać i odczytywać kontekst naszej aplikacji z pliku. Na Windows Phone plik taki będziemy przechowywać w IsolatedStorage, natomiast w aplikacji Windows Store, w Storage’u dla aplikacji. W takim razie możemy stworzyć sobie interfejs IDataSerializer, który zserializuje nam obiekt konkretnej klasy.

public interface IDataSerializer
{
    Task<ApplicationContext> LoadData();

    void SaveData(ApplicationContext context);
}

Ok, problem się zaczyna, kiedy chcemy w biblitece PCL rzeczywiście coś zapisać. Przecież nie możemy napisać var serializer = new IDataSerializer(). Potrzebujemy konkretnej implementacji. Do tego właśnie posłuży nam wzorzec Inversion Of Control. Wystarczy, że dodamy do wszystkich naszych projektów jakie stworzyliśmy do tej pory bibliotekę MVVM Light Libaries only (PCL). 


mvvmlightnuget

Uważajcie żeby nie dodawać innych bibliotek MVVM Light, bo potem mogą wyniknąć problemy z referencjami.

Następnie mając już dodane wszystkie referencje możemy pobrać konkretną instancję naszego interfejsu w taki oto sposób:

var dataSerializer = ServiceLocator.Current.GetInstance<IDataSerializer>();
var appContext = await dataSerializer.LoadData();

Na chwilę obecną nie interesuje nas czy to zapisuje do pamięci telefonu czy na dysk. Pobieramy konkretną implementację i ładujemy dane. Pierwsze pytanie, jakie się nasuwa to: “Ok, ale gdzie my to wstrzykujemy”. Właśnie, żeby wstrzyknąć konkretną implementację przechodzimy na początek do aplikacji Windows Phone i wykonujemy następujące kroki:

  1. Tworzymy klasę ViewModelLocator
  2. Dodajemy statyczny konstruktor dla tej klasy i w środku wstrzykujemy naszą własną implementację zdefiniowanego wcześniej interfejsu (1):
    static ViewModelLocator()
    {
        ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
        SimpleIoc.Default.Register<IDataSerializer, WPDataSerializer>();  //1
    }
  3. W pliku App.xaml dodajemy do Application Resources definicję naszej klasy, aby przy starcie aplikacji wykonał się nam konstruktor:
    <Application.Resources>
        <vm:ViewModelLocator x:Key="Locator" />
    </Application.Resources>

Identyczne kroki powtarzamy dla aplikacji Windows Store, przy czym podajemy mu inną implementację naszego interfejsu.

Po wykonaniu tych 3 kroków wszystko jest już gotowe. Jeśli uruchomimy aplikacje Windows Phone, to logika w PCLu skorzysta z WPDataSerializer, natomiast jeśli uruchomimy aplikację Windows Store, ServiceLocator zwróci nam obiekt W8DataSerializer (czy jak tam go sobie nazwiemy).

ViewModele

Tutaj praktycznie kończy się nasza wiedza potrzebna do zaimplementowania PCL-a. Natomiast chciałbym również pokazać jak MVVM Light pozwala na wstrzykiwanie ViewModeli do widoków. Czasami chcemy aby dla jednej strony powstała tylko jedna instancja ViewModel i za każdym wejściem na tę stronę otwierał się ten sam obiekt, a nie nowy. Ja wcześniej to załatwiałem poprzez tworzenie statycznej właściwości w App.xaml.cs i pobierania ViewModelu stamtąd. Natomiast mając już zaimplementowane IoC możemy to zrobić w trochę inny sposób. Sposób ten działa zarówno dla WP jak i W8:

  1. W naszym pliku ViewModelLocator dodajemy (1 – jeśli chcemy stworzyć jedną instancję ViewModelu, 2 – jeśli chcemy tworzyć zawsze nową instancję)
    //1
    public HomeViewModel Home
    {
        get
        {
            return ServiceLocator.Current.GetInstance<HomeViewModel>();
        }
    }
    
    //2
    public HomeViewModel Home
    {
        get
        {
            return new HomeViewModel();
        }
    }
  2. W widoku dodajemy do głównego elementu (PhoneApplicationPage – WP, Page – W8) taki atrybut:
    DataContext="{Binding Home, Source={StaticResource Locator}}"

PS. Zacząłem tworzyć bibliotekę wystawiającą interfejsy dla podobnych części systemu Windows Phone oraz Windows 8, natomiast podczas jej pisania zauważyłem, że oba te system tak bardzo się jeszcze różnią, że nie potrafię wielu rzeczy połączyć w jeden interfejs. Możliwe, że niedługo dopiszę brakujące rzeczy i wtedy wrzucę ją na github’a.

PS2. Mam w planach nakręcić filmik pokazujący w trochę bardziej praktyczny sposób połączenie ze sobą tych dwóch aplikacji, natomiast brak czasu i brak pomysłu na taki filmik trochę mnie zatrzymuje.