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. 🙂

Splash screen with shadow

Zawsze chciałem coś takiego zrobić, a do tej pory nie wiedziałem że jest to możliwe. Efekt możecie zobaczyć poniżej, a do zrobienia takiego splash screena użyłem tylko tego co daje nam podstawowy WPF. W zasadzie bazując na tym rozwiązaniu można tworzyć aplikacje, rzucające cień na pulpit (czego bardzo mi brakuje w Windows 8).

 splashscreen

Sam efekt jest bardzo prosty do osiągnięcia i wszystko można zapisać w XAMLu.

<Window x:Class="SplashScreenApp.SplashScreen"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Height="350" Width="225" 
        WindowStyle="None" 
        AllowsTransparency="True" 
        Background="Transparent"
        WindowStartupLocation="CenterScreen"
        ShowInTaskbar="False">
    <Grid Background="Transparent">
        <Grid Margin="15" Background="#252526">
            <Grid.BitmapEffect>
                <DropShadowBitmapEffect Direction="-90" Softness="15" ShadowDepth="1" />
            </Grid.BitmapEffect>
            <StackPanel VerticalAlignment="Center" HorizontalAlignment="Center">
                <TextBlock Text="My App" Foreground="White" FontSize="30"  />
                <TextBlock Text="v.1.0" Foreground="White" FontSize="10"  />
            </StackPanel>
        </Grid>
    </Grid>
</Window>

To co tutaj najważniejsze to:

WindowStyle=”None” – usuwa ramki z okna
AllowsTransparency=”True” – pozwala na przezroczystość wewnątrz okna
WindowStartupLocation=”CenterScreen” – ustawiamy pozycje startową okna na środek ekranu
ShowInTaskbar=”False” – chowamy okno z taskbara

No i najważniejsze czyli tworzymy grida wewnątrz grida, dając lekki margines dla wewnętrznego elementu i dla niego też tworząc DropShadowBitmapEffect. Oszukujemy więc trochę bo nasze okno jest takie duże jak okno wraz z cieniem, ale całość wyszła bardzo ładnie :)

Custom Xml Generator

Dzisiaj chciałbym przedstawić Wam kolejną moją bibliotekę, której kod możecie znaleźć na githubie. Do napisania jej natknął mnie ostatnio problem przed którym stanąłem pisząc jedną z aplikacji. Jak zapewne się domyślacie z nazwy posta, będzie to biblioteka mająca związek z XML-em. Otóż standardowy XmlSerializer, dostępny w .NET-cie pozwala nam w bardzo prosty sposób serializować obiekty do XML-a. Niestety serializer ten ma swoje wady, gdyż jako nazwę elementu w XML-u przyjmuje nazwę parametru w klasie. Można to oczywiście zmienić, niestety trzeba to robić za pomocą atrybutów. Mój problem polegał na tym, że dostałem obiekt z WCF’a, więc i klasa leżała po stronie WCF’a, nie mogłem więc dodać sobie dowolnych atrybutów, tak aby nadać nazwy wybranym elementom. Jedynym sposobem jest tworzyć ręcznie całą strukturę XElement. Dlatego postanowiłem stworzyć cos na kształt generatora XML, który bardzo upraszcza tworzenie struktury XML-a. Poniżej przedstawiam sposób działania tej biblioteki.

Załóżmy, ze otrzymujemy z zewnętrznego serwisu obiekt klasy User. Wszystkie potrzebne modele przedstawione są poniżej.

public class User
{
    public string Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public int Age { get; set; }
    public List<Teacher> Teachers { get; set; }
    public SchoolClass Class { get; set; }
}

public class SchoolClass
{
    public string Name { get; set; }
    public Teacher Tutor { get; set; }
}

public class Teacher
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

Chcąc wygenerować XML-a musimy nadać nazwy interesującym nas właściwościom. Możemy bindować właściwości na 3 sposoby: jako atrybut, jako element oraz jako kolekcję służą do tego kolejno metody: BindAttribute, BindTag i BindCollection. Wszystkie jako pierwszy parametr pobierają Expression, które wskazuje jaki parametr ma zostać zbindowany, zaś drugi parametr to string oznaczający nazwę elementu bądź atrybutu. Dodatkowo metody BindTag moze przyjąć jako drugi parametr innego XmlBuilder’a, który zajmie się dalszym prasowaniem elementu zaś metoda BindCollection potrzebuje jeszcze trzeciego parametru który albo oznacza nazwę tagu pojedynczego elementu w kolekcji, bądź XmlBuilder’a.

var dbBinder = new XmlBuilder("u"); //1
dbBinder.BindAttribute(e => e.FirstName, "fName");  //2
dbBinder.BindTag(e => e.Age, "a"); //3
dbBinder.BindTag(e => e.Class, classBuilder) //4
dbBinder.BindCollection(e => e.Teachers, "ts", "t"); //5
dbBinder.BindCollection(e => e.Teachers, "ts", teacherBuilder) //6
  1. Tworzymy nasz główny XmlBuilder podając w konstruktorze jako parametr nazwę głównego elementu.
  2. Bindujemy właściwość FirstName jako argument o nazwie fName.
  3. Bindujemy właściwość Age jako oddzielny element o nazwie ‘a’.
  4. Bindujemy właściwość Class jako oddzielny element określony przez innego XmlBuildera.
  5. Bindujemy kolekcję Teachers, gdzie sama kolekcja będzie oznaczona przez tag ‘ts’, a każdy jej element przez tag ‘t’.
  6. Bindujemy kolekcję Teachers, gdzie sama kolekcja będzie oznaczona przez tag ‘ts’, a każdy jej element przez teacherBuildera.

Po wywołaniu metody GenerateXml i przesłaniu w jej parametrze obiektu, z którego chcemy stworzyć XMLa dostajemy XElement, który posiada tylko zbindowane atrybuty i tagi. Umieszczam tą bibliotekę mimo, iż wiem, że można jeszcze sporo do niej dopisać. W najbliższym czasie zamierzam dodać bindowanie kolekcji bez opakowania go w nadrzędny element oraz domyślną serializację do xml’a . Jeśli macie jeszcze jakieś pomysły, co by się przydało możecie pisać lub dopisać samemu i wysłać na github :)

Link do github’a

RandomData

Już jakiś czas temu przedstawiałem na grupie .NET pewną niewielką bibliotekę, którą napisałem jednej nocy. Napisanie jej wynikło z potrzeby wygenerowania listy randomowych użytkowników, na których mógłbym pokazać sposób działania LINQ. Dziś znalazłem chwilę czasu, żeby poprawić w niej kilka błędów i opisać jej działanie.

Tak więc, przede wszystkim biblioteka składa się ze zbioru atrybutów, do generowania losowych danych.  Za pomocą tych atrybutów możemy oznaczać pola we własnej klasie i za pomocą specjalnego obiektu Randomizer możemy wygenerować jeden obiekt, bądź całą kolekcję. Przykład działania znajduje się poniżej.

class User
{
 [Identity]
 public int Id { get; set; }

 [RandomFirstName()]
 public string FirstName { get; set; }

 [RandomLastName]
 public string LastName { get; set; }

 [RandomDateTime("1960-01-01", "2013-08-06")]
 public DateTime BornDate { get; set; }

 [RandomText(300)]
 public string Description { get; set; }

 [RandomValue("Poland", "Germany", "France", "USA")]
 public string Country { get; set; }

 public override string ToString()
 {
 var strBuilder = new StringBuilder();
 strBuilder.Append("Id: ").Append(Id).Append(" || ");
 strBuilder.Append("FirstName: ").Append(FirstName).Append(" || ");
 strBuilder.Append("LastName: ").Append(LastName).Append(" || ");
 strBuilder.Append("BornDate: ").Append(BornDate).Append(" || ");
 strBuilder.Append("Country: ").Append(Country);

 return strBuilder.ToString();
 }
}

Klasa, wyposażona w atrybuty z biblioteki RandomData.

var randomizer = new Randomizer<user>();
var users = randomizer.GetRandomCollection(10);

Wygenerowanie kolekcji randomowych użytkowników

Aktualnie w skład biblioteki wchodzą następujące atrybuty:

  • Identity – generuje identyfikator obiektu, można podać z jakim skokiem mają zostac generowane kolejne liczby,
  • RandomBool – generuje wartość true lub false,
  • RandomDateTime – generuje losową datę, można podać wartość minimalną i maksymalną,
  • RandomFirstName – generuje imię z angielskiego słownika imion, można określić, czy ma to być imię męskie czy żeńskie,
  • RandomLastName – generuje nazwisko z angielskiego słownika nazwisk
  • RandomNumber – generuje losową liczbę, można podac zakres,
  • RandomText – generuje napis ‘Lorem ipsum’, można podać ile liter ma być w napisie,
  • RandomValue – losuje jedną z wartości podanych do konstruktora atrybutu.

Zachęcam do pobierania biblioteki i jeśli ktoś ma pomysły na rozwój, bądź komentarze do kodu bardzo proszę o kontakt. Cały kod jest dostępny na githubie, więc możecie go pobierać i jeśli będziecie chcieli go rozwinąć zachęcam do dzielenia się tym również na githubie.

Lazy Loading kontekstu aplikacji

W większościach aplikacji, kontekst ładujemy na starcie. Wtedy mamy najwięcej czasu na zużycie jak największych zasobów procesora przez większą ilość czasu. System ten sprawdza się w grach, zdarzają się jednak aplikacje, które nie zawsze korzystają z załadowanego kontekstu. Warto wtedy zastosować, coś co nazywa się “leniwym ładowaniem”. Oznacza to, że odpowiednie zasoby ładujemy dopiero w chwili kiedy się do nich odwołujemy po raz pierwszy. Dajmy na to, że mamy obiekt ApplicationContext, który zawiera wiele elementów, które aplikacja przy zamykaniu zapisuje w pamięci, natomiast przy uruchamiania wczytuje je z pamięci. Obiekt ten zawiera również jakiś obiekt BigObject, do którego to elementu odwołujemy się tylko w specjalnych sytuacjach, dlatego nie chcemy tego obiektu ładować przy starcie.

public class ApplicationContext
{
    public BigObject BigObject { get; private set; }

    public void LoadContext() {
        BigObject = LoadObject();
    }

    private BigObject LoadObject() {
        //loading object from memory
    }
}

Wystarczy mała zmiana kodu aby stworzyć obiekt, który ładuje się dopiero po odwołaniu się do niego. Ciekawe jest to, jak niewiele kodu trzeba napisać, żeby osiągnąć pożądany efekt.

public class ApplicationContext
{
    private BigObject _bigObject;
    public BigObject BigObject {
        get { return _bigObject ?? (_bigObject = LoadObject()); }
    }

    public void LoadContext() {
    }

    private BigObject LoadObject() {

    }
}

Często jednak wczytanie kontekstu wiąże się z odczytywaniem czegoś z pamięci, więc z operacjami, które mogą trwać sporo czasu jeśli obiekt do załadowania jest sporych rozmiarów. Dzięki temu, że w C# można tak łatwo tworzyć funkcje asynchroniczne, po małej modyfikacji (wystarczy opakować zmienne w klasę Task) możemy stworzyć obiekt, który wczytywać się będzie asynchronicznie.

public class ApplicationContext
{
    private Task<BigObject> _bigObject;
    public Task<BigObject> BigObject {
        get { return _bigObject ?? (_bigObject = LoadObject()); }
    }

    public void LoadContext() {
    }

    private Task<BigObject> LoadObject() {
        //loading object from memory
    }
}

W tym przypadku należy pamiętać, aby przed odwołaniem się do właściwości BigObject postawić słówko kluczowe await.

var bo = await appContext.BigObject;

Jednak czekać na wczytanie danych będziemy tylko za pierwszym razem, każde kolejne odwołanie się do BigObject zwróci nam jego wartość automatycznie.

Computer Auto Shutdown

Wychodzisz na imprezę, a została Ci godzina do pobrania Windows 8? Możesz zostać w domu, zostawić włączonego kompa na całą noc albo napisać na szybko program, który Ci go wyłączy za godzine.

Ja wybrałem tę ostatnią opcję. Mając więc nadzieję, że komuś się może ta apka przyda i dzięki mnie nie spóźni się na koncert, wrzucam ją na swojego skydrive.

Link do pobrania: http://sdrv.ms/TNGkva

Subtitles Viewer

Postanowiłem udostępnić wczesną wersję programu do wyświetlania napisów. Dzięki niemu można wyświetlić na ekranie napisy, bez potrzeby używania odtwarzaczy wideo. Możemy więc zgrać napisy na przykład z filmem oglądanym online, lub też, jeśli nasz program nie obsługuje odtwarzania napisów. Na razie aplikacja obsługuje jedynie pliki srt.

Mam jeszcze wiele rzeczy w TODO List, co do tego programu, ale jeśli ktoś użyje aplikacji i natknie się na rażący błąd, prosiłbym o kontakt

Aplikacje można pobrać stąd. Do uruchomienia jej potrzebny jest .NET Framework w wersji 4 lub wyższej.