Pokazywanie postów oznaczonych etykietą servicestack. Pokaż wszystkie posty
Pokazywanie postów oznaczonych etykietą servicestack. Pokaż wszystkie posty

poniedziałek, 10 marca 2014

[NoSQL] Redis: klient C#

Twórcy Redis-a nie dostarczają oficjalnych sterowników do C#, ale protokół komunikacji z bazą jest na tyle prosty, że mamy do wyboru kilka driverów Redisa dla C# jak i dla wielu innych języków. W tym poście przedstawione zostaną możliwości klienta będącego częścią frameworka ServiceStack. Driver instalujemy tradycyjnie NuGetem.



Najważniejszą klasą jest RedisClient implementująca kilka ważnych interfejsów. Poza IDisposable są to:
  • IRedisNativeClient - odpowiadający natywnemu API Redis-a
  • ICacheClient - wprowadza warstwę abstrakcji dla cache'y, wykorzystywany wewnątrz ServiceStacka
  • IRedisClient  - wyższy poziom abstrakcji
  • IRedisTypedClient - operacje na typach, dodatkowe funkcjonalności
Przykłady:

using (IRedisNativeClient client = new RedisClient())
{
    client.Set("urn:messages:1", Encoding.UTF8.GetBytes("Hello C# World"));
}

using (IRedisNativeClient client = new RedisClient())
{
    var result = Encoding.UTF8.GetString(client.Get("urn:messages:1"));
    Console.WriteLine(result);
}

using (IRedisClient client = new RedisClient())
{
    var customerNames = client.Lists["urn:customernames"];
    customerNames.Clear();
    customerNames.Add("Joe");
    customerNames.Add("Mary");
    customerNames.Add("Bob");
}

using (IRedisClient client = new RedisClient())
{
    var customerNames = client.Lists["urn:customernames"];
    foreach (var name in customerNames)
        Console.WriteLine(name);
}

Typowany klient daje możliwość tworzenia sekwencji zapewniających unikatowość identyfikatorów. Sekwencje takie tworzone są "per typ".

long lastId = 0;
using(IRedisClient client = new RedisClient())
{
    var customerClient = client.GetTypedClient<Customer>();
    var customer = new Customer()
                       {
                           Id = customerClient.GetNextSequence(),
                           Address = "123 Main Street",
                           Name = "Bob Green",
                           Orders =
                               new List<Order>()
                                   {
                                       new Order() {OrderNumber = "AB123"},
                                       new Order() {OrderNumber = "AB124"}
                                   }
                       };
    var storedCustomer = customerClient.Store(customer);
    lastId = storedCustomer.Id;
}

using(IRedisClient client = new RedisClient())
{
    var customerClient = client.GetTypedClient<Customer>();
    var customer = customerClient.GetById(lastId);
    Console.WriteLine("Got customer {0} with name {1}", customer.Id, customer.Name);
}

Klient C# udostępnia też możliwość definiowania transakcji w poniższy sposób:


using(IRedisClient client = new RedisClient())
{
  var transaction = client.CreateTransaction();
  transaction.QueueCommand(c => c.Set("abc",1));
  transaction.QueueCommand(c => c.Increment("abc",1));
  transaction.Commit();
  var result = client.Get<int>("abc");
  Console.WriteLine(result);
}

sobota, 7 września 2013

[C#|Visual Studio] ServiceStack: Caching i Walidacja

Cache'ować dane można w ServiceStacku na różne sposoby. W rozwiązaniach linuksowych wykorzystuje się Redis - bazę "InMemory". Można korzystać także z MemoryCacheClient, którego konfigurujemy w pliku Global.asax.

container.Register<ICacheClient>(new MemoryCacheClient());

Cache można ustawiać i resetować w RESTowych endpointach. Ważne jest, aby każda cache'owana wartość miała swój unikalmy klucz, który możemy utworzyć za pomocą klasy UrnId.

public object Post(Entry entry)
{
    var cacheKey = UrnId.Create<Entry>(entry.Date.ToShortDateString());
    RequestContext.RemoveFromCache(base.Cache, cacheKey);
    CounterService.Add(entry.Count);
    return new EntryResponse() {Total = CounterService.Print()};
}

public object Get(Entry entry)
{
    var cacheKey = UrnId.Create<Entry>(entry.Date.ToShortDateString());
    return RequestContext.ToOptimizedResultUsingCache(base.Cache, cacheKey, new TimeSpan(0,0,0,5), 
        () => (object)CounterService.Print());
}

Wyliczenie cache'owanej wartości odbywa się poprzez lambda expression.

ServiceStack w genialny sposób wspiera tworzenie reguł walidacyjnych za pomocą FluentValidation. Reguły tworzymy w konstruktorach odpowiednich klas. Walidację należy najpierw zarejestrować.

public class MyEntryAppHost : AppHostBase
{
    public override void Configure(Funq.Container container)
    {
        Plugins.Add(new ValidationFeature());
        container.RegisterValidators(typeof(EntryService).Assembly);
    }
}

Przykładowa klasa walidacyjna:

public class EntryValidator : AbstractValidator<Entry>
{
    public EntryValidator()
    {
        RuleFor(e => e.Count).GreaterThanOrEqualTo(0).WithMessage("Koniecznie nieujemna");
        RuleFor(e => e.Date).LessThanOrEqualTo(DateTime.Now);
    }
}

piątek, 6 września 2013

[C#|Visual Studio] ServiceStack: IoC i ORM

Domyślnym kontenerem IoC w ServiceStacku jest Funq. Jest to prosty kontener zoptymalizowany pod kątem szybkości i lekkości. Domyślnie rejestrując w nim zależności rejestrujemy je jako Singletony. ServiceStack automatycznie wykona wstrzyknięcie zależności do publicznego property w klasach dziedziczących po Service. Przykład konfiguracji Funq (w pliku Global.asax i metodzie Configure).

public override void Configure(Funq.Container container)
{
    container.RegisterAutoWired<CounterService>();
    container.RegisterAutoWiredAs<CounterService, ICounterService>().ReusedWithin(ReuseScope.None);
    container.Register<ICounterService>(new CounterService());

    var cs = container.Resolve<ICounterService>();

    Console.WriteLine("Registered type is {0}", cs.GetType());
}

Zależności możemy rejestrować w trybie AutoWired, gdzie przeważnie wykonuje się mapowanie klasa na interfejs lub klasa w samą siebie. Do ręcznego ustawiania zależności wykorzystuje się z kolei metodę Register, natomiast do pobierania metodę Resolve. Jeżeli chcemy, by za każdym razem kontener zwracał nowy obiekt, musimy dopisać instrukcję ReusedWithin z parametrem None.

Mechanizmem do mapowania obiektowo - relacyjnego jest w ServiceStacku OrmLite. Jest to mapper prosty w użyciu i lekki, a zarazem bardzo wydajny, Kolejną jego zaletą jest multiplatformowość - działa z wieloma systemami, co widać przy próbie instalacji NuGetem.


Do tworzenia tabel wykorzystuje się obiekty POCO, które mogą być tymi samymi obiektami co DTO. Mamy do dyspozycji także znane z EntityFramework DataAnnotations. Przykład obiektu POCO:


[Route("/entry")]
[Authenticate]
[RequiredRole("User")]
public class Entry : IReturn<EntryResponse>
{
    [AutoIncrement]
    public int Id { get; set; }
    public DateTime Date { get; set; }
    public int Count { get; set; }
}

OrmLite typy złożone serializuje i zapisuje jako BLOBy. Można natomiast zakładać constrainty poprzez użycie odpowiednich atrybutów. Do tworzenia połączeń służy typ IDbConnectionFactory, który można skonfigurować w kontenerze IoC.

public override void Configure(Funq.Container container)
{
    IDbConnectionFactory factory =
        new OrmLiteConnectionFactory(HttpContext.Current.Server.MapPath("~/App_Data/data.txt"),
                                     SqliteDialect.Provider);

    container.Register<IDbConnectionFactory>(factory);

    container.RegisterAutoWiredAs<CounterService, ICounterService>();
}

Operacje na bazie przeprowadza się w bardzo wygodny sposób

public class CounterService : ICounterService
{
    public IDbConnectionFactory DbConnectionFactory { get; set; }
    private int _counter;

    public void Add(int i)
    {
        _counter += i;
        using (var dbConn = DbConnectionFactory.CreateDbConnection())
        {
            dbConn.Open();
            dbConn.CreateTable<Entry>();
            dbConn.Insert<Entry>(new Entry()
                                     {
                                         Count = i,
                                         Date = DateTime.Now
                                     });
        }
    }

    public int Print()
    {
        using (var dbConn = DbConnectionFactory.CreateDbConnection())
        {
            //WHERE - lambda expression lub SQL jako string
            dbConn.Open();
            return dbConn.Select<Entry>().Sum(x => x.Count);
        }
    }
}

czwartek, 29 sierpnia 2013

[C#|Visual Studio] ServiceStack: Filtry

Filtrami w ServiceStacku nazywane są fragmenty kodu wywoływane przed lub po każdym zapytaniu. Można ustawiać im priorytety, które odpowiadają później kolejności, w jakiej filtry się wykonują. Aby stworzyć filtr wystarczy dziedziczyć po klasie RequestFilterAttribute lub ResponseFilterAttribute.

public class IpRecordFilter : RequestFilterAttribute
{
    public ICacheClient Cache { get; set; }

    public override void Execute(IHttpRequest req, IHttpResponse res, object requestDto)
    {
        Cache.Add(req.UserHostAddress, req.RemoteIp);
    }
}

Użycie filtra sprowadza się do dodania atrybutu nad DTO lub serwisem.

public class EntryService : Service
{
    public int Sum { get; set; }

    [IpRecordFilter]
    public object Post(Entry entry)
    {
        Sum += entry.Count;
        return new EntryResponse() {Total = Sum};
    }
}

środa, 28 sierpnia 2013

[C#|Visual Studio] ServiceStack: Autentykacja i autoryzacja

Proces autentykacji ma na celu potwierdzenie tożsamości użytkownika, natomiast autoryzacja ma na celu wertfikację uprawnień użytkownika. W Service Stacku obie funkcjonalności zostały zaimplementowane w prosty sposób. Zaczynamy od modyfikacji w pliku Global.asax

public override void Configure(Funq.Container container)
{
    Plugins.Add(new AuthFeature( () => new AuthUserSession(), 
        new IAuthProvider[]
        {
            new BasicAuthProvider()
        }));

    Plugins.Add(new RegistrationFeature());

    container.Register<ICacheClient>(new MemoryCacheClient());
    var userRepository = new InMemoryAuthRepository();
    container.Register<IUserAuthRepository>(userRepository);
}

Skonfigurowany został jeden z gotowych providerów, BasicProvider. Programiści mają też możliwość definiowania własnych providerów. RegistrationFeature umożliwi REST-owe definiowanie ról. Repozytorium pacjentów trzymane będzie w pamięci. W tym samym miejscu możemy dodać pierwszego użytkownika z rolą Admin

string hash, salt;

new SaltedHash().GetHashAndSaltString("password", out hash, out salt);
userRepository.CreateUserAuth(new UserAuth()
                                  {
                                      Id = 1,
                                      FirstName = "John",
                                      Email = "jdoe@gmail.com",
                                      PasswordHash = hash,
                                      UserName = "jdoe",
                                      LastName = "Doe",
                                      Salt = salt,
                                      Roles = new List<string>(){RoleNames.Admin},
                                      Permissions = new List<string>(){"GetEntry"}
                                  }, "password");

Wymagane role i uprawnienia a także konieczność autentykacji deklarujemy atrybutami.

[Route("/entry")]
[Authenticate]
[RequiredRole("User")]
public class Entry : IReturn<EntryResponse>
{
    public DateTime Date { get; set; }
    public int Count { get; set; }
}

Logowanie i przydzielanie ról użytkownikom (w szczególnym przypadku samemu sobie) wykonuje się w poniższy sposób

var client = new JsonServiceClient("http://localhost:51270"){UserName = "jdoe", Password = "password"};
client.Send<AssignRolesResponse>(new AssignRoles()
                                     {
                                         UserName = "jdoe",
                                         Roles = new List<string>(){"User"},
                                         Permissions = new List<string>(){"GetEntry"}
                                     });

niedziela, 25 sierpnia 2013

[C#|Visual Studio] ServiceStack: Klient C#

Restowe API można łatwo odpytywać z kodu w C#. Wystarczy dodać nowy projekt w Visual Studio, który będzie zawierał referencję do projektu serwerowego. W klienckim projekcie należy też doinstalować .dll ServiceStack.Text. Jeżeli klientem ma być np. aplikacja konsolowa, to trzeba także przełączyć wersję .NET na np. 4.0 (bez Client Profile).


Z uwagi na wygodę po stronie klienckiej, obiekty DTO należy pisać w poniższy sposób:

[Route("/entry")]
public class Entry : IReturn<EntryResponse>
{
    public DateTime Date { get; set; }
    public int Count { get; set; }
}

public class EntryResponse
{
    public int Total { get; set; }
    public ResponseStatus ResponseStatus { get; set; }
}

a więc tak, aby implementować interfejs IReturn<T> oraz w obiektach typu T zamieszczać ResponseStatus, gdzie automatycznie zostanie wstawiony kod błędu.

Samo wywołanie serwisu po stronie klienckiej można wykonać synchronicznie lub asynchronicznie, np.

var client = new JsonServiceClient("http://localhost:51270");

var response = client.Send(new Entry() {Count = 12});
Console.WriteLine(response.Total);

client.SendAsync(new Entry() { Count = 24 }, 
    resp => Console.WriteLine(resp.Total),
    (err,exc) => Console.WriteLine("error"));

Odpowiedź zostanie zdeserializowana do odpowiedniego typu.w przypadku obu wywołań. Nie trzeba podawać większej ilości parametrów, gdyż wszystkie są pobierane z obiektów DTO (route i typ zwracany). JsonServiceClient można wymienić na inne typy klienckie, takie jak: JsvServiceClient czy CsvServiceClient

piątek, 23 sierpnia 2013

[C#|Visual Studio] ServiceStack: Tworzenie WebSerwisów

W tym poście zostanie opisane, jak utworzyć prosty projekt z RESTowymi serwisami w oparciu o ServiceStacka. Zaczynamy od stworzenia w Visual Studio pustego projektu ASP .NET.


Następnie za pomocą NuGeta instalujemy dllki ServiceStack.


Następnie tworzymy proste obiekty DTO, do których serializowane i deserializowane będą responsy i requesty REST-owe. Dobrą praktyką jest, aby obiekty DTO były symetryczne i przez konwencję do nazwy obiektu zwrotnego dodajemy "Response".

[Route("/entry/{Date}", "GET")]
public class Entry
{
    public DateTime Date { get; set; }
    public int Count { get; set; }
}

public class EntryResponse
{
    public int Total { get; set; }
}

Poprzez atrybuty definiujemy routingi, w których fragmenty ścieżek można bindować bezpośrednio do properties DTO. Następnie dodajemy klasę serwisu, która dziedziczy po bazowej klasie Service z SeriveStack. W klasie dodajemy metody o takich nazwach jak czasowniki HTTP, gdzie parametrem będzie zdefiniowane wcześniej DTO.

public class EntryService : Service
{
    public int Sum { get; set; }

    public object Post(Entry entry)
    {
        Sum += entry.Count;
        return new EntryResponse() {Total = Sum};
    }
}

Aby wystartować serwis, wystarczy skorzystać z pliku .asax.

public class Global : System.Web.HttpApplication
{

    public class MyEntryAppHost : AppHostBase
    {
        public MyEntryAppHost()
            : base("MyEntryService", typeof(EntryService).Assembly)
        {
        }

        public override void Configure(Funq.Container container)
        {
            
        }
    }

    protected void Application_Start(object sender, EventArgs e)
    {
        new MyEntryAppHost().Init();
    }
    //...
}

Metoda Configure posłuży jako bootstrapper, gdzie będzie można konfigurować np. kontener IoC. Ostatnim etapem jest skonfigurowanie pliku Web.config, gdzie dodajemy odpowiedni handler.

<configuration>
    <system.web>
        <compilation debug="true" targetFramework="4.0" />
    </system.web>

  <system.web>
    <httpHandlers>
      <add path="*" type="ServiceStack.WebHost.Endpoints.ServiceStackHttpHandlerFactory, ServiceStack" verb="*"/>
    </httpHandlers>
  </system.web>

</configuration>

Po uruchomieniu aplikacji mamy możliwość podglądu i testowania naszych endpointów.

wtorek, 20 sierpnia 2013

[C#|Visual Studio] ServiceStack: Wprowadzenie

ServiceStack to framework będący alternatywą dla WCF, WebAPI czy MVC. Jedną z głównych zalet jest cross - platformowość. Autorem Service Stacka nie jest Microsoft, ale community, które udostępnia go na zasadzie open source. Co więcej aplikacje pisane w ServiceStack nie muszą być hostowane na IIS, można je kompilować na Mono i uruchamiań np, na Linuxie.

Nazwa ServiceStack ma symbolizować stos technologii, na jakich budowane są aplikacje.


Kolejnymi względami przemawiającymi za tą technologią są prostota w stosunku do innych frameworków (w ServiceStack mamy do czynienia z konwencjami, które eliminują sporo zbędnych konfiguracji), oraz wydajność poszczególnych komponentów w porównaniu z alternatywnymi technologiami, głównie narzędzia ORM, kontenera IoC, serializator JSON.

W Visual Studio Service Stack instalujemy za pomocą NuGeta.

Przy instalacji do projektu załączane są dodatkowe .dll - ki, takie jak Text (serializacja JSON), Redis (api .NET do bazy NoSql), OrmLite (mapper obiektowo relacyjny).

Web Serwisy pisane w ServiceStacku bazować mają na Data Transfer Objects. Ideę DTO przedstawia poniższy rysunek

Parametrem każdego requestu jest prosty obiekt DTO mający ustawione odpowiednie pola. Jeżeli np. chcemy odpytywać jakąś kolekcję po różnych parametrach, to ustawiamy w danym momencie ten, który nas interesuje w obiekcie DTO.

Strona ServiceStack: http://www.servicestack.net