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

niedziela, 29 czerwca 2014

[C#|Visual Studio] AOP: Method Interception

Opakowanie metody możemy wykonać zarówno w Castle.DynamicProxy jak i przez Postsharp. Oba podejścia nieco różnią się od siebie.

W przypadku Castle zakładamy, że będziemy korzystać z kontenera IoC. Mamy prosty interfejs IMyInterface oraz prosty typ MyType. Rozpoczynamy od stworzenia interceptora, który będzie logował zdarzenia. Musi on dziedziczyć po interfejsie IInterceptor z przestrzeni nazw IInterceptor. Interfejs ten wystawia metodę Intercept.

public class LoggingInterceptor : IInterceptor
{
    public void Intercept(IInvocation invocation)
    {
        Console.WriteLine("On Enter");
        try
        {
            invocation.Proceed();
        }
        catch (Exception)
        {
            Console.WriteLine("On Error");
            throw;
        }
        finally
        {
            Console.WriteLine("On Exit");
        }
    }
}

Pozostaje jeszcze skonfigurować odpowiednio kontener i można korzystać z typu opakowanego w intereceptor.

using (var windsorContainer = new WindsorContainer())
{
    windsorContainer.Register(Component.For<LoggingInterceptor>());
    windsorContainer.Register(Component.For<IMyInterface>()
                                    .ImplementedBy<MyType>()
                                    .Interceptors<LoggingInterceptor>());

    var type = windsorContainer.Resolve<IMyInterface>();

    type.Run();
}

W Postsharpie dziedziczymy po bazowej klasie OnMethodBoundaryAspect, która definiuje potrzebne nam metody wirtualne. Ważne, by nasz aspekt oznaczyć atrybutem Serializable.

[Serializable]
public class LoggingInterceptor2: OnMethodBoundaryAspect
{
    public override void OnEntry(MethodExecutionArgs args)
    {
        Console.WriteLine("On Entry");
        base.OnEntry(args);
    }

    public override void OnException(MethodExecutionArgs args)
    {
        Console.WriteLine("On Error");
        base.OnException(args);
    }

    public override void OnExit(MethodExecutionArgs args)
    {
        Console.WriteLine("On Exit");
        base.OnSuccess(args);
    }
}

Nasza klasa jest atrybutem, który możemy umieścić nad definicją dowolnej metody.

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);
        }
    }
}

piątek, 12 kwietnia 2013

[IoC] Ninject

Ostatni post, dotyczący kontenerów IoC pokazuje możliwości Ninjecta. Jest to stosunkowo nowy (2007), darmowy kontener. W celu dodania go do projektu w Visual Studio korzystamy z NuGeta.



Pracę z kontenerem pokazano poniżej:

using (var ninjectContainer = new StandardKernel())
{
    ninjectContainer.Bind<IEngine>().To<GasEngine>();
    var car4 = ninjectContainer.Get<AudiA4>();
    car4.Run();
}

Podobnie jak w większości przypadków nie musimy rejestrować wszystkich typów (tych, o które nie prosimy przez interfejs). Poniżej kilka innych ciekawych funkcjonalności:

public class AudiA4Module : NinjectModule
{
    public override void Load()
    {
        Kernel.Bind<IEngine>().To<DieselEngine>().InSingletonScope();
        Kernel.Rebind<IEngine>()
            .ToMethod(context =>
                                {
                                    var rnd = new Random();
                                    var num = rnd.Next()%2;
                                    if (num == 1)
                                        return new GasEngine();
                                    else
                                        return new DieselEngine();
                                });
    }
}


using (var ninjectContainer = new StandardKernel(new AudiA4Module()))
{
    var car5 = ninjectContainer.Get<AudiA4>();
    car5.Run();
}

Moduły wspierają separację kodu konfigurującego. Nie musimy bindować koniecznie do typu, możemy dokonać bindingu do metody, gdzie wykonujemy jakąś logikę. Ponadto warto pamiętać, że domyślnie zwrócony zostanie zawsze nowy obiekt. Możemy to zmienić, wywołując na przykład metodę InSingletonScope (dla singletonów). Co więcej binding można odwołać metodą Rebind.

[IoC] StructureMap

Kolejnym ciekawym kontenerem IoC, po Unity i Castle of Windsor, jest StructureMap. API wszystkich trzech jest bardzo podobne, choć StructureMap rozwijany jest od 2004 roku i początkowo korzystało się z niego w zupełnie inny sposób. W celu dodania go do projektu w Visual Studio korzystamy z NuGeta.


Aby wykorzystać StructureMap, musimy pamiętać, aby używać .NET Framework 4.0 w wersji pełnej (nie zadziała wersja Client Profile. Pracę z kontenerem pokazano poniżej:

using(var structureMapContainer = new Container())
{
    structureMapContainer.Configure(x => x.For<IEngine>().Use<GasEngine>());
    var car3 = structureMapContainer.GetInstance<AudiA4>();
    car3.Run();
}

Konfiguracja odbywa się przez lambda expressions, nie musimy rejestrować wszystkich typów (tych, o które nie prosimy przez interfejs). Poniżej kilka innych użytecznych funkcjonalności:

public class AudiA4Registry : Registry
{
    public AudiA4Registry()
    {
        For<IEngine>().LifecycleIs(new SingletonLifecycle()).Use<DieselEngine>();
    }


using (var structureMapContainer = new Container(new AudiA4Registry()))
{
    var car3 = structureMapContainer.TryGetInstance<AudiA4>();
    if(car3 != null)
        car3.Run();
}

Rejestry pozwalają na odseparowanie kodu konfigurującego kontener. Metoda TryGetInstance zwróci w przypadku powyższego kodu null, ponieważ typ nie jest jawnie zarejestrowany.

wtorek, 9 kwietnia 2013

[IoC] Castle Windsor

Obok Unity, drugim interesującym kontenerem IoC jest Castle Windsor. Oba kontenery udostępniają całkiem podobne API, warto jednak znać podstawowe różnice. Castle Windsor jest open - sourcowym projektem, jednym z pierwszych .NET-owych kontenerów IoC.W celu dodania go do projektu w Visual Studio korzystamy z NuGeta.


Podstawową przewagą nad Unity jest możliwość stworzenia własnych instalatorów - obiektów implementujących interfejs IWindsorInstaller. W instalatorze takim możemy skonfigurować część zależności. Podczas tworzenia kontenera mamy możliwość dodania wielu instalatorów.


public class AudiA4Installer : IWindsorInstaller
{
    public void Install(IWindsorContainer container, IConfigurationStore store)
    {
        container.Register(Component.For<AudiA4>().LifeStyle.Transient);
        container.Register(Component.For<IEngine>()
            .ImplementedBy<DieselEngine>().LifeStyle.Transient);
    }
}

Różnice w stosunku do Unity są dwie: musimy rejestrować typy o które prosimy bezpośrednio (nie przez interfejs), domyślny cykl życia obiektu to Singleton, dlatego konieczne jest ustawienie Transient - tworzenie nowej instancji przy każdym Resolve.

using (var windsorContainer = new WindsorContainer())
{
    windsorContainer.Install(new AudiA4Installer());
    var car2 = windsorContainer.Resolve<AudiA4>();
    car2.Run();
}

Castle Windsor wstrzykuje zarówno przez konstruktor jak i przez setter, tak więc klasa wyższego poziomu może być skonstruowana na dwa sposoby.

niedziela, 7 kwietnia 2013

[IoC] Unity

Unity jest kontenerem IoC dostarczanym przez firmę Microsoft. Można go spotkać, jako część biblioteki Enterprise Library, lub pograć managerem pakietów NuGet.


Do projektu dodajemy poniższą referencję:

using Microsoft.Practices.Unity;

Przykład użycia kontenera Unity:

using (var unityContainer = new UnityContainer())
{
    unityContainer.RegisterType<IEngine, DieselEngine>();
    //Setter Injection
    unityContainer.RegisterType<IEngine, DieselEngine>(new InjectionProperty("Power", "100"));
    var car2 = unityContainer.Resolve<AudiA4>();
    car2.Run();
}

Z przykładu tego wynikają dwa fakty. Po pierwsze, jeżeli chcemy dostać typ i prosimy o niego metodą Resolve, a jako parametr generyczny podajemy typ, a nie interfejs, to nie musimy takiego typu rejestrować. Po drugie możemy wstrzykiwać także pojedyncze propercje poprzez klasę InjectionProperty.

Jeżeli chcemy zarejestrować jakiś obiekt, jako singleton, to podajemy przez parametr sposób, w jaki kontener ma zarządzać takim obiektem. Domyślnie mamy typ TransientLifetimeManager, który za każdym razem tworzy nową instancję. Dla singletonów wybieramy ContainerControlledLifetimeManager.

using (var unityContainer = new UnityContainer())
{
    //Singleton
    unityContainer.RegisterType<IEngine, GasEngine>(new ContainerControlledLifetimeManager());
    var car2 = unityContainer.Resolve<AudiA4>();
    car2.Run();
}

Widzimy, że za Unity przemawia proste API zaspokajające podstawowe potrzeby przy wstrzykiwaniu zależności.

sobota, 6 kwietnia 2013

[IoC] Kontenery IoC

Kontenerem IoC można w skrócie nazwać pewien mechanizm wykonujący wstrzykiwanie zależności (Dependency Injection). Funkcjonalnością kontenera musi być możliwość konfiguracji zależności jakie zachodzą między obiektami. Kiedy prosimy kontener o typ podając mu na przykład interfejs, wie co ma nam zwrócić, a także wie jakie zależności powinien dany obiekt posiadać (czym różni się od wzorca ServiceLocatora).

Przykład zależności pomiędzy obiektami:

public class AudiA4
    {
        private readonly IEngine _engine;

        public AudiA4(IEngine engine)
        {
            _engine = engine;
        }

        public void Run()
        {
            _engine.Run();
        }
    }

    public interface IEngine
    {
        void Run();
    }

    public class DieselEngine : IEngine
    {
        public void Run()
        {
            Console.Write("Diesel engine running...");
        }
    }

Kontener tworzą dwie metody. Pierwsza pozwala na rejestrowanie typów w słowniku. Typy rejestrowane powinny być po interfejsach tylko wtedy jeżeli implementujemy dany interfejs więcej niż raz. W przeciwnym wypadku tworzymy niepotrzebne byty. Druga metoda tworzy obiekt na podstawie wcześniej skonfigurowanych zależności. W C# możemy skorzystać z mechanizmu Reflection, przez co zajmuje nam to bardzo mało kodu. Ponieważ w bardziej rozbudowany aplikacjach możemy mieć wielopoziomowe grafy obiektów, metodę Resolve wołamy rekurencyjnie, aby rozwiązać wszystkie zależności.


public class IocContainer
{
    private Dictionary<Type, Type> dependencyMap;

    public IocContainer()
    {
        dependencyMap = new Dictionary<Type, Type>();
    }

    public T Resolve<T>()
    {
        return (T) Resolve(typeof(T));
    }

    private object Resolve(Type type)
    {
        Type resolvedType = null;
        try
        {
            resolvedType = dependencyMap[type];
        }
        catch
        {
            throw new KeyNotFoundException(String.Format("Cannot resolve type {0}", type.FullName));
        }
        var ctor = resolvedType.GetConstructors().First();
        var parameters = ctor.GetParameters();
        if (parameters.Count() == 0)
            return Activator.CreateInstance(resolvedType);
        IList<object> @params = new List<object>();
        foreach (var parameter in parameters)
        {
            @params.Add(Resolve(parameter.ParameterType));
        }
        return ctor.Invoke(@params.ToArray());
    }

    public void Register<TFrom, TTo>()
    {
        dependencyMap.Add(typeof(TFrom), typeof(TTo));
    }
}

Przykład użycia kontenera:

static void Main(string[] args)
{
    var container = new IocContainer();
    container.Register<AudiA4, AudiA4>();
    //zależnie od konfiguracji
    container.Register<IEngine, DieselEngine>();
    var car = container.Resolve<AudiA4>();
    car.Run();
}

piątek, 5 kwietnia 2013

[SOLID] DIP: Inversion of Control, Dependency Injection

SOLID to akronim reprezentujący 5 zasad programowania obiektowego. Ostatnia litera D pochodzi od zasady odwrócenia zależności (Dependency Inversion Principle - DIP).

Problem zależności w kodzie dotyczy na przykład sytuacji jak poniżej:

public class AudiA4
{
 public void Run()
 {
  //zależność
  var engine = new GasEngine();
  engine.Run();
 }
}

Zasada odwrócenia zależności głosi, że:
Moduły wysokiego poziomu nie powinny zależeć od modułów niskiego poziomu. Jedne i drugie powinny być zależne od pewnych abstrakcji. 
Abstrakcje same w sobie także nie powinny zależeć od szczegółów. Dzięki temu dodawanie nowych komponentów lub ich wymiana nie będzie powodować zmian w modułach wysokiego poziomu.

Sposobem na zastosowanie tej zasady jest "wzorzec" Inversion of Control. Cudzysłów bierze się stąd, że IoC definiowane jest na stosunkowo wysokim poziomie abstrakcji w porównaniu z innymi wzorcami. Możemy go zastosować do różnego rodzaju kontroli:
  • flow aplikacji
  • sterowanie pomiędzy systemami za pomocą wspólnego interfejsu
  • tworzenie i binding zależności w programie
Sposobem implementacji IoC jest wstrzykiwanie zależności (Dependency Injection - DI).

Flow aplikacji - przykładem na odwrócenie zależności jest zastosowanie graficznego interfejsu użytkownika. W starych aplikacjach konsolowych, program pyta użytkownika o konkretną daną, oczekuje na odpowiedź i wykonuje się dalej. Po odwróceniu zależności, użytkowik z GUI dostarcza do programu te informacje, które akurat chce dostarczyć. Następuje zatem przejście od kodu proceduralnego do programu będącego reakcjami na zdarzenia użytkownika. Wszystko zgodnie z zasadą "Don't call us, we will call you".

Interface Inversion - przyjmując,  że aplikacja składa się z providerów usług i ich konsumentów, możemy zauważyć, że tradycyjnie to konsument zależy od providerów. Po odwróceniu zależności to konsument, poprzez pewien interfejs decyduje o tym, czego potrzebuje od providerów. Konsument posiada zatem referencję do pewnego interfejsu, który implementują providerzy, sam nie musi się do niczego dostosowywać.

Tworzenie i binding obiektów - polega na tym, aby tworzyć obiekty niskiego poziomu poza obiektami wysokiego poziomu.

Pewnym typem IoC jest Dependency Injection. Cały problem polega na tym, że skoro nie chcemy tworzyć obiektów niskiego poziomu w obiektach wysokiego poziomu, to jakiś mechanizm musi nam je wstrzykiwać. Mamy zatem do dyspozycji trzy podstawowe pomysły na wstrzykiwanie zależności:
  • przez konstruktor - wymusza wstrzykiwanie przy tworzeniu obiektu

IEngine gasEngine = new GasEngine();
AudiA4 car = new AudiA4(gasEngine);

public class AudiA4
{
 private readonly _engine;

 public AudiA4(IEngine engine)
 {
  _engine = engine;
 }

 public void Run()
 {
  _engine.Run();
 }
}

  • przez setter - dzięki temu podejściu można korzystać z klasy przed wstrzyknięciem zależności, w C# można korzystać bezpośrednio z properties, w innych językach trzeba posiłkować się funkcjami.

AudiA4 car = new AudiA4();
car.Engine = new GasEngine();

public class AudiA4
{
 public IEngine Engine { get; set; }

 public void Run()
 {
  Engine.Run();
 }
}

  • przez interfejs - jawnie pokazujemy, że do danej klasy wstrzykiwane będą zależności. 

AudiA4 car = new AudiA4();
var engine = new GasEngine();
((IDependsOnEngine)car).Inject(engine);

public class AudiA4 : IDependsOnEngine
{
 private IEngine _engine;

 public void Run()
 {
  _engine.Run();
 }

 public void Inject(IEngine engine)
 {
  _engine = engine;
 }
}

public interface IDependsOnEngine
{
 void Inject(IEngine engine);
}

Dependency Injection jest genialnym mechanizmem, jednak warto pamiętać o kilku jego wadach, aby nie nadużywać go, gdy nie jest potrzebny. Przede wszystkim narusza zasadę enkapsulacji - silnik wyciągany jest poza obiekt samochodu. Często także zależności tworzymy wcześniej niż ich potrzebujemy, bo na przykład zmusza nas do tego konstruktor. Nadużywanie DI może także doprowadzić do sytuacji, gdy klasa implementuje kilkanaście interfejsów, co także nie jest raczej wskazane.

czwartek, 15 listopada 2012

.NET Framework

Wszystko na temat Visual Studio, języka C# i technologii opartych na platformie .NET Framework: