GenericRepository implementacja (dłuższy kod)

GenericRepository implementacja (dłuższy kod)
JP
  • Rejestracja:ponad 7 lat
  • Ostatnio:4 miesiące
  • Postów:1065
0

Siemka
Pomijając to czy generic repo to zło czy nie to chciałbym się was zapytać o jakość takiej implementacji. Znalazłem taki przykład kiedyś i nawet kilka razy użyłem w kilku projektach (trochę rozbudowane CRUD-y). Niby działa i co całkiem sprawnie, ale jakie mogą być wpadki i ograniczenia takiej implementacji?
Jak widać obiekty są odłączane od kontekstu przy odczycie i podłączane przy zapisie i aktualizacji) ale po zmianach trzeba dostarczyć do obiektu info o EntityState (co nie jest jakimś problemem).

Dodatkowo umożliwia zapisanie czy aktualizację całego drzewa obiektów (o ile każdy obiekt na każdej gałęzi będzie miał określony EntityState).

Czyli czy Wam się podoba taki twór i dlaczego nie? :)

public interface IEntity
{
    [NotMapped]
    EntityState EntityState { get; set; }
}

public enum EntityState
{
    Unchanged,
    Added,
    Modified,
    Deleted
}

public class BaseEntity<T> : IEntity
{
    public T Id { get; set; }
    public DateTime Created { get; set; }

    public BaseEntity()
    {
        Created = DateTime.UtcNow;
    }
    [NotMapped]
    public EntityState EntityState { get; set; }
}


public interface IGenericDataRepository<T> : IDisposable  where T : class, BetKom.Model.Common.IEntity
{
    IList<T> GetAll(params Expression<Func<T, object>>[] navigationProperties);
    IList<T> GetList(Func<T, bool> where, params Expression<Func<T, object>>[] navigationProperties);
    T GetSingle(Func<T, bool> where, params Expression<Func<T, object>>[] navigationProperties);
    void Add(params T[] items);
    void Update(params T[] items);
    void Remove(params T[] items);
}

public class GenericDataRepository<T> : IGenericDataRepository<T> where T : class, BetKom.Model.Common.IEntity
{
    public GenericDataRepository()
    {
        
    }
    public virtual IList<T> GetAll(params Expression<Func<T, object>>[] navigationProperties)
    {
        List<T> list;
        using (var context = new DBContext())
        {
            IQueryable<T> dbQuery = context.Set<T>();

            //Apply eager loading
            foreach (Expression<Func<T, object>> navigationProperty in navigationProperties)
                dbQuery = dbQuery.Include<T, object>(navigationProperty);

            list = dbQuery
                .AsNoTracking()
                .ToList<T>();
        }
        return list;
    }

    public virtual IList<T> GetList(Func<T, bool> where,
         params Expression<Func<T, object>>[] navigationProperties)
    {
        List<T> list;
        using (var context = new DBContext())
        {
            IQueryable<T> dbQuery = context.Set<T>();

            //Apply eager loading
            foreach (Expression<Func<T, object>> navigationProperty in navigationProperties)
                dbQuery = dbQuery.Include<T, object>(navigationProperty);

            list = dbQuery
                .AsNoTracking()
                .Where(where)
                .ToList<T>();
        }
        return list;
    }

    public virtual T GetSingle(Func<T, bool> where,
         params Expression<Func<T, object>>[] navigationProperties)
    {
        T item = null;
        using (var context = new DBContext())
        {
            IQueryable<T> dbQuery = context.Set<T>();

            //Apply eager loading
            foreach (Expression<Func<T, object>> navigationProperty in navigationProperties)
                dbQuery = dbQuery.Include<T, object>(navigationProperty);

            item = dbQuery
                .AsNoTracking() //Don't track any changes for the selected item
                .FirstOrDefault(where); //Apply where clause
        }
        if (item != null)
            item.EntityState = BetKom.Model.Common.EntityState.Unchanged;
        return item;
    }

    /* rest of code omitted */

    public virtual void Add(params T[] items)
    {

        PUpdate(items);
    }

    public virtual void Update(params T[] items)
    {

        PUpdate(items);
    }

    public void PUpdate(params T[] items)
    {
        using (var context = new DBContext())
        {
            DbSet<T> dbSet = context.Set<T>();
            foreach (T item in items)
            {
                dbSet.Attach(item);
                var dd = context.ChangeTracker.Entries<IEntity>();
                foreach (DbEntityEntry<IEntity> entry in dd)
                {
                    IEntity entity = entry.Entity;
                    entry.State = GetEntityState(entity.EntityState);
                }
            }
            context.SaveChanges();
    }

    public virtual void Remove(params T[] items)
    {
        PUpdate(items);
    }

    protected static System.Data.Entity.EntityState GetEntityState(BetKom.Model.Common.EntityState entityState)
    {
        switch (entityState)
        {
            case BetKom.Model.Common.EntityState.Unchanged:
                return System.Data.Entity.EntityState.Unchanged;
            case BetKom.Model.Common.EntityState.Added:
                return System.Data.Entity.EntityState.Added;
            case BetKom.Model.Common.EntityState.Modified:
                return System.Data.Entity.EntityState.Modified;
            case BetKom.Model.Common.EntityState.Deleted:
                return System.Data.Entity.EntityState.Deleted;
            default:
                return System.Data.Entity.EntityState.Detached;
        }
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }
    protected virtual void Dispose(bool disposing)
    {
        if (disposing)
        {
            //// free managed resources  
            //if (managedResource != null)
            //{
            //    managedResource.Dispose();
            //    managedResource = null;
            //}
        }
        //// free native resources if there are any.  
        //if (nativeResource != IntPtr.Zero)
        //{
        //    Marshal.FreeHGlobal(nativeResource);
        //    nativeResource = IntPtr.Zero;
        //}
    }
}
MS
  • Rejestracja:ponad 10 lat
  • Ostatnio:około godziny
  • Postów:312
1

JP
  • Rejestracja:ponad 7 lat
  • Ostatnio:4 miesiące
  • Postów:1065
0

Ale co chciałeś powiedzieć tym linkiem bo większość punktów z linka nie ma tu zastosowania. Szczególnie jak zamiast generycznego repozytorium (GR) zrobisz podobną, konkretną implementację dla AggregateRoot-a (co jest możliwe bez problemu w podanej implementacji i nawet tak wykorzystywane przeze mnie).
Zresztą pisałem, żeby nie oceniać Samego faktu czy GR ma sens czy nie.

Inna sprawa, ze jak mam 10 tabel czysto słownikowych to GR też jest złe? No to co zamiast? DBContext w okienkach WinForms?

edytowany 1x, ostatnio: jacek.placek
N0
po prostu nie przeczytał pierwszego zdania z Twojego posta ;)
MrBean Bean
Taki odruch kompulsywny.
MrBean Bean
  • Rejestracja:ponad 7 lat
  • Ostatnio:ponad 7 lat
  • Postów:117
0

Nie rozumiem co w tym "repozytorium" robi GetEntityState. Nie lepiej ci użyć ObjectStateManager ?

Ja tam nic nie umiem wszystkie moje aplikacje to Table Module :).

Ale może mój kolega coś więcej powie https://www.google.pl/search?biw=1920&bih=974&q=Change+Tracking+of+POCO+snapshot+and+proxy&oq=Change+Tracking+of+POCO+snapshot+and+proxy&gs_l=psy-ab.3...11278.14726.0.14934.5.4.1.0.0.0.139.437.2j2.4.0....0...1.1.64.psy-ab..0.0.0....0.j65hLsGaV_M


▒▒▒▒▒▒▒▒█▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀█
▒▒▒▒▒▒▒█░▒▒▒▒▒▒▒▓▒▒▓▒▒▒▒▒▒▒░█
▒▒▒▒▒▒▒█░▒▒▓▒▒▒▒▒▒▒▒▒▄▄▒▓▒▒░█░▄▄
▒▒▄▀▀▄▄█░▒▒▒▒▒▒▓▒▒▒▒█░░▀▄▄▄▄▄▀░░█
MrBean Bean
  • Rejestracja:ponad 7 lat
  • Ostatnio:ponad 7 lat
  • Postów:117
0

Sorry ale pociągnę temat zagadką jest dla mnie te repo. Dlaczego są tam metody virtualne ?

To bardziej wygląda jak fasada

Kopiuj
Expression<Func<T, object>>[] navigationProperties

Ty to wstrzykujesz do warstwy widoku?

Inna sprawa, ze jak mam 10 tabel czysto słownikowych to GR też jest złe? No to co zamiast? DBContext w okienkach WinForms?

Ja bym zrobił komendy i do tego fabrykę.


▒▒▒▒▒▒▒▒█▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀█
▒▒▒▒▒▒▒█░▒▒▒▒▒▒▒▓▒▒▓▒▒▒▒▒▒▒░█
▒▒▒▒▒▒▒█░▒▒▓▒▒▒▒▒▒▒▒▒▄▄▒▓▒▒░█░▄▄
▒▒▄▀▀▄▄█░▒▒▒▒▒▒▓▒▒▒▒█░░▀▄▄▄▄▄▀░░█
JP
A możesz napisać dlaczego? W czym komendy i fabryka jest lepsza?
JP
  • Rejestracja:ponad 7 lat
  • Ostatnio:4 miesiące
  • Postów:1065
0

To nie moje, jak pisałem na początku. Znalazłem i się pytam o opinie. Było to jakiś czas temu i teraz nie mogę odnaleźć nawet na jakiej to było stronie.
Wirtualne pewnie są po to, żeby można przesłonić.

Nie wstrzykuję nic nigdzie. navigationProperties to są "dzieci" do pobrania razem z rodzicem. Można sobie wybrać, że chcesz pobrać rodzica z jakimiś dziećmi albo jakieś inne rekordy w relacjach.

MrBean Bean
  • Rejestracja:ponad 7 lat
  • Ostatnio:ponad 7 lat
  • Postów:117
0

A możesz napisać dlaczego? W czym komendy i fabryka jest lepsza?

Będziesz mógł nimi zastąpić grube serwisy. W tedy każdy select czy transakcja do bazy ma swoją klasę. Możesz też pójść dalej i zrobić cqrs.
Repo jest częścią pakietu, który powinien być zależny od domeny. Zwykle wypluwa to co potrzebuje serwis domenowy:). Jeśli nie robisz DDD to prawdopodobnie w ogóle go nie potrzebujesz.


▒▒▒▒▒▒▒▒█▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀█
▒▒▒▒▒▒▒█░▒▒▒▒▒▒▒▓▒▒▓▒▒▒▒▒▒▒░█
▒▒▒▒▒▒▒█░▒▒▓▒▒▒▒▒▒▒▒▒▄▄▒▓▒▒░█░▄▄
▒▒▄▀▀▄▄█░▒▒▒▒▒▒▓▒▒▒▒█░░▀▄▄▄▄▄▀░░█
JP
  • Rejestracja:ponad 7 lat
  • Ostatnio:4 miesiące
  • Postów:1065
0

Dzięki za odpowiedź.

MrBean Bean napisał(a):

A możesz napisać dlaczego? W czym komendy i fabryka jest lepsza?

Będziesz mógł nimi zastąpić grube serwisy. W tedy każdy select czy transakcja do bazy ma swoją klasę. Możesz też pójść dalej i zrobić cqrs.

CQRS to OK ale raczej przy większych potrzebach iż moje. Np. mam 15 słownikowych tabel. W każdej maks. 100 rekordów. CQRS ma sens? Same Commandy pewnie tak. Albo jakiś WinForms-owy manager aplikacji ASP.NET MVC w którym są importy (i ręczna edycja) produktów, opcji itp głównie z plików Excela.

Repo jest częścią pakietu, który powinien być zależny od domeny. Zwykle wypluwa to co potrzebuje serwis domenowy:).

To jest jasne. Ja nie mówię, ze generic repo jest sensowne dla DDD bo w DDD rządzi domena a ta powinna gadać swoim (domenowym) językiem.

Jeśli nie robisz DDD to prawdopodobnie w ogóle go nie potrzebujesz.

To nie jest tak. IMHO DDD ma znacznie wyższy techniczny próg wejścia. Ja "miększe" tematy DDD to raczej ogarniam ale mam wrażenie, że cała architektura aplikacji się mocno komplikuje a to trzeba potem utrzymać. Trzeba mieć zasoby a ja jestem mikro MiSiem. Może niedługo będę aktualizował jeden większy system to spróbuję jakoś mocniej do niego podejść pod katem DDD. Tam jest kilka modułów (zamówienia, sprzedaż, produkcja, logistyka, pecety, terminale mobilne, maszyny przemysłowe z komunikacją bezpośrednią itp) i wydaje się, ze DDD byłoby idealne ale musiałbym się sporo douczyć a nie wiem czy będe miał na to czas i budżet.

MrBean Bean
  • Rejestracja:ponad 7 lat
  • Ostatnio:ponad 7 lat
  • Postów:117
0

Jeśli masz problem z wydajnością to może zrób sobie fizyczne widoki, to znaczy skompilowane selecty. Od strony transakcja patrząc przez pryzmat CQRS robisz sobie handler AddFakturaHandlerCommand do niego komendę z 10 kluczami do słowników, no i chyba git?


▒▒▒▒▒▒▒▒█▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀█
▒▒▒▒▒▒▒█░▒▒▒▒▒▒▒▓▒▒▓▒▒▒▒▒▒▒░█
▒▒▒▒▒▒▒█░▒▒▓▒▒▒▒▒▒▒▒▒▄▄▒▓▒▒░█░▄▄
▒▒▄▀▀▄▄█░▒▒▒▒▒▒▓▒▒▒▒█░░▀▄▄▄▄▄▀░░█
edytowany 1x, ostatnio: MrBean Bean
JP
  • Rejestracja:ponad 7 lat
  • Ostatnio:4 miesiące
  • Postów:1065
0

Ale ja nie mam problemów z wydajnością.
Chciałem się tylko zapytać o tę implementację GR do np. słownikowych tabel. Mi się ona ładnie spina z WinForms-ami i Gridami z DevExpress szczególnie jak chcę zapisać od razy całe drzewo do db. Albo niektóre dzieci w drzewie, tylko trzeba ustawić odpowiedni EntityState. Np Opcje i WartośćOpcji dla jakiegoś Produktu.
Pobieram sobie Opcję z Wartościami z takiego repo. Obiekty są odłączone od kontekstu.
Mogę sobie w gridach coś pozmieniać i wykonać repo.Update(Opcja op) i wszystko się ładnie zapisuje (zmiany w Opcji i zmiany w wartościach opcji).
No ale może są inne, lepsze metody do takich zadań?

MrBean Bean
  • Rejestracja:ponad 7 lat
  • Ostatnio:ponad 7 lat
  • Postów:117
0

Repo nie może być stabilne to to co korzysta z repo ma być stabilne a repo niestabilne. Projektowanie to nie przebieranie w wzorcach i wybieranie tych "najlepszych" a raczej zarządzanie zależnościami, tworzenie powiązań logicznych.


▒▒▒▒▒▒▒▒█▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀█
▒▒▒▒▒▒▒█░▒▒▒▒▒▒▒▓▒▒▓▒▒▒▒▒▒▒░█
▒▒▒▒▒▒▒█░▒▒▓▒▒▒▒▒▒▒▒▒▄▄▒▓▒▒░█░▄▄
▒▒▄▀▀▄▄█░▒▒▒▒▒▒▓▒▒▒▒█░░▀▄▄▄▄▄▀░░█
JP
  • Rejestracja:ponad 7 lat
  • Ostatnio:4 miesiące
  • Postów:1065
0

Hm... Nawet się zgadzam (przy założeniu, ze zrozumiałem poprawnie). Tylko mam wrażenie, ze nie piszemy o tym samym problemie bo to co napisałeś to też dotyczy bardziej DDD niż CRUD. A mi chodzi o ocenę rozwiązania dla CRUD+.
Np m u siebie słowniki Marka -> Model -> Typ. Podobnie jak np. w samochodach. I tu nie ma żadnej domeny. nic się z tymi obiektami nie dzieje, nie ma żadnego przetwarzania. wszystko mocno zamknięte bo to jest aplikacja UI dla maszyny (a maszyna raczej się nie zmieni przez całe swoje życie). I mam jedno repo MarkaRepository w którym jest obsługa tych 3 klas. I wydaje mi się, że podawane grzechy GR nie maja tu zastosowania.

MrBean Bean
  • Rejestracja:ponad 7 lat
  • Ostatnio:ponad 7 lat
  • Postów:117
0

No niestety EF potrzebuje jakiejś nakładki typu pseudo repo jeśli przepuszczasz to jeszcze przez warstwę serwisową to powinno być ok (nie wiem zgaduje). Poza tym jak my mamy mówić na tym samym poziomie abstrakcji. Ja nie widzę całości kodu, czy diagramu, dokumentacji ja muszę zgadywać. To że ty coś mi tam tłumaczysz po swojemu to nie znaczy że ja to sobie tak samo narysuję jak ty. Nie rozumiem czy nie można tego wrzucić na githuba?


▒▒▒▒▒▒▒▒█▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀█
▒▒▒▒▒▒▒█░▒▒▒▒▒▒▒▓▒▒▓▒▒▒▒▒▒▒░█
▒▒▒▒▒▒▒█░▒▒▓▒▒▒▒▒▒▒▒▒▄▄▒▓▒▒░█░▄▄
▒▒▄▀▀▄▄█░▒▒▒▒▒▒▓▒▒▒▒█░░▀▄▄▄▄▄▀░░█
edytowany 1x, ostatnio: MrBean Bean
JP
Jakoś na to nie wpadłem. Akurat nie używam GitHub-a ani żadnych innych publicznych repozytoriów ale to bardzo dobry pomysł. Się poprawię.
TA
  • Rejestracja:ponad 9 lat
  • Ostatnio:około rok
  • Postów:315
0

Wyglada jako tako (strasznie dlugie) - ale brakuje mi obslugi stronicowania/sortowania - co jak tabele bedzie miala 100000000 elelmentow? EntityState tez nie ma sensu (zadnego) - jesli chcesz aby grid pozwalal Ci updatetowac wiele elementow to zamiast robic to ustawianiem state dodaj sobie metode ktora wywola konkretne metody update/delete/create.

GR ma czesto sens - ma tylko jeden problem z DDD a raczej osobami stosujacymi DDD widzacymi cos innego niz DDD ;)

Btw zapytaj osoby od DDD jak dojsc na jakas ulice to odpowie Ci ze zle ze tam idziesz i czy na pewno masz parasol bo moze padac ;)

edytowany 2x, ostatnio: tamtamtu
neves
  • Rejestracja:ponad 21 lat
  • Ostatnio:około 2 godziny
  • Lokalizacja:Kraków
  • Postów:1114
2
tamtamtu napisał(a):

GR ma czesto sens - ma tylko jeden problem z DDD a raczej osobami stosujacymi DDD widzacymi cos innego niz DDD ;)

Btw zapytaj osoby od DDD jak dojsc na jakas ulice to odpowie Ci ze zle ze tam idziesz i czy na pewno masz parasol bo moze padac ;)

Problem wynika z nazewnictwa, ponieważ niestety doszło do sytuacji że dwa różne wzorce w obiegowej opinii mają tą samą nazwę: Repository.
Repository oryginalnie pochodzi z DDD, i występujące pod tą samą nazwą w książce Fowlera o wzorcach klasy enterpise.
Natomiast to co tutaj mamy to jest to DAO - Data Access Object, występujące pod nazwą Table Data Gateway u Fowlera, a potocznie nazywane repozytorium.
I przez to jest masa chaosu i nieporozumień, bo to są dwa różne wzorce znajdujące się podobnym miejscu w architekturze, a mające jednak trochę różne odpowiedzialności.


edytowany 2x, ostatnio: neves
JP
  • Rejestracja:ponad 7 lat
  • Ostatnio:4 miesiące
  • Postów:1065
0
tamtamtu napisał(a):

Wyglada jako tako (strasznie dlugie) - ale brakuje mi obslugi stronicowania/sortowania - co jak tabele bedzie miala 100000000 elelmentow?

To jest dla aplikacji WinForms i jak widać metoda GetList ma jako parametr "Func<T, bool> where" i zawsze (a nawet ZAWSZE) tam gdzie może być więcej rekordów idzie ograniczenie po datach a ja używam tego jako dostęp do słowników i w tych słownikach jest tyle danych ile jest (np województw w PL jest tyle ile jest i więcej nie będzie). Podobnie z np. kolorami.

EntityState tez nie ma sensu (zadnego) - jesli chcesz aby grid pozwalal Ci updatetowac wiele elementow to zamiast robic to ustawianiem state dodaj sobie metode ktora wywola konkretne metody update/delete/create.

Tylko, że taka postać umożliwia mi wykonanie Update na obiekcie root ze zmienionymi niektórymi obiektami w jakichś kolekcjach w obiekcie root.

Np Opcja ma listę Wartości. Pobieram rekord Opcja razem z listą wartości. Zmieniam wartości (edycja, dodanie...) i robię Update na obiekcie Opcja i wartości, które są zmienione, dodane, usunięte aktualizują się wg EntityState.

MrBean Bean
  • Rejestracja:ponad 7 lat
  • Ostatnio:ponad 7 lat
  • Postów:117
0
neves napisał(a):
tamtamtu napisał(a):

GR ma czesto sens - ma tylko jeden problem z DDD a raczej osobami stosujacymi DDD widzacymi cos innego niz DDD ;)

Btw zapytaj osoby od DDD jak dojsc na jakas ulice to odpowie Ci ze zle ze tam idziesz i czy na pewno masz parasol bo moze padac ;)

Problem wynika z nazewnictwa, ponieważ niestety doszło do sytuacji że dwa różne wzorce w obiegowej opinii mają tą samą nazwę: Repository.
Repository oryginalnie pochodzi z DDD, i występujące pod tą samą nazwą w książce Fowlera o wzorcach klasy enterpise.
Natomiast to co tutaj mamy to jest to DAO - Data Access Object, występujące pod nazwą Table Data Gateway u Fowlera, a potocznie nazywane repozytorium.
I przez to jest masa chaosu i nieporozumień, bo to są dwa różne wzorce znajdujące się podobnym miejscu w architekturze, a mające jednak trochę różne odpowiedzialności.

W książce Fowlera repo to mieszanka Query Object ora Metadata Maping.
Evans za to przestrzega przed tego typu elastycznymi repo opartych o specyfikacje.

Table Data Gateway to tylko przykrywka dla stringów sqla, czym w jego przypadku powinie się zająć normalny ORM.

To nie tak że repo nazywane jest potocznie DAO. Ludzie na siłę wpychają intefejs DAO w implementacje repo.

Tutaj mamy doczynienia z czymś więcej niż repo czy DAO. hmmm...


▒▒▒▒▒▒▒▒█▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀█
▒▒▒▒▒▒▒█░▒▒▒▒▒▒▒▓▒▒▓▒▒▒▒▒▒▒░█
▒▒▒▒▒▒▒█░▒▒▓▒▒▒▒▒▒▒▒▒▄▄▒▓▒▒░█░▄▄
▒▒▄▀▀▄▄█░▒▒▒▒▒▒▓▒▒▒▒█░░▀▄▄▄▄▄▀░░█
somekind
Moderator
  • Rejestracja:około 17 lat
  • Ostatnio:około 3 godziny
  • Lokalizacja:Wrocław
0

Poza tym, że nazwa jest bez sensu, bo to zwykłe DAO, które z repozytorium to nie ma nic wspólnego, to kod też jest bez sensu, bo to tylko taki wrapper, który wycina funkcjonalność znajdującego się pod spodem kontekstu ORMa, a poza tym niczego nie daje. Kawał dobrej, nikomu niepotrzebnej roboty.

A u Fowlera z repozytorium wszystko jest ok. Repozytorium jest abstrakcją nad warstwą dostępu do danych, nie jej częścią. Repozytorium daje dostęp do obiektów domenowych w formie przypominającej kolekcję i tyle.

Kliknij, aby dodać treść...

Pomoc 1.18.8

Typografia

Edytor obsługuje składnie Markdown, w której pojedynczy akcent *kursywa* oraz _kursywa_ to pochylenie. Z kolei podwójny akcent **pogrubienie** oraz __pogrubienie__ to pogrubienie. Dodanie znaczników ~~strike~~ to przekreślenie.

Możesz dodać formatowanie komendami , , oraz .

Ponieważ dekoracja podkreślenia jest przeznaczona na linki, markdown nie zawiera specjalnej składni dla podkreślenia. Dlatego by dodać podkreślenie, użyj <u>underline</u>.

Komendy formatujące reagują na skróty klawiszowe: Ctrl+B, Ctrl+I, Ctrl+U oraz Ctrl+S.

Linki

By dodać link w edytorze użyj komendy lub użyj składni [title](link). URL umieszczony w linku lub nawet URL umieszczony bezpośrednio w tekście będzie aktywny i klikalny.

Jeżeli chcesz, możesz samodzielnie dodać link: <a href="link">title</a>.

Wewnętrzne odnośniki

Możesz umieścić odnośnik do wewnętrznej podstrony, używając następującej składni: [[Delphi/Kompendium]] lub [[Delphi/Kompendium|kliknij, aby przejść do kompendium]]. Odnośniki mogą prowadzić do Forum 4programmers.net lub np. do Kompendium.

Wspomnienia użytkowników

By wspomnieć użytkownika forum, wpisz w formularzu znak @. Zobaczysz okienko samouzupełniające nazwy użytkowników. Samouzupełnienie dobierze odpowiedni format wspomnienia, zależnie od tego czy w nazwie użytkownika znajduje się spacja.

Znaczniki HTML

Dozwolone jest używanie niektórych znaczników HTML: <a>, <b>, <i>, <kbd>, <del>, <strong>, <dfn>, <pre>, <blockquote>, <hr/>, <sub>, <sup> oraz <img/>.

Skróty klawiszowe

Dodaj kombinację klawiszy komendą notacji klawiszy lub skrótem klawiszowym Alt+K.

Reprezentuj kombinacje klawiszowe używając taga <kbd>. Oddziel od siebie klawisze znakiem plus, np <kbd>Alt+Tab</kbd>.

Indeks górny oraz dolny

Przykład: wpisując H<sub>2</sub>O i m<sup>2</sup> otrzymasz: H2O i m2.

Składnia Tex

By precyzyjnie wyrazić działanie matematyczne, użyj składni Tex.

<tex>arcctg(x) = argtan(\frac{1}{x}) = arcsin(\frac{1}{\sqrt{1+x^2}})</tex>

Kod źródłowy

Krótkie fragmenty kodu

Wszelkie jednolinijkowe instrukcje języka programowania powinny być zawarte pomiędzy obróconymi apostrofami: `kod instrukcji` lub ``console.log(`string`);``.

Kod wielolinijkowy

Dodaj fragment kodu komendą . Fragmenty kodu zajmujące całą lub więcej linijek powinny być umieszczone w wielolinijkowym fragmencie kodu. Znaczniki ``` lub ~~~ umożliwiają kolorowanie różnych języków programowania. Możemy nadać nazwę języka programowania używając auto-uzupełnienia, kod został pokolorowany używając konkretnych ustawień kolorowania składni:

```javascript
document.write('Hello World');
```

Możesz zaznaczyć również już wklejony kod w edytorze, i użyć komendy  by zamienić go w kod. Użyj kombinacji Ctrl+`, by dodać fragment kodu bez oznaczników języka.

Tabelki

Dodaj przykładową tabelkę używając komendy . Przykładowa tabelka składa się z dwóch kolumn, nagłówka i jednego wiersza.

Wygeneruj tabelkę na podstawie szablonu. Oddziel komórki separatorem ; lub |, a następnie zaznacz szablonu.

nazwisko;dziedzina;odkrycie
Pitagoras;mathematics;Pythagorean Theorem
Albert Einstein;physics;General Relativity
Marie Curie, Pierre Curie;chemistry;Radium, Polonium

Użyj komendy by zamienić zaznaczony szablon na tabelkę Markdown.

Lista uporządkowana i nieuporządkowana

Możliwe jest tworzenie listy numerowanych oraz wypunktowanych. Wystarczy, że pierwszym znakiem linii będzie * lub - dla listy nieuporządkowanej oraz 1. dla listy uporządkowanej.

Użyj komendy by dodać listę uporządkowaną.

1. Lista numerowana
2. Lista numerowana

Użyj komendy by dodać listę nieuporządkowaną.

* Lista wypunktowana
* Lista wypunktowana
** Lista wypunktowana (drugi poziom)

Składnia Markdown

Edytor obsługuje składnię Markdown, która składa się ze znaków specjalnych. Dostępne komendy, jak formatowanie , dodanie tabelki lub fragmentu kodu są w pewnym sensie świadome otaczającej jej składni, i postarają się unikać uszkodzenia jej.

Dla przykładu, używając tylko dostępnych komend, nie możemy dodać formatowania pogrubienia do kodu wielolinijkowego, albo dodać listy do tabelki - mogłoby to doprowadzić do uszkodzenia składni.

W pewnych odosobnionych przypadkach brak nowej linii przed elementami markdown również mógłby uszkodzić składnie, dlatego edytor dodaje brakujące nowe linie. Dla przykładu, dodanie formatowania pochylenia zaraz po tabelce, mogłoby zostać błędne zinterpretowane, więc edytor doda oddzielającą nową linię pomiędzy tabelką, a pochyleniem.

Skróty klawiszowe

Skróty formatujące, kiedy w edytorze znajduje się pojedynczy kursor, wstawiają sformatowany tekst przykładowy. Jeśli w edytorze znajduje się zaznaczenie (słowo, linijka, paragraf), wtedy zaznaczenie zostaje sformatowane.

  • Ctrl+B - dodaj pogrubienie lub pogrub zaznaczenie
  • Ctrl+I - dodaj pochylenie lub pochyl zaznaczenie
  • Ctrl+U - dodaj podkreślenie lub podkreśl zaznaczenie
  • Ctrl+S - dodaj przekreślenie lub przekreśl zaznaczenie

Notacja Klawiszy

  • Alt+K - dodaj notację klawiszy

Fragment kodu bez oznacznika

  • Alt+C - dodaj pusty fragment kodu

Skróty operujące na kodzie i linijkach:

  • Alt+L - zaznaczenie całej linii
  • Alt+, Alt+ - przeniesienie linijki w której znajduje się kursor w górę/dół.
  • Tab/⌘+] - dodaj wcięcie (wcięcie w prawo)
  • Shit+Tab/⌘+[ - usunięcie wcięcia (wycięcie w lewo)

Dodawanie postów:

  • Ctrl+Enter - dodaj post
  • ⌘+Enter - dodaj post (MacOS)