Paweł Łukasiewicz
2015-07-03
Paweł Łukasiewicz
2015-07-03
Udostępnij Udostępnij Kontakt

Lady loading jest pojęciem polegającym na opóźnieniu ładowania obiektu, aż do punktu w którym tych danych potrzebujemy. Ujmując to w innych słowach, obiekt ładujemy na żądanie, zamiast niepotrzebnego, wcześniejszego ładowania danych.

Dla przykładu rozważmy przedstawiony poniżej przykład klasy Customer, która wewnątrz ma wiele obiektów Order. Przyjrzyj się z bliska konstruktorowi klasy Customer. Kiedy obiekt Customer jest tworzony obiekt Order jest ładowany w tym samym momencie. W takim wypadku jeżeli potrzebujemy lub też nie potrzebujemy obiektu Order on i tak zostanie załadowany.

Co jednak w przypadku gdybyśmy tworzyli obiekt Customer a później, na żądanie ładowali obiekt Order?

class Customer
{
    private List<Order> OrderList = null;
    public string CustomerName;
    public Customer()
    {
        CustomerName = "Pawel";
        // W konstruktorze zwracamy od razu listę zleceń
        // Nie jest ważne czy tych danych potrzebujemy czy też nie
        OrderList = LoadOrders();
    }
    private List<order> LoadOrders()
    {
        // Tworzymy listę tymczasową, która zostanie wypełniona danymi
        List<Order> temp = new List<Order>();
        Order o = new Order();
        o.OrderNumber = "O123";
        temp.Add(o);
        o = new Order();
        o.OrderNumber = "O433";
        temp.Add(o);
        return temp;
    }
}
class Order
{
    public string OrderNumber{get;set;}
}

Zastanówmy się nad kodem, który implementuje klasę Customer jak w powyższym przykładzie. W momencie tworzenia obiektu Customer nie powinniśmy ładować danych do naszej listy, gdyż w tym momencie te dane nie są nam jeszcze potrzebne. Jednakże, kiedy dojdziemy do przedstawionej poniżej pętli foreach dane te powinny załadowane (ładowanie obiektu na żądanie).

// Tworzymy instancję naszej klasy ale nie ładujemy danych do listy
Customer cs = new Customer();
Console.WriteLine("Dane klienta: ", cs.CustomerName);
// Jak tylko dochodzimy do pętli ładujemy dane
foreach (Order o1 in cs.Orders)
{
    Console.WriteLine(o1.OrderNumber);
}

Jak zatem zaimplementować lazy loading?

Jeżeli chcemy zaimplementować lazy loading w powyższym przykładzie należy wprowadzić następujące zmiany do projektu:

  • usunięcie ładowania listy z konstruktora klasy Customer;
  • tworzymy właściwość w naszej klasie, która pozwala nam na załadowanie danych do listy o ile dane te nie zostały już załadowane.

using System;
using System.Collections.Generic;
namespace LazyLoading
{
    class Program
    {
        static void Main(string[] args)
        {
            // Tworzymy instancję naszej klasy ale nie ładujemy danych do listy
            CustomerLazyLoading cs = new CustomerLazyLoading();
            Console.WriteLine("Dane klienta: ", cs.customerName);
            // Jak tylko dochodzimy do pętli ładujemy dane
            Console.WriteLine("Lista zleceń: ");
            foreach (OrderLazyLoading o1 in cs.Orders)
            {
                Console.WriteLine(o1.OrderNumber);
            }
            Console.ReadKey();
            // Wynik działania programu
            //Dane klienta: Pawel
            //Lista zlecen:
            //O123
            //O433
        }
    }
    // Klasa w której zaimplementujemy 'lazy loading'
    class CustomerLazyLoading
    {
        private List<OrderLazyLoading> OrderListLazyLoading = null;
        public string customerName;
        // W konstruktorze wypełniamy jedynie dane klienta
        public CustomerLazyLoading()
        {
            customerName = "Pawel";
        }
        // Nasza metoda do wypełniania listy
        private List<OrderLazyLoading> LoadOrders()
        {
            // Tworzymy listę tymczasową, która zostanie wypełniona danymi
            List<OrderLazyLoading> temp = new List<OrderLazyLoading>();
            OrderLazyLoading o = new OrderLazyLoading();
            o.OrderNumber = "O123";
            temp.Add(o);
            o = new OrderLazyLoading();
            o.OrderNumber = "O433";
            temp.Add(o);
            return temp;
        }
        // Tworzymy właściwość, która wypełnia listę, kiedy potrzebujemy danych
        public List<OrderLazyLoading> Orders
        {
            get
            {
                if (OrderListLazyLoading == null)
                    OrderListLazyLoading = LoadOrders();
                return OrderListLazyLoading;
            }
        }
    }
    class OrderLazyLoading
    {
        public string OrderNumber { get; set; }
    }
}

Czy w .NET istnieją obiekty za pomocą, których możemy zaimplementować lazy loading?

W .Net mamy do dyspozycji Lazy<T>, który pozwala na automatyczną obsługę lazy loading. Załóżmy, że chcemy zaimplementować Lazy<> w powyższym kodzie.

Musimy zatem wprowadzić dwie zmiany. Pierwsza z nich to utworzenie obiektu używając Lazy<>:

private Lazy<List<Orders>> OrdersList;

Druga rzecz to połączenie obiektu Lazy<> z naszą metodą, która pomoże nam załadować dane:

OrdersList = new Lazy<List<Orders>>(() => LoadOrders());

Teraz, jak tylko klient wykona wywołanie obiektu OrdersList, dojdzie to wywołania metody LoadOrders(), która wypełni naszą listę danymi.

Poniżej kompletny kod ze szczegółowym opisem:

using System;
using System.Collections.Generic;
namespace LadyLoadingNETObject
{
    class Program
    {
        static void Main(string[] args)
        {
            // Tworzymy instancję naszej klasy ale nie ładujemy danych do naszej listy
            Customer cs = new Customer();
            Console.WriteLine("Dane klienta: {0}", cs.customerName);
            // Jak tylko dochodzimy do pętli dane te powinny zostać załadowane
            Console.WriteLine("Lista zleceń: ");
            foreach (Orders o1 in cs.OrdersProperty)
            {
                Console.WriteLine(o1.OrderNumber);
            }
            Console.ReadKey();
            // Wynik działania programu
            //Dane klienta: Pawel
            //Lista zlecen:
            //O123
            //O433
        }
    }
    class Customer
    {
        private Lazy<list<Orders>> OrdersList;
        public string customerName;

        public Customer()
        {
            customerName = "Pawel";
            // Łączymy obiekt Lazy<> z naszą metodą. Jak tylko dojdzie do wywołania obiektu OrdersList
            // dane na naszej liście zostaną wypełnione
            OrdersList = new Lazy<List<Orders>>(() => LoadOrders());
        }

        // Właściwość dzięki, której załadujemy dane do listy na żądanie
        public List<Orders> OrdersProperty
        {
            get
            {
                return OrdersList.Value;
            }
        }

        // Nasza metoda do wypełniania listy
        private List<Orders> LoadOrders()
        {
            // Tworzymy listę tymczasową, która zostanie wypełniona danymi
            List<Orders> temp = new List<Orders>();
            Orders o = new Orders();
            o.OrderNumber = "O123";
            temp.Add(o);
            o = new Orders();
            o.OrderNumber = "O433";
            temp.Add(o);

            return temp;
        }
    }

    class Orders
    {
        public string OrderNumber;
    }
}

Wady i zalety lazy loading

Poniżej lista zalet użycia lazy loading:

  • minimalizuje czas uruchamiania aplikacji;
  • aplikacja zużywa mniej pamięci ponieważ dane są ładowane na żądanie;
  • unikamy niepotrzebnego odpytywania bazy danych.

Jedyną wadą jest to, że kod staje się bardziej skomplikowany. Sprawdzenie, czy potrzebujemy załadować dane czy nie ma bardzo niewielki wpływ na spadek wydajności.

Jak widzicie, mamy zdecydowanie więcej zalet niż wad.

Przeciwieństwem lazy loading (leniwe ładowanie danych) jest eager loading. W tym podejściu ładujemy wszystkie obiekty do pamięci tak szybko jak obiekt jest tworzony.