fbpx

Cześć!

W dwudziestym dziewiątym wpisie na blogu poruszę temat, od którego nie jeden programista zaczynał swoją programistyczną karierę. W tym gronie też jestem ja i pamiętam, jak w 2013 roku gorączkowo w C# zgłębiałem temat CRUD do portfolio. Wtedy zadawałem sobie tytułowe pytanie — czym jest CRUD?

Dziś podchodzę do tematu z uśmiechem na ustach, bo wiem jak dużą ekscytację miałem, gdy pierwszy raz coś udało mi się odczytać dane, zmodyfikować i usunąć je oraz dodać do zbioru.

No właśnie, zostawiam Ci kilka zagadnień na zachętę i lecimy:

  • Czym jest CRUD?
  • Jak wygląda architektura MVC na prostym przykładzie?
  • W jaki sposób zaimplementować CRUD?

Odpalaj swoje IDE i zabieramy się do pracy.

Zatrzymaj się!


Książka „Programistą być” to obowiązkowa pozycja dla każdego, kto interesuje się tematem programowania!

Z książki dowiesz się między innymi o tym:

  • Czy matematykastudia techniczne i język angielski są konieczne do tego, by rozpocząć pracę jako programista?
  • Gdzie szukać informacji o programowaniu i w jaki sposób się uczyć?
  • Jak znaleźć pierwszą pracę i w jaki sposób rozwijać swój programistyczny potencjał?
  • Czym na co dzień zajmuje się programista?
  • Czy każdy może zostać programistą?

I wiele, wiele więcej…


Czym jest CRUD — Wprowadzenie

CRUD to akronim od słów Create, Read (Retrieve), Update, Delete (Destroy). Dotyczy on operacji, jakie programista wykonuje na zbiorze danych, czyli może być to plik tekstowy, baza danych czy struktura danych, której cykl życia zależy od cyklu życia uruchomionej aplikacji. Najczęściej jednak używany jest w kontekście pracy z danymi przechowywanymi w bazie danych.

Do tego oczywiście może być używany w protokole komunikacyjnym http. W tabeli poniżej rozpisałem metody SQL i te z protokołu HTTP do obsługi operacji CRUD.

Operacje CRUD w SQL i HTTP

Jeśli popatrzymy na CRUD bardziej szczegółowo, to będzie to wyglądało następująco:

  • Create (stwórz) – tworzenie użytkownika, produktu, zamówienia, roli użytkownika czy faktury. Ten sam widok użyty będzie użyty do tworzenia oraz aktualizacji danych;
  • Read (odczytaj) – odczytanie danych do widoku użytkownika, produktu, zamówienia, roli użytkownika czy faktury. Zazwyczaj wykorzystywany do podglądu danych lub do pobrania danych do edycji do formularza;
  • Update (zaktualizuj) – aktualizacja danych użytkownika, produktu, zamówienia, roli użytkownika czy faktury, która wykorzystuje ten sam formularz co Create i dane pobierane są za pomocą operacji Read;
  • Delete (usuwanie) – usunięcie użytkownika, produktu, zamówienia, roli użytkownika czy faktury. Zazwyczaj usunięcie nie jest fizycznym skasowaniem danych z tabeli. Polega to na zmianie flagi, która odpowiada za widoczność danych. Dla przykładu flaga Active, która w momencie usuwania zmienia swój stan z 1 na 0. Dane usunięte mogą być jednak używane dalej w funkcji Read przykładowo do raportów;

Każda z tych czterech operacji jest bazową funkcjonalnością aplikacji, jakich używamy na co dzień. Co ciekawe, wielkie systemy mają w sobie dziesiątki implementacji CRUD. Wyobraź sobie prosty sklep internetowy. Dodajemy tam produkty, składamy zamówienia, zapisujemy klientów, dodajemy rabaty. Wszystko to obsługuje osobna funkcjonalność, czyli CRUD.

CRUD jednak nie jest wszystkim, co taka aplikacja zawierać musi, ponieważ dochodzą walidacje, logi, testy jednostkowe i cała logika biznesowa składa się na kompletne rozwiązanie wdrażane na produkcji. 

Czym jest CRUD — Implementacja

Czas pokazać CRUD od strony kodu, gdzie do implementacji używać będę języka C# oraz framework dotNET Core wraz z bazą danych MSSQL. Język nie jest tu istotny, najważniejsze dla mnie w tym momencie jest to, byś Ty ogarniał koncepcję. Polecam Ci zaimplementować CRUD w używanym przez Ciebie języku i wrzucił go sobie do portfolio. Będziesz mieć co pokazać na rozmowie o pracę, a na stanowisko juniora nie powinno zabraknąć zagadnień przedstawionych w dzisiejszym artykule.

Baza danych

Zacznę od stworzenia bazy danych i aby to zrobić, polecam zainstalować SQL Server Management Studio (SSMS) w dowolnej wersji, ale ja preferuję wersję v18. Następnie utworzę tabelę o nazwie Product, gdzie będę chciał dodawać, usuwać, modyfikować i odczytywać dane o produktach. W tabeli znajdą się następujące pola:

  • ID;
  • Name;
  • Description;
  • Code;
  • Count;
  • ProduceDate;
  • Active;

Oczywiście może być ich więcej, jednak do zrozumienia zagadnienia tyle w zupełności wystarczy. Cały skrypt tworzący bazę danych oraz tabelę przedstawiam poniżej. Nie ma potrzeby zagłębiać się w szczegóły kodu SQL. 

USE [master]
GO

CREATE DATABASE [CRUD]
 CONTAINMENT = NONE
 ON  PRIMARY 
( NAME = N'CRUD', FILENAME = N'C:\Program Files\Microsoft SQL Server\MSSQL15.MSSQLSERVER\MSSQL\DATA\CRUD.mdf' , SIZE = 8192KB , MAXSIZE = UNLIMITED, FILEGROWTH = 65536KB )
 LOG ON 
( NAME = N'CRUD_log', FILENAME = N'C:\Program Files\Microsoft SQL Server\MSSQL15.MSSQLSERVER\MSSQL\DATA\CRUD_log.ldf' , SIZE = 8192KB , MAXSIZE = 2048GB , FILEGROWTH = 65536KB )
 WITH CATALOG_COLLATION = DATABASE_DEFAULT
GO
ALTER DATABASE [CRUD] SET COMPATIBILITY_LEVEL = 150
GO
IF (1 = FULLTEXTSERVICEPROPERTY('IsFullTextInstalled'))
begin
EXEC [CRUD].[dbo].[sp_fulltext_database] @action = 'enable'
end
GO
ALTER DATABASE [CRUD] SET ANSI_NULL_DEFAULT OFF 
GO
ALTER DATABASE [CRUD] SET ANSI_NULLS OFF 
GO
ALTER DATABASE [CRUD] SET ANSI_PADDING OFF 
GO
ALTER DATABASE [CRUD] SET ANSI_WARNINGS OFF 
GO
ALTER DATABASE [CRUD] SET ARITHABORT OFF 
GO
ALTER DATABASE [CRUD] SET AUTO_CLOSE OFF 
GO
ALTER DATABASE [CRUD] SET AUTO_SHRINK OFF 
GO
ALTER DATABASE [CRUD] SET AUTO_UPDATE_STATISTICS ON 
GO
ALTER DATABASE [CRUD] SET CURSOR_CLOSE_ON_COMMIT OFF 
GO
ALTER DATABASE [CRUD] SET CURSOR_DEFAULT  GLOBAL 
GO
ALTER DATABASE [CRUD] SET CONCAT_NULL_YIELDS_NULL OFF 
GO
ALTER DATABASE [CRUD] SET NUMERIC_ROUNDABORT OFF 
GO
ALTER DATABASE [CRUD] SET QUOTED_IDENTIFIER OFF 
GO
ALTER DATABASE [CRUD] SET RECURSIVE_TRIGGERS OFF 
GO
ALTER DATABASE [CRUD] SET  DISABLE_BROKER 
GO
ALTER DATABASE [CRUD] SET AUTO_UPDATE_STATISTICS_ASYNC OFF 
GO
ALTER DATABASE [CRUD] SET DATE_CORRELATION_OPTIMIZATION OFF 
GO
ALTER DATABASE [CRUD] SET TRUSTWORTHY OFF 
GO
ALTER DATABASE [CRUD] SET ALLOW_SNAPSHOT_ISOLATION OFF 
GO
ALTER DATABASE [CRUD] SET PARAMETERIZATION SIMPLE 
GO
ALTER DATABASE [CRUD] SET READ_COMMITTED_SNAPSHOT OFF 
GO
ALTER DATABASE [CRUD] SET HONOR_BROKER_PRIORITY OFF 
GO
ALTER DATABASE [CRUD] SET RECOVERY FULL 
GO
ALTER DATABASE [CRUD] SET  MULTI_USER 
GO
ALTER DATABASE [CRUD] SET PAGE_VERIFY CHECKSUM  
GO
ALTER DATABASE [CRUD] SET DB_CHAINING OFF 
GO
ALTER DATABASE [CRUD] SET FILESTREAM( NON_TRANSACTED_ACCESS = OFF ) 
GO
ALTER DATABASE [CRUD] SET TARGET_RECOVERY_TIME = 60 SECONDS 
GO
ALTER DATABASE [CRUD] SET DELAYED_DURABILITY = DISABLED 
GO
ALTER DATABASE [CRUD] SET ACCELERATED_DATABASE_RECOVERY = OFF  
GO
EXEC sys.sp_db_vardecimal_storage_format N'CRUD', N'ON'
GO
ALTER DATABASE [CRUD] SET QUERY_STORE = OFF
GO
USE [CRUD]
GO

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[Product](
	[ID] [int] IDENTITY(1,1) NOT NULL,
	[Name] [nvarchar](50) NOT NULL,
	[Description] [nvarchar](max) NOT NULL,
	[Code] [nchar](10) NOT NULL,
	[Count] [int] NULL,
	[ProduceDate] [datetime] NOT NULL,
	[Active] [bit] NOT NULL,
 CONSTRAINT [PK_Product] PRIMARY KEY CLUSTERED 
(
	[ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
USE [master]
GO
ALTER DATABASE [CRUD] SET  READ_WRITE 
GO

Kontroler

Każda operacja będzie posiadać swój własny kontroler. W skrócie kontroler przyjmuje dane wejściowe od użytkownika i reaguje na jego poczynania, zarządzając aktualizacje modelu oraz odświeżenie widoków. Mam w planach stworzyć kilka wpisów na temat architektury oprogramowania i znajdzie się tam miejsce na wzorzec architektoniczny MVC (Model-View-Controller). Szybko pokażę Ci grafikę, która pomoże zrozumieć „przepływ” w CRUDowej aplikacji.

CRUD - rysunek pokazujący przepływ danych

  • Użytkownik używa kontrolera (CZERWONA STRZAŁKA);
  • Kontroler korzysta z modelu/modyfikuje go (ZIELONA STRZAŁKA)
  • Model aktualizuje widok (POMARAŃCZOWA STRZAŁKA);
  • Widok prezentuje się użytkownikowi (NIEBIESKA STRZAŁKA);

W moim przypadku kontroler będzie posiadał następujące metody:

  • Index (get) – wczytanie widoku głównego, w której znajduje się tabela wraz z wszystkimi produktami;

  • Details (get) – wczyta widok szczegółów, w którym możemy sprawdzić detale konkretnego jednego produktu;
  • Create (get) – wczyta widok pozwalający utworzyć nowy produkt;
  • Create (post) – przetworzy widok tworzenia nowego produktu podczas kliknięcia przycisku Create;
  • Edit (get) – wczyta widok pozwalający edytować istniejący produkt;
  • Edit (post) – przetworzy widok edytowania istniejącego produktu podczas kliknięcia przycisku Edit;
  • Delete (get) – wczyta widok pozwalający usunąć istniejący produkt;
  • Delete (post) – przetworzy widok usuwania istniejącego produktu podczas kliknięcia przycisku Delete;

Nazwy kontrolerów widoczne są również w adresie URL w przeglądarce. Dla przykładu, jeśli chcę edytować produkt o ID 5, to adres będzie wyglądał następująco: https://mojadomena.pl/Edit?id=5. Szczegóły produktu o ID 5 sprawdzę pod adresem https://mojadomena.pl/Details?id=5. I tak dalej. Kod klasy ProductController dostępny jest poniżej.

using Microsoft.AspNetCore.Mvc;

namespace CRUD.Controllers
{
    public class ProductController : Controller
    {
        // GET: ProductController
        public ActionResult Index()
        {
            return View();
        }

        // GET: ProductController/Details/5
        public ActionResult Details(int id)
        {
            return View();
        }

        // GET: ProductController/Create
        public ActionResult Create()
        {
            return View();
        }

        // POST: ProductController/Create
        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult Create(IFormCollection collection)
        {
            try
            {
                return RedirectToAction(nameof(Index));
            }
            catch
            {
                return View();
            }
        }

        // GET: ProductController/Edit/5
        public ActionResult Edit(int id)
        {
            return View();
        }

        // POST: ProductController/Edit/5
        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult Edit(int id, IFormCollection collection)
        {
            try
            {
                return RedirectToAction(nameof(Index));
            }
            catch
            {
                return View();
            }
        }

        // GET: ProductController/Delete/5
        public ActionResult Delete(int id)
        {
            return View();
        }

        // POST: ProductController/Delete/5
        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult Delete(int id, IFormCollection collection)
        {
            try
            {
                return RedirectToAction(nameof(Index));
            }
            catch
            {
                return View();
            }
        }
    }
}

Model

Na potrzeby tej prostej aplikacji będę tworzył jedną klasę będącą modelem produktu. Zawierać ona musi dokładnie te same pola, które znajdują się w tabeli Product w bazie danych. Ważne jest, by w modelu dokładnie określić format daty ProduceDate. Pozostałe pola są zrozumiałe. Modelu tego będziemy używać do aktualizacji oraz dodawania nowych produktów i przede wszystkim do prezentowania danych.

using System.ComponentModel.DataAnnotations;

namespace CRUD.Model
{
    public class ProductModel
    {
        public int ID { get; set; }
        public string Name { get; set; }
        public string Description { get; set; }
        public string Code { get; set; }
        public int Count { get; set; }

        [DataType(DataType.Date)]
        [DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
        public DateTime ProduceDate { get; set; }

        public bool Active { get; set; } 
    }
}

Widoki

Widoki to największa część kodu, ponieważ każda metoda GET z kontrolera ma swój odpowiednik w widoku. W dotNET do opisywania widoków używa się języka HTML ze specjalnie przygotowanym silnikiem renderującym o nazwie RAZOR. Pozwala on łączyć kod języka C# z językiem HTML, więc jeśli uczysz się innego języka, takiego jak Python, JS, PHP czy Java, to Twój kod będzie wyglądał inaczej. Najważniejsze jest, by zrozumieć koncepcję tego, czym jest CRUD i jak ważna jest jego znajomość na początku kariery programisty. Poniżej zobacz kod wraz z widokiem z przeglądarki.

Strona główna

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.min.css" />
    <link rel="stylesheet" href="~/css/site.css" asp-append-version="true" />
    <link rel="stylesheet" href="~/CRUD.styles.css" asp-append-version="true" />
</head>
<body>
    <div class="container">
        <main role="main" class="pb-3">
            @RenderBody()
        </main>
    </div>

    <script src="~/lib/jquery/dist/jquery.min.js"></script>
    <script src="~/lib/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
    <script src="~/js/site.js" asp-append-version="true"></script>

    @await RenderSectionAsync("Scripts", required: false)
</body>
</html>

Edycja

@page
@model CRUD.Pages.EditModel

@{
    ViewData["Title"] = "Edit";
}

<h1>Edit</h1>

<h4>ProductModel</h4>
<hr />
<div class="row">
    <div class="col-md-4">
        <form method="post">
            <div asp-validation-summary="ModelOnly" class="text-danger"></div>
            <input type="hidden" asp-for="ProductModel.ID" />
            <div class="form-group">
                <label asp-for="ProductModel.Name" class="control-label"></label>
                <input asp-for="ProductModel.Name" class="form-control" />
                <span asp-validation-for="ProductModel.Name" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="ProductModel.Description" class="control-label"></label>
                <input asp-for="ProductModel.Description" class="form-control" />
                <span asp-validation-for="ProductModel.Description" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="ProductModel.Code" class="control-label"></label>
                <input asp-for="ProductModel.Code" class="form-control" />
                <span asp-validation-for="ProductModel.Code" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="ProductModel.Count" class="control-label"></label>
                <input asp-for="ProductModel.Count" class="form-control" />
                <span asp-validation-for="ProductModel.Count" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="ProductModel.ProduceDate" class="control-label"></label>
                <input asp-for="ProductModel.ProduceDate" class="form-control" />
                <span asp-validation-for="ProductModel.ProduceDate" class="text-danger"></span>
            </div>
            <div class="form-group form-check">
                <label class="form-check-label">
                    <input class="form-check-input" asp-for="ProductModel.Active" /> @Html.DisplayNameFor(model => model.ProductModel.Active)
                </label>
            </div>
            <div class="form-group">
                <input type="submit" value="Save" class="btn btn-primary" />
            </div>
        </form>
    </div>
</div>

<div>
    <a asp-page="./Index">Back to List</a>
</div>

@section Scripts {
    @{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}

Usuwanie

@page
@model CRUD.Pages.DeleteModel

@{
    ViewData["Title"] = "Delete";
}

<h1>Delete</h1>

<h3>Are you sure you want to delete this?</h3>
<div>
    <h4>ProductModel</h4>
    <hr />
    <dl class="row">
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.ProductModel.Name)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.ProductModel.Name)
        </dd>
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.ProductModel.Description)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.ProductModel.Description)
        </dd>
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.ProductModel.Code)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.ProductModel.Code)
        </dd>
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.ProductModel.Count)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.ProductModel.Count)
        </dd>
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.ProductModel.ProduceDate)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.ProductModel.ProduceDate)
        </dd>
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.ProductModel.Active)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.ProductModel.Active)
        </dd>
    </dl>
    
    <form method="post">
        <input type="hidden" asp-for="ProductModel.ID" />
        <input type="submit" value="Delete" class="btn btn-danger" /> |
        <a asp-page="./Index">Back to List</a>
    </form>
</div>


Dodawanie

@page
@model CRUD.Pages.CreateModel

@{
    ViewData["Title"] = "Create";
}

<h1>Create</h1>

<h4>ProductModel</h4>
<hr />
<div class="row">
    <div class="col-md-4">
        <form method="post">
            <div asp-validation-summary="ModelOnly" class="text-danger"></div>
            <div class="form-group">
                <label asp-for="ProductModel.Name" class="control-label"></label>
                <input asp-for="ProductModel.Name" class="form-control" />
                <span asp-validation-for="ProductModel.Name" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="ProductModel.Description" class="control-label"></label>
                <input asp-for="ProductModel.Description" class="form-control" />
                <span asp-validation-for="ProductModel.Description" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="ProductModel.Code" class="control-label"></label>
                <input asp-for="ProductModel.Code" class="form-control" />
                <span asp-validation-for="ProductModel.Code" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="ProductModel.Count" class="control-label"></label>
                <input asp-for="ProductModel.Count" class="form-control" />
                <span asp-validation-for="ProductModel.Count" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="ProductModel.ProduceDate" class="control-label"></label>
                <input asp-for="ProductModel.ProduceDate" class="form-control" />
                <span asp-validation-for="ProductModel.ProduceDate" class="text-danger"></span>
            </div>
            <div class="form-group form-check">
                <label class="form-check-label">
                    <input class="form-check-input" asp-for="ProductModel.Active" /> @Html.DisplayNameFor(model => model.ProductModel.Active)
                </label>
            </div>
            <div class="form-group">
                <input type="submit" value="Create" class="btn btn-primary" />
            </div>
        </form>
    </div>
</div>

<div>
    <a asp-page="Index">Back to List</a>
</div>

@section Scripts {
    @{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}


Szczegóły

@page
@model CRUD.Pages.DetailsModel

@{
    ViewData["Title"] = "Details";
}

<h1>Details</h1>

<div>
    <h4>ProductModel</h4>
    <hr />
    <dl class="row">
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.ProductModel.Name)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.ProductModel.Name)
        </dd>
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.ProductModel.Description)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.ProductModel.Description)
        </dd>
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.ProductModel.Code)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.ProductModel.Code)
        </dd>
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.ProductModel.Count)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.ProductModel.Count)
        </dd>
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.ProductModel.ProduceDate)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.ProductModel.ProduceDate)
        </dd>
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.ProductModel.Active)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.ProductModel.Active)
        </dd>
    </dl>
</div>
<div>
    <a asp-page="./Edit" asp-route-id="@Model.ProductModel.ID">Edit</a> |
    <a asp-page="./Index">Back to List</a>
</div>

Czym jest CRUD — Podsumowanie

CRUD może wydawać się trudny na początku, ale jeśli szukasz pierwszej pracy, to koniecznie dodaj swoje rozwiązanie do portfolio! Możesz dzięki takiemu projektowi zrobić swoją pierwszą aplikację, na przykład często polecana na początku aplikacja do tworzenia listy zadań lub listy zakupów. CRUD w tym przypadku sprawdzi się idealnie jako szablon na start. 

Oczywiście ten projekt znajdziesz na moim koncie GitHub, więc jeśli pracujesz w języku C#, to koniecznie go pobieraj i zmodyfikuje pod własne potrzeby. Daj znać w komentarzu, czy wpis Ci pomógł, bo teraz już mam nadzieję, że wiesz, czym jest CRUD. Z tego miejsca zapraszam Cię do kolejnego wpisu, jakim jest wpis na temat liczb zmiennoprzecinkowych.

PS. Będę wdzięczny za komentarze pod tym postem!

PS2. Czeka też na Ciebie prezent. Wystarczy, że zapiszesz się na Newsletter poniżej!

PS3. Dołącz do naszej grupy na Facebook!

Subskrybuj

Zapisz się do Mailowej Akademii Programowania.

MAP to jedyne takie miejsce, gdzie co niedzielę otrzymasz rozwiązanie programistycznego zadania.

Do tego otrzymasz darmowe konsultacje w przypadku odpowiedzi na moje maile. Wspólnie staniemy się lepszymi programistami!

Nie przegap i dołącz już dziś do 747 osób będących w MAP!

Brzmi ciekawie? Nie zwlekaj i dołącz już teraz!

PS. Na start otrzymasz bonus w formie PDF z rozwiązanymi dziesięcioma programistycznymi problemami!

Autor

👨‍💻 .NET and Python programming passionate 🏦 Digtial Banking Solutions 🎓 Student 📊 Psychology 📚 Bookworm 🏠 Warsaw

Napisz komentarz

Witryna wykorzystuje Akismet, aby ograniczyć spam. Dowiedz się więcej jak przetwarzane są dane komentarzy.

Programistą być to przewodnik po programistycznym świecie, który każdy zainteresowany programowaniem powinien przeczytać!

X