You are on page 1of 167

Table

of Contents
Introduction 1.1
İlk Uygulamanız 1.2
SDK'yi indirin 1.2.1
C# ile Merhaba Dünya 1.2.2
ASP.NET Core Projesi Oluşturma 1.2.3
MVC temelleri 1.3
Kontrolör Oluşturma 1.3.1
Model Oluşturma 1.3.2
Görüntü(View) oluşturma 1.3.3
Servis sınıfı ekleme 1.3.4
Bağımlı Enjeksiyon Kullanma 1.3.5
Kontrolörü Tamamlama 1.3.6
Ekstra paket Ekleme 1.4
Veri Tabanı Kullanma 1.5
Veri Tabanına Bağlanma 1.5.1
İçeriği Güncelleme 1.5.2
Göç Oluşturma 1.5.3
Yeni Servis Sınıfı Oluşturma 1.5.4
Yeni Özellikler Ekleme 1.6

2
Yeni Yapılacak Maddesi Ekleme 1.6.1
Maddeleri Onay Kutusu ile Tamamlama 1.6.2
Güvenlik ve Kimlik 1.7
Facebook Girişi Ekleme 1.7.1
Kimlik Kontrolü 1.7.2
Uygulama İçerisinde Kimlik Kullanma 1.7.3
Role Göre Yetkilendirme 1.7.4
Otomatik Test 1.8
Unit Testi 1.8.1
Integration Testi 1.8.2
Uygulamanın Dağıtılması 1.9
Azure'a Dağıtma 1.9.1
Docker ile Dağıtma 1.9.2
Sonuç 1.10

3
Introduction

Giriş
ASP.NET Core El Kitabı kitabına başladığınız için
teşekkürler. Bu kısa kitabı yazmamın amacı ASP.NET
Core 2.0 hakkında bilgi almak isteyen yazılımcılara kolay
bir başlangıç yapmalarını sağlamak. Bu kitapta yeni
framework ve bu framework ile nasıl web uygulamaları
yapılacağını göreceksiniz.

Bu kitap aşağıdaki yapıda hazırlanmıştır. Bu projede bir


web to-do uygulaması yapacağız:

MVC patern temeli ( Model-View-Controller) The


basics of the MVC (Model-View-Controller) pattern
Önyüz(Front-end) ile Arka uç(Back-end) birlikte nasıl
çalışır.
Dependency injection(Bağımlılık Enjeksiyonu) nasıl
yapılır ve neden kullanışlıdır.
Veri tabanına nasıl yazılır? Veri tabanından nasıl
okunur?
Giriş, kayıt ve güvenlik nasıl yapılır.
Yaptığımız projeyi nasıl web ortamına taşırız.

Bu projeyi tamamlamak için ASP.NET core ile alakalı hiç


birşey bilmek zorunda değilsiniz.

4
Introduction

Başlamadan Önce
Bu projenin tamamlanmış halin
GitHub'da(https://www.github.com/nbarbettini/little-
aspnetcore-todo) mevcut, bilgisayarınıza indirip kendi
versiyonunuz ile kıyaslayabilirsiniz. GitHub .

Bu kitabın ingilizcesine
buradan(https://github.com/nbarbettini/little-aspnetcore-
book) erişebilirsiniz. Ben çeviri işlemlerini yapacağım ve
bunu Türkçe kitap olarak web ortamında yayınlayacağım.

Bu kitap problemler düzeltildikçe yenilenmektedir. İngilizce


yeni versiyonuna (littleasp.net/book) 'dan ulaşabilirsiniz.
Kitabın en son sayfasına bakarak versiyonuna ve değişen
bölümlere ulaşabilirsiniz.

Bu kitap kimin için ?


Eğer yazılım ile uğraşıyorsanız bu kitap size modern web
uygulamarının nasıl yapılacağına dair bir giriş
niteliğindedir. Web uygulamalarının nasıl yapılacağını
0'dan öğreneceksiniz. Doğal olarak kısa bir kitap
olduğundan bunun içerisinde herşeyi bulamayabilirsiniz.
Fakat bu daha ileri uygulamalar için bir başlangıç
niteliğindedir.

5
Introduction

Eğer ASP.NET MVC geliştiriciyseniz, lütfen evinizdeymiş


gibi hissedin. ASP.NET Core sizin bildiklerinize ek olarak
yeni araçlar ve basitlikler getirmekte. Aşağıda bazı
değişikliklere değineceğim.

Daha önce hangi dilde çalıştığınıza bağlı olmaksızın bu


kitap size basit bir ASP.NET uygulamasının nasıl
yapılacağını gösterecektir. Bu kitap ile Ön-yüz ve Arka-
uç'u nasıl yapılandıracağınızı, birbirleriyle iletişimi nasıl
kuracağınız, database'e nasıl bağlanacağınızı, nasıl test
edeceğinizi ve web ortamına nasıl taşıyacağınızı
öğreneceksiniz.

ASP.NET CORE nedir?


ASP.NET Core Microsoft tarafından web uygulamaları
inşa etmek için yapılmış bir iskelettir(framework). Bu
iskelet ile APIler ve Microservisler oluşturulabilir. İskelet
çokça bilinen MVC(Model-View-Controller), bağımlılık
enjeksiyonu gibi paternleri kullanmaktadır. Apache 2.0
lisansına sahiptir ki bu da kaynağın ulaşılabilir ve bedava
olduğunu gösterir.

ASP.NET Core Microsof .NET runtime üzerine kuruludur.


Bu Java Virtual Machine benzeridir. Böylece birçok dil ile
ASP.NET Core ugulamaları yazabilirsiniz. Örneğin C#,
Visual Basic, F# gibi. C# en popüleri olduğundan dolayı

6
Introduction

bu kitapta C# kullanılacaktır. Bunun yanında ASP.Net


Core ugulamasını Windows, Mac ve Linux işletim
sistemlerinde kullanabilirsiniz.

Neden başka bir web isketine


ihtiyacımız var ?
Hali hazırda bir çok web iskeleti bulunmakta. Örneğin
Node/Express, Spring, Ruby on Rails, Django, Laravel ve
daha bir çoğu. ASP.Net Core'un bu iskeletlere üstünlüğü
nedir ?

Hız. ASP.NET Core hızlıdır. .NET kodu derlenmiş


olduğundan dolayı Interpreted(yorumlanmış) dillere
göre ( Javascript, Ruby ) daha hızlıdır. Ayrıca
ASP.NET multitreading ve asenkron işler(tasks) için
en uygun hale getirilmiştir. Bundan dolayı Node.js'e
göre 5-10 kat arası daha hızlıdır.

Ecosystem. ASP.NET Core belki yeni olabilir fakat


.NET çok uzun süredir kullanılmaktadir. Bundan olayı
binlerce paket Nuget ( paket yönetimi ) vasıtasıyla
kullanılabilmektedir. Nuget npm, Ruby gems veya
Maven tarzı bir paket yönetim uygulamasıdır. Örneğin
şu anda JSON deserialization, database
bağlayıcıları, PDF oluşturma araçları ve binlercesi.

7
Introduction

Güvenlik. Microsoft takımın güvenliği oldukça ciddiye


almaktadır. ASP.NET core baştan sona güvenlik
üzerine kurulmuştur. Örneğin input sterilizasyonu
veya cross-site request forger(XSRF) otomatik olarak
çalışmaktadır. Böylece sizin bunları kontrol etmenize
gerek yoktur. Ayrıca Static Typing olduğundan dolayı
.NET derleyicinin tüm özelliklerinden
faydalanabilirsiniz. Static Typing için tüm yapının
düzgün olmasını sağlamaktadır. Böylece data yapısı
üzerinde istenilmeyen şeyler yaptığınızda doğrudan
hata alacaksınız.

.NET Core ve .NET Standart


Bu kitapta ASP.NET core (web iskeleti) öğreneceksiniz.
Bazen .NET runtime terimini duyabilirsiniz. Ayrıca bu kitap
boyunca .NET Core ve .NET Standart terimlerini
duyabilirsiniz. Bu kafanızda bir karışıklığa neden olabilir.
Kısaca şu şekilde anlatmaya çalışayım:

.NET Standard Platform bağımsız .NET'te bulunan API


leri tanımlar. .NET standart kod veya fonksiyonaliteye dair
birşey söylememektedir. Sadece API tanımıdır. Birçok
çeşit "versiyon" veya "kadame" .NET Standart

8
Introduction

bulunmakta. Bu Standart kaç tane API olduğundan


bahsetmektedir. Örneğin .NET Standar 2.0 .NET Standart
1.5'e göre daha fazla API içermektedir.

.NET Core ise Windows, Mac veya Linux'a kurulan .NET


runtime'dır. .NET standard'da tanımlanan interfaceleri
burada anlamlandırırsınız(implement) Tabi bu
anlamlandırma platform tabanlıdır. Böylece siz .NET
Core'u kendi platformunuza göre kurarsınız. Örneğin
.NET Framework .NET Standard'ın sadece Windows için
anlamlandırdığı(implement) iskelettir. .NET Core çıkana
kadar yalnızca .NET Framework vardı.

Eğer isimlendirmekte kafanız karışıyorsa çok takılmayın


bu konuya. Projede daha iyi anlayacaksınız.

ASP.NET 4 geliştiricileri için


not
Eğer daha önce ASP.NET kullanmadıysanız lütfen bir
sonraki bölüme geçiniz!

ASP.NET Core tamamen ASP.NET'in baştan yazılmış


halidir, yeni nesil teknolojilere daha iyi ayak uydurabilmek
ve System.Web'den, IIS ve Windows'tan ayırabilmek için

9
Introduction

yapımıştır. ASP.NET 4 de OWIN/Kata olaylarını


hatırlarsınız. Bu Katana projesi ASP.NET 5 oldu ve buna
ASP.NET Core dediler

Katana projesine göre Startup sınıfı öne alındı ve artık


Application_Start veya Global.asax kullanmamıza

gerek kalmadı.Tüm iletişim hattı middleware üzerinden


sağlanıyor ve artık MVC ve WEB API ayrımı kalktı bu da
demek oluyor ki Controllerdan doğrudan View veya status
code veya data döndürebiliyoruz. Bağımlılık Enjeksiyonu (
Dependency Injection) doğrudan geliyor böylece hiç bir
StructureMap veya Niject tarzı container yüklemenize
gerek kalmadı. Ayrıca tüm framework hız ve runtime
verimi üzerinde iyileştirildi.

Bu kadar giriş bölümü yeterli. Şimdi ASP.NET Core'un


derinliklerine inebiliriz.

NOT: Türkçeleştirme ile alakalı yanlış yaptığım veya sizin


gözünüze çarpan bir sorun var ise lütfen yeni bir github
konusu(issue) açın.

10
İlk Uygulamanız

İlk Uygulamanız
ASP.NET Core kullanarak ilk uygulamanızı yazmaya hazır
mızınız?

Favori Kod Editörünüz. .NET Core uygulaması yazmak


için Atom, Sublime, Notepad veya istediğiniz herhangi bir
editör kullanabilirsiniz. Eğer bir favoriniz yoksa Visual
Studio Code'u denemenizi tavsiye ederim. Bedava
platform bağımsız ve C#, Javascript, HTML ve daha
birçok dil desteği bulunmakta. "Visual Studio Code indir"
diye arattırıp yönergelere uyarak yeni versiyonunu
indirebilirsiniz.

Eğer Windows kullanıyorsanız Visual Studio


kullanabilirsiniz. Bunun için Visual Studio 2017 15.3
versiyonu ve sonrasını kullanmanız gerekmektedir.
(Community Versiyonu sizin için yeterli olacaktır). Visual
Studio harika kod tamamlama ve C#'a özel özellikleri ile
güzel bir deneyim sunacaktır. Fakat Visual Studio
Code'da buna yakın bir deneyim sunar.

.NET Core SDK. Kullandığınız platform veya editöre bağlı


olmaksızın .NET Core SDK kurmanız gerekmektedir. Bu
size komut satırı araçları ve temel kütüphaneleri

11
İlk Uygulamanız

sağlayacaktır. SDK Windows, Mac veya Linux için


kurulabilir.

Editör'e karar verdikten sonra ".NET Core SDK" diye


aratıp yönergelere göre SDK kurmanız gerekmektedir.

12
SDK'yi indirin

SDK'yi indirin
".net core indir" diye arattırıp Microsoft'un web sitesinde
bulunan yönergeleri inceleyerek platformunuza .NET
Coru'u indirin. Bilgisayarınıza yükledikten sonra dotnet
komut satırı aracı ile (CLI) her şeyin doğru çalıştığına
emin olmak için

dotnet --version

2.0.0

--info bayrağı ile çalıştırdığınızda aşağıdaki gibi

platformunuza ait bilgi verecektir:

dotnet --info

.NET Command Line Tools (2.0.0)

Product Information:
Version: 2.0.0
Commit SHA-1 hash: cdcd1928c9

Runtime Environment:
OS Name: Mac OS X
OS Version: 10.12

(more details...)

13
SDK'yi indirin

Eğer yukarıdaki gibi bir bilgi gördüyseniz artık hazırsınız.

14
C# ile Merhaba Dünya

C# ile Merhaba Dünya


ASP.NET Core a başlamadan önce basit bir C#
uygulaması yapıp çalıştıralım

Bunu komut satırı ile yapabilirsiniz. Önce Terminal( veya


powershell ) açıp dosyayı kaydetmek istediğiniz yere
gidin. Örneğin Documents:

cd Documents

dotnet komutu ile yeni proje oluşturun:

dotnet new console -o CsharpHelloWorld


cd CsharpHelloWorld

Bu basit bir C# projesi oluşturacaktır. Oluştururken ekrana


bazı şeyler yazabilir. Proje iki önemli dosyadan
oluşmaktadır. Bunlar .csproj proje dosyası uzantısı ve
C# kodu .cs uzantısı ile oluşturduğunuz klasörde
bulunmaktadır. Eğer editörünüz ile dosyayı açarsanız

CsharpHelloWorld.csproj

15
C# ile Merhaba Dünya

<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.0</TargetFramework>
</PropertyGroup>

</Project>

Yukarıdaki gibi bir içerik göreceksiniz. Proje dosyası XML


tabanlıdır ve proje hakkında bazı üstdata(metadata)
göreceksiniz. Daha sonra başka paketlere referans
ederseniz bunlar bu dosya içerisinde görünecektir. Yani
bu proje npm de bulunan project.json dosyası gibidir.
Çoğunlukla bu dosyayı elle değiştirmeniz
gerekmemektedir.

Program.cs

16
C# ile Merhaba Dünya

using System;

namespace CsharpHelloWorld
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
}
}
}

static void Main C# programının giriş noktasıdır. Eğer

bu metod bir sınıf içerisinde yazılmışsa buna Program


denir. using cümlesi sistem içerisindeki sınıfları getirmek
için kullanılır. Bunlar .NET sınıfları olabilecei gibi başka
sınıflar da olabilir.

Projenin içerisinde terminalinizden(Komut satırı) dotnet


run yazarak programı çalıştırabilirsiniz. Programınız

çalıştığında aşağıdaki çıktıyı göreceksiniz.

dotnet run

Hello World!

Bu bölümde .NET projesinin nasıl yaratılıp çalıştırıldığını


gördünüz. Artık ASP.NET Core uygulamasına geçebiliriz.

17
C# ile Merhaba Dünya

18
ASP.NET Core Projesi Oluşturma

ASP.NET Projesi Oluşturma


Hala Merhaba Dünya( Hello World ) projesi
içerisindeyseniz. Bir üst klasöre geçiniz

cd ..

Daha sonra yeni bir proje oluşturmak için dotnet new , ve


birkaç parametre kullanacağız

dotnet new mvc --auth Individual -o AspNetCoreTodo


cd AspNetCoreTodo

Bu işlem mvc temalı yeni bir proje oluşturacak ve buna


ek olarak kimlik doğrulama için güvenlik yapısını
projemize ekleyecektir. Bunu daha sonra Güvenlik
bölümünde daha geniş biçimde inceleyeceğiz.

Klasöre girdiğinize içinde birkaç dosya ve klasöre


göreceksiniz. Şu anda yapmanız gereken ilk şey projeyi
çalıştırmak ve aşağıdaki çıktıyı almak.

dotnet run

Now listening on: http://localhost:5000


Application started. Press Ctrl+C to shut down.

19
ASP.NET Core Projesi Oluşturma

Program bitmedi dikkat ederseniz bunu yerine 5000


portundan çalışan bir web sunucu başlattı ve gelecek
talepleri dinlemeye başladı.

Tarayıcınızı açıp http://localhost:5000 adresine


gittiğinde ASP.NET başlangıç sayfasını göreceksiniz. Artık
projenizin çalıştığına eminsiniz. Terminalde Ctrl-C tuş
kombinasyonu ile sunucuyu durdurabilirsiniz

ASP.NET Core projesinin parçaları


dotnet new mvc teması sizin için birçok dosya ve klasör

oluşturur. Bunlar içerinde en önemlileri aşağıdaki gibidir:

Program.cs ve Startup.cs Dosyaları web server ve


ASP.NET Core bağlantılarının oluşturulmasını sağlar.
Startup sınıfı ek olarak özelkatman(middleware)

eklemenizi sağlar ve bu şekilde statik içerik sunabilir


veya hata sayfalarını kontrol edebilirsiniz. Ayrıca
bağımlı enjeksion taşıyıcıya kendi servisinizi
ekleyebilirsiniz(Bu konuya daha sonra geleceğiz).

Models, Views ve Controllers Klasörleri Model-


View-Controller mimarisine has bileşenleri tutar. Bu
üç klasörü bir sonraki konuda işleyeceğiz.

20
ASP.NET Core Projesi Oluşturma

wwwroot Klasörü statik dosyaları tutar. Bunlar CSS,


JavaScript, ve imaj dosyalarıdır. Başlangıçta CSS ve
Javascript paketleri bower ile yönetilir. Fakat bunun
yerine başka paket yönetilicileri (nmp veya yarn) da
kullanmak mümkündür. wwwroot klasöründeki
dosyalar statik olarak sunulacaktır. Ayrıca bu içerikler
otomatik olarak minimize edilebilir.

The appsettings.json dosyası ASP.NET Core'un


başlangıçta kullanacağı dosyadır. Burada database
bağlantıları ve proje içerisinde doğrudan yazmak
istemediğiniz ( hard coded ) şeyleri burada
tanımlayabilirsiniz.

Visual Studio Code için ipuçları


Eğer Visual Studio Code veya Visual Studio
kullanıyorsanız, bir kaç ipucu vermek isterim.

F5 ile proje çalıştırma ve hata yakalama


yakalama:

Projeniz açıkken F5'e basıp projenizi çalıştırabilirsiniz. Bu


komut satırından yazdığınız dotnet run komutu ile
aynıdır. Bunun yanında hata yakalamak için ara verme
noktası ( breakpoint) koyarak daha etkin bir şekilde
çalışabilirsiniz. Bunun için göreselde olduğu gibi sol
tarafta bulunan boşluğa tıklamanız gerekmektedir.

21
ASP.NET Core Projesi Oluşturma

Ampül butonu ile problem çözümü: Eğer


projenizde derleme hatası alırsanız ( kırmızı alt
çizgiler) bu kodun üzerine tıklayıp sol tarafına bakın.
Size hataya dair bir öneri verecek veya düzeltmenize
yardımcı olacaktır. Örneğin size eksik using
cümlesini gösterecek ve bunu otomatik olarak
getirtmek için yardımcı olacaktır.Ampül Örneği:

Kolayca derle: Command-Shift-B veya Control-


Shift-B ile dotnet build komutunu çalıştırabilirsiniz.

Böylece komut satırından birşey yazmanıza gerek


kalmaz.

Git hakkında not

22
ASP.NET Core Projesi Oluşturma

Eğer kodunuzu Git veya Github üzerinden yönetiyorsanız


şimdi git init komutunu çalıştırarak projeye git ile
başlamanızın zamanı. .gitignore dosyasını ekleyerek
bin ve obj gibi Git'e eklenmesine gerek olmayan

dosyaları sistemden çıkarabilirsiniz.Github üzerindeki


Visual Studio gitignore iskeleti olduka iyi çalışmaktadır.
(https://github.com/github/gitignore).

Daha üzerinde durulacak birçok konu var. Artık projemize


geçebiliriz ve uygulamamızı yazabiliriz.

23
MVC temelleri

MVC Temelleri
Bu bölümde ASP.NET Core MVC sistemini inceleyeceğiz.
MVC (Model-View-Controller) web uygulamaları
geliştirmek için kullanacağımız ve neredeyse tüm web
iskeletlerinde kullanılan ( Ruby on Rails ve Express ) bir
kalıptır. Ayrıca Angular gibi ön yüz iskeletlerinde de
kullanılmaktadır. IOS ve Android üzerinde de yine
uygulama yazmak için MVC benzeri bir yapı
kullanılmaktadır.

İsminde belirtildiği gibi MVC üç bileşenden oluşmaktadır


bunlar : modeller, view(görüntüler) ve kontrolörler.
Kontrolörler web browser üzerinden gelen talepleri
karşılayarak hangi kod parçacığının çalışacağına karar
verir. Views ( görüntüler) genelde sadece tema görevini
görür bunlar genelde html şablon dilleridir. Örneğin
Handlebars, Pug veya Razor. Yaptığı işlem sadece
kontrolörden gönderilen veriyi almak ve ekranda
göstermektir. Modeller verilerin tutulmasını ve
Görüntülere eklenerek kullanıcıya gösterilmesini sağlar.

MVC için genel kalıp şu şekildedir:

Kontrolör talebi alır ve veritabanında bulunan bilgilere


bakar.

24
MVC temelleri

Kontrolör bu bilgilerden model üreterek


Görüntü(view)'e ekler.
Görüntü kullanıcının tarayıcısında görünür.
Kullanıcı butona tıklar ve yeni bir talebi kontrolöre
gönderir.

Eğer daha önce MVC kalıbı ile çalıştıysanız, kendinizi


evinizdeymiş gibi rahat hissedeceksiniz. Eğer MVC ile
çalışmadıysanız bu bölüm basit bir şekilde başlamınıza
yardımcı olacaktır.

Ne Yapacağız
MVC üzerinde yapılan "Merhaba Dünya" uygulaması
"Yapılacaklar Listesi" uygulamasıdır. Küçük ve basit bir
projedir fakat MVC'nin tüm bölümlerini kullanmanız
gerekmektedir. Bu da büyük uygulamalarda
kullanacağınız çoğu konsepti kapsamaktadır.

Bu kitapta yapılacaklar listesi uygulaması yapacağız. Bu


listeye kullanıcı yeni madde ekleyebilecek veya bittiğinde
onay kutusu ile tamamlandığını belirtebilecek. Bunun için
Arka uçta ASP.NET Core, C# ve MVC kalıbı kullanacağız.
Önyüzde ise HTML, CSS ve Javascript kullanacağız.

25
MVC temelleri

Eğer dotnet new mvc ile hala projenizi oluşturmadıysanız


lütfen önceki bölüme bakarak projeyi oluşturun ve
sayfanın ulaşılabilir olduğunu tarayıcınızdan konrol edin.

26
Kontrolör Oluşturma

Kontrolör Oluşturma
Hali hazırda Controller klasöründe HomeController gibi
ana sayfayı gösteren bir kontrolör dahil birkaç kontrolör
bulunmaktadır. Sunucumuz çalıştığında tarayıcıdan
http://localhost:5000 adresine gittiğimizde bize o

sayfayı gösteren kontrolör HomeController dür.

Yapılacaklar listesi için bu klasör içerisine TodoController


adında bir kontrolör oluşturalım. Sonuna .cs eklentisini
koymayı unutmayın. Oluşturduktan sonra içerisine
aşağıdaki kodu yapıştırın

Controllers/TodoController.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;

namespace AspNetCoreTodo.Controllers
{
public class TodoController : Controller
{
// Aksiyonlar buraya gelecek
}
}

27
Kontrolör Oluşturma

Kontrolör tarafından idare edilen rotalara aksiyon denir.


Bunlar kontrolör içindeki metodlarla ifade edilir. Örneğin
HomeController üç tane aksiyon bulunmaktadır ( Index ,

About , ve Contact ). Bu aksiyonlar ASP.NET Core

tarafında şu şekilde rotalara yerleştirilmişlerdir.

localhost:5000/Home -> Index()


localhost:5000/Home/About -> About()
localhost:5000/Home/Contact -> Contact()

ASP.NET Core rotaları yaratmak için birkaç kalıp


kullanmaktadır. Örneğin kontrolörümüzün ismi
FooController olur ise rotamız doğrudan /Foo oluyor.

/Foo/Index ile /Foo aynı anlama geliyor ayrıca Index

aksiyonu yazmanıza gerek kalmıyor yani. Bu proje


boyunca biz varsayılan haliyle kullanacağız.

TodoController içerisine // Aksiyon buraya gelecek

yazısı yerine Index aksiyonu oluşturalım

28
Kontrolör Oluşturma

public class TodoController : Controller


{
public IActionResult Index()
{
// Database'den değerleri getir

// Gelen değerleri yeni modele koy

// Modeli Görünyüye ekle ve sayfayı göster.


}
}

Aksiyon metodları Görüntü(view), JSON verisi, veya


HTTP status kodu 200 OK veya 404 Not Found gibi
esnek değerler döndürebilir.

Pratikte kontrolörün olabildiğince basit olmasına özen


gösterilir. Böyle bir kontrolörü hedef olarak aldığımızda
kontrolör sadece veritabanından değerler alıp bunları
modele koyma işini yapar. Ardından önyüze gönderir.

Kontrolörün devamını yazmadan önce Model ve Görüntü


(view) oluşturmanız gerekmektedir.

29
Model Oluşturma

Model Oluşturma
İki türlü model sınıfı bulunmaktadır: Yapılacaklar
maddesini veritabanında temsil eden sınıf. Buna entity de
denilmektedir. İkincisi ise bu modeli önyüze göndermeye
yaran modeldir. Her ikiside model olarak
tanımlanabileceğinden dolayı biz ikincisine Görüntü
Modeli diyeceğiz.

Öncelikle Models klasörünün içerisine TodoItem adında


bir sınıf yaratalım.

Models/TodoItem.cs

using System;

namespace AspNetCoreTodo.Models
{
public class TodoItem
{
public Guid Id { get; set; }

public bool IsDone { get; set; }

public string Title { get; set; }

public DateTimeOffset? DueAt { get; set; }


}
}

30
Model Oluşturma

Buradaki sınıf her bir yapılacak için gerekli olan alanları


tanımlar. Bunlar ID, title, isDone, DueAt gibi değerlerdir.
Dikkat ettiyseniz DateTimeOffset? biraz farklıdır. Buradaki
soru işareti null olabileceğini belirtir. Böylece bu alanın
kontrolü yapılmadan bir işlem yapılırsa Örneğin
DueAt.Day dediğimizde eğer değer null ise hata atması
gerekmektedir. Fakat soru işareti koyarak önce null
kontrolü yapmış oluyoruz.

globally unique identifier çok uzun bir yazı ve rasgele


seçildiğinden dolayı aynı değerin üstüste gelmesi
neredeyse imkansızdır. Bunun için ID olarak GUID
kullanacağız. Guid örneği : 43ec09f2-7f70-4f4b-9559-
65011d5781bb .

Tabi bu değer yerinde veri tabanınızdan sayısal bir


değer döndürmesini isteyebilirsiniz. Bunun için
tablonudaki o alanı otomatik artım olarak
işaretlemelisiniz. Böylece her yeni veri girişde bu
kolon yeni bir değer alacaktır.

Bu noktada hangi veri tabanı kullandığınız önemli değil.


Örneğin bu SQL Server, MySql, MongoDb, Redis veya
başka aklınıza gelen herhangi bir veri tabanı olabilir.
Oluşturduğunuz model veri tabanındaki bir satırın C# ta
nasıl görüneceğini tanımlar. Bundan dolayı sizin veri
tabanı hakkında endişelenmenize gerek yok.

31
Model Oluşturma

Genellikle veri tabanında oluşturduğunuz tablo modelinize


çok benzemektedir. Fakat birebir aynı olmayabilir. Örneğin
şu anda yaptığımız örnekte TodoItem tek bir satırı ifade
etmektedir. Fakat biz önyüzde bunların listesini görmek
istiyoruz. Bundan dolayı bizim Görüntü Modeli
oluşturmalıyız.

İstediğimiz TodoItem Listesidir.


Models/TodoViewModel.cs

using System.Collections.Generic;

namespace AspNetCoreTodo.Models
{
public class TodoViewModel
{
public IEnumerable<TodoItem> Items { get; set
; }
}
}

IEnumerable<> C# ta Itemlar demenin yeni şeklidir. Bu

özellik boş olabilir, bir tane veya daha fazla TodoItem


içerebilir.( Teknik olarak array'a benzesede tam olarak
array değildirler. IEnumerable bizim ihtiyacımız olduğunda
veri tabanına istek gönderir. bkz: Lazy Loading. Ayrıca

32
Model Oluşturma

Linq sorgusunun sonunda çalıştırıldığını varsayarsak


öncesinde veritabanına sorgu göndermezsek
performanslı bir işlem yapmış oluruz)

IEnumerable<> arayüzü System.Collections.Generic

isim uzayında ( namespace ) yer almaktadır, using


kullanarak bunu dosyanın başında belirmeniz
gerekmektedir.

Artık yapılacaklar için modelleri yaptığımıza göre


TodoViewModel i alacak bir Görüntü(view) oluçturmanın

zamanı geldi.

33
Görüntü(View) oluşturma

Görüntü(View) Oluşturma
ASP.NET Core'da görüntü Razor Şablon Dili üzerine
kuruludur. Bu HTML ile C# kodunu birleştirebilmemizi
sağlar. ( Eğer daha öncesinde Javascript'de Jade/Pug
veya Handlebars, Javada Thymeleaf kullandıysanız ana
hatlarıyla ne yaptığımızı anladınız demektir)

Çoğunlukla görüntü kodları sadece HTML'dir. Görüntü


Modelinden gelen değerleri içerisinden alıp HTML
içerisine yerleştirmek için C# kullanırız. C# komutları @
sembolü ile başlar.

Index aksiyonu tarafından gösterilecek sayfada liste

halinde görüntü modelinde taşınan TodoItem 'ı


göstermemiz gerekmektedir. İsmine münhasır Görüntü
dosyalarını View klasörünün içerisinde oluşturmamız
gerekmektedir. Bu klasörün içerisine kontrolörün ismine
göre bir başka klasör açıp aksiyon ismiyle dosya
oluşturmamız gerekmektedir.

Örneğin Index aksiyonunu kullanacağız ve


kontrolörümüzün ismi TodoController , yapmamız
gereken Views içerisine Todo adında bir klasör

34
Görüntü(View) oluşturma

oluşturmak ve bunun içerisine Index.cshtml dosyası


oluşturmak. Razor şema dili .cshtml uzantısını
kullanmaktadır.

Views/Todo/Index.cshtml

@model TodoViewModel;

@{
ViewData["Title"] = "Yapılacaklar Listesini Yöne
t";
}

<div class="panel panel-default todo-panel">


<div class="panel-heading">@ViewData["Title"]</div>

<table class="table table-hover">


<thead>
<tr>
<td>&#x2714;</td>
<td>Item</td>
<td>Due</td>
</tr>
</thead>

@foreach (var item in Model.Items)


{
<tr>
<td>
<input type="checkbox" name="@item.I
d" value="true" class="done-checkbox">
</td>
<td>@item.Title</td>

35
Görüntü(View) oluşturma

<td>@item.DueAt</td>
</tr>
}
</table>

<div class="panel-footer add-item-form">


<form>
<div id="add-item-error" class="text-danger">
</div>
<label for="add-item-title">Yeni yapılacak e
kle:</label>
<input id="add-item-title">
<button type="button" id="add-item-button">E
kle</button>
</form>
</div>
</div>

Sayfanın başındaki @model tanımı Razor'a hangi modelin


beklendiğini bildiriyor. Bu modele daha sonra Model
özelliği üzerinden ulaşılır.

Model.Items da veri olduğunu varsayarsak bu verilere

foreach cümlesiyle döngü içerisine alabilir ve bu

döngünün içerisinde <tr> ile satırı oluşturup değerlerimizi


yazdırabiliriz. Ayrıca gördüğünüz gibi her satır için onay
kutusunu da başta yerleştirdik. Daha sonra bunu
işaretlediğimizde görevin tamamlandığını bildiren kodu
yazacağız.

36
Görüntü(View) oluşturma

Dosya düzeni
Dosyanın içerisine baktığınızda <body> veya <footer>
nerede diyebilirsiniz. Görüntü dosyaları daha kolay
çalışabilmemiz için Views/Shared/_Layout.cshtml
içerisinde tanımlanır. Bizim oluşturduğumuz yeni sayfa
dinamik olarak değişen içerik bölümüne gelir.

ASP.NET Core'un varsayılan teması Bootstrap ve


JQuery'i içerir. Böylece çok hızlı bir şekilde web
uygulaması geliştirebilirsiniz. Tabiki kendi CSS ve
Javascript kütüphanelerinizi de kullanabilirsiniz.

Stil üzerinde değişiklikler


Şimdilik CSS üzerinde değişiklik yapmak için site.css
dosyasının en altına aşağıdaki stilleri yapıştırın.

wwwroot/css/site.css

div.todo-panel {
margin-top: 15px;
}

table tr.done {
text-decoration: line-through;
color: #888;
}

37
Görüntü(View) oluşturma

Kodda da gördüğünüz gibi CSS kuralları ile sayfanıza


istediğiniz stili verebilirsiniz.

ASP.NET Core elbette bundan çok daha fazlasını


yapabilir. Örneğin kısmi görüntüler(partial views) veya
sunucu tarafından yorumlanmış görüntü bileşenleri vs.
Fakat basit bir web sitesi için bunlara gerek yoktur. Bu
konuyu daha derinine incelemek isterseniz
https://docs.asp.net adresini kullanabilirsiniz.

38
Servis sınıfı ekleme

Servis Sınıfı Ekleme


Modeli, Görüntü dosyasını ve kontrolörü oluşturdunuz.
Kontrolör içerisinde oluşturduğunuz modeli kullanabilmek
için yapılacaklar listesini veri tabanından alacak kodu
yazmanız gerekmekte.

Aslında veri tabanına bağlanıp model oluşturma işleminin


tamamını kontrolör içerisinde yapabiliriz. Fakat bu iyi bir
pratik değildir. Bunun yerine tüm veri tabanı kodlarını
servis adını verdiğimiz sınıflarda yaparsak
kontrolörlerimiz olabildiğince sade olur. Bu da test etmeyi
ve daha sonra veri tabanı kodlarında değişiklik yapmayı
kolaylaştırır.

Uygulamayı mantık katmanı, veri tabanı katmanı ve


uygulama katmanı gibi katmanlara ayırma olayına
bazen 3-ties veya n-tier mimari de denir.

.NET ve C# interface konseptine sahiptir. Interface


metodların ve özelliklerin aslında işi yapan sınıftan
ayrılması olarak tanımlanabilir. Interface sınıflarınızı
decoupled(ayrılmış) şekilde tutmanızı sağlar. Bu da test
etmeyi daha kolay hale getirir. Bunu daha sonra Otomatik
Test bölümünde göreceğiz.

39
Servis sınıfı ekleme

Önce Interface'i oluşturarark yapılacaklar listesi için


oluşturulacak servisi tanımlayalım. Bu oluşturacağımız
dosyaları da Services klasörünün içerisinde
oluşturmalıyız.
Services/ITodoItemService.cs

using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using AspNetCoreTodo.Models;

namespace AspNetCoreTodo.Services
{
public interface ITodoItemService
{
Task<IEnumerable<TodoItem>> GetIncompleteIte
msAsync();
}
}

Dikkat ederseniz dosyanın isimuzayı(namespace)


AspNetCoreTodo.Services . İsimuzayı .NET kodlarının

organize olmasına yarar. Burada gördüğünüz isimuzayı


klasör yapısını takip etmiştir.

Bu dosya AspNetCoreTodo.Services isimuzayına sahip ve


TodoItem sınıfını talep ettiğinden dolayı

AspNetCoreTodo.Models isimuzayını bu dosyaya using

40
Servis sınıfı ekleme

cümlesi kullanarak eklememiz gerekmektedir. Aksi halde


aşağıdaki gibi bir hata göreceksiniz.

The type or namespace name 'TodoItem' could not be f


ound (are you missing a using directive or an assemb
ly reference?)

Interface olmasından dolayı aslında çalışan hiç bir ko


yoktur sadece tanımı oluşturduk. Yapacağımız Serviste
GetIncompleteItemsAsync metodunu uygulamamız ve

IEnumerable<TodoItem> yerine

Task<IEnumerable<TodoItem>> döndürmemiz

gerekmektedir.

Eğer kafanız karıştıysa şu şekilde düşünebilirsiniz.


"Yapılacaklar'ı içeren listeyi içeren bir görev"

Task tipi promise ve future benzeri bir tiptir, burada

kullanmamızın nedeni bu metodu asenkron yapmak


istememizden dolayıdır. Diğer bir deyişle veri tabanından
alacağımız listenin ne zaman geri döneceğini
bilmediğimizden bu şekilde kullanmaktayız.

Artık Interface'i tanımladık. Gerçekten işi yapan servis


sınıfını yapmaya başlayabiliriz. Veri tabanı kodunu daha
sonraki Veri tabanı kullanma bölümünde daha derinine
inceleyeceğiz. Şimdilik örnek veriler kullanarak işlemimizi
gerçekleştireceğiz.

41
Servis sınıfı ekleme

Services/FakeTodoItemService.cs

using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using AspNetCoreTodo.Models;

namespace AspNetCoreTodo.Services
{
public class FakeTodoItemService : ITodoItemServ
ice
{
public Task<IEnumerable<TodoItem>> GetIncomp
leteItemsAsync()
{
// Return an array of TodoItems
IEnumerable<TodoItem> items = new[]
{
new TodoItem
{
Title = "Learn ASP.NET Core",
DueAt = DateTimeOffset.Now.AddDa
ys(1)
},
new TodoItem
{
Title = "Build awesome apps",
DueAt = DateTimeOffset.Now.AddDa
ys(2)
}
};

return Task.FromResult(items);
}
}

42
Servis sınıfı ekleme

FakeTododItemService ITodoItemService arayüzünü

sağlamıştır. Şu anda her defasında iki tane yapılacak


döndermektedir. Fakat ilerde Veri Tabanı Kullanma
bölümünde bu verileri veri tabanından çekeceğiz.

43
Bağımlı Enjeksiyon Kullanma

Bağımlı Enjeksiyon Kullanma


Tekrar TodoController 'a dönün ve oluşturduğumuz
ITodoItemService 'i kullanmak için aşağıdaki eklemeleri

yapın.

public class TodoController : Controller


{
private readonly ITodoItemService _todoItemServi
ce;

public TodoController(ITodoItemService todoItemS


ervice)
{
_todoItemService = todoItemService;
}

public IActionResult Index()


{
// Get to-do items from database

// Put items into a model

// Pass the view to a model and render


}
}

44
Bağımlı Enjeksiyon Kullanma

ITodoItemService servis isim uzayında olduğundan

dolayı bunu bu sınıfınıza using cümlesini sayfanın en


üst bölümüne yazarak getirmelisiniz.

using AspNetCoreTodo.Services;

Yazdığımız ilk satır özel(private) bir değişken


tanımlayarak ITodoItemService referenasını tutar. Daha
sonra bu değişken üzerinden Index aksiyonu içerisinde
servisimizi kullanabileceğiz.
public TodoController(ITodoItemService todoItemService)

satırı ise constructor tanımıdır. Yani sınıf ilk defa


başlatıldığında bu metod bir defa çalışır. Bu sınıf ilk defa
çalıştığında servisimizi bu kontrolöre enjekte etmek
istediğimizden yukarıdaki gibi değişkenimizi constructor
içerisinde ayarlıyoruz.

Arayüzler(Inteface) işimizi çok kolaylaştırmaktadır.


Örneğin bu kontrolörümüzde biz ItodoItemService
arayüzünü kullanıyoruz. Özel bir servise bağlı
olmadığımızdan bu servisimizi şu anda
ITodoItemService olarak tanımladık. İleride bunu

değiştirip gerçeğini kullandığımız zaman kontrolör


içierisinde hiç bir değişiklik yapmamıza gerek
kalmaz. Bu olay ayrıca Otomatik Test işini de
kolaylaştırır. Bunu ileriki bölümlerde daha kapsamlı
göreceğiz.

45
Bağımlı Enjeksiyon Kullanma

En nihayetinde ITodoItemService 'ini aksiyonunuz


içerisinde kullanacağız.

public IActionResult Index()


{
var todoItems = await _todoItemService.GetIncomp
leteItemsAsync();

// ...
}

Hatırlarsanız GetIncompleteItemsAsync metodu


Task<IEnumerable<TodoItem>> döndürmüştü. Geriye Task

döndüğünde aklınıza ilk gelmesi gereken şey sonuçların


doğrudan gelmeyebileceğidir. Bunun için await
kelimesini kullanıyoruz. Bu kelime sonuna komutumuzun
cevap gelene kadar beklemesini sağlıyor.

Örneğin bu javascript te promise ile çözülmeye


çalışıldı fakat daha önce ajax ve callback
fonksyionları ile çalışanlar daha iyi hatırlayacaklardır.
Bu fonksiyonları kullanmak cehenneme dönüşebilir.
ASP.NET core güzel bir yazım tarzı ile bizi bu
karmaşıklıktan kurtarmaktadır.

Kodunuzdan veri tabanına çağrı yaptığınızda Task


kalıbını kullanmak oldukça yaygındır. Çünkü veri
tabanından veya ağdan verileri alana kadar bize hiç bir

46
Bağımlı Enjeksiyon Kullanma

değer döndermeyecektir. Daha önce de bahsettiğimiz gibi


Task Javascriptte bulunan promise yapısınıa

benzemektedir.

Javascript'e göre Task Javascript callback


fonksyionlarına göre daha kullanışlıdır. await
kelimesi işimizi olukça kolaylaştırmaktadır. await
kodumuzun asenkron çağrılar işin durdurulmasını
sağlar. Bu sırada servisimiz verileri veri tabanından
alır ve geri geri gönderir. Sonra kodumuz kaldığımız
yerden devam eder. Tam olarak anlayamadıysanız
ileriki bölümlerde daha iyi anlayacaksınız.

Burada bilmeniz gereken bir şey IActionResult yerine


Task<IActionResult> kullanmanız gerektiği ve bunu

asenkron olarak tanımlamak için async kelimesi


kullanmanız gerektiğidir.

public async Task<IActionResult> Index()


{
var todoItems = await _todoItemService.GetIncomp
leteItemsAsync();

// Put items into a model

// Pass the view to a model and render


}

47
Bağımlı Enjeksiyon Kullanma

Nerdeyse sonuna geldik. TodoController 'ı


ITodoItemService 'e bağlı hale getirdiniz. Fakat şu anda

hala ITodoItemService arayüzüne FakeTodoItemService


kullanması gerektiğini söylemedik. Şimdilik bir sınıfımız
var neden böyle birşey yapmaya ihtiyacımız var gibi
düşünebilirsiniz. Fakat ileride daha fazla sınıf
kullanabiliriz. Kural olarak bunların hepsinin tanımlanması
gerekmektedir.

Tanımlamak için ( Spring framework @wired ) Startup


sınıfı içerisindeki ConfigureServices metodunu
kullanacağız. Bu sınıf şimdilik aşağıdaki gibi
görünmektedir..

Startup.cs

public void ConfigureServices(IServiceCollection ser


vices)
{
// Bazı kodlar

services.AddMvc();
}

ASP.NET Core'a ITodoItemService için


FakeTodoItemService kullan demek için bu metod

içerisinde herhangi bir yere aşağıdaki kodu yazmalıyız.

48
Bağımlı Enjeksiyon Kullanma

services.AddScoped<ITodoItemService, FakeTodoItemSer
vice>();

AddScoped sizin servisinizi servis konteyner'ına scoped

olarak ekler. Bu her FakeTodoItemService çağırıldığında


yeniden üretilmesini sağlar. Bu veritabanaı ilişkili
servislerde çokça kullanılan yöntemdir. Ayrıca servisi
singletons olarak da tanımlayabilirsiniz. Bu da bu
servisin sadece bir defa başlangıçta üretilmesini sağlar.

Bu kadar! Artık TodoController 'a talep geldiğinde.


ASP.NET Core kontrolör ITodoItemService 'i talep
ettiğinde FakeTodoItemService 'i sunacak. Çünkü biz bağı
bulunan servisimizi enjekte ettik. İşte bu kalıba
dependency injection denilmektedi.

49
Kontrolörü Tamamlama

Kontrolörü Tamamlama
Artık kontrolörümüzü tamamlayabiliriz. Artık
kontrolörümüzün servis katmanına bağlandığını
varsayabiliriz. Bu servisten aldığımız veriler ile daha önce
oluşturduğumuz TodoViewModel oluşturup bunu önyüze
gönderebiliriz.
Controllers/TodoController.cs

public async Task<IActionResult> Index()


{
var todoItems = await _todoItemService.GetIncomp
leteItemsAsync();

var model = new TodoViewModel()


{
Items = todoItems
};

return View(model);
}

Test

50
Kontrolörü Tamamlama

Eğer Visual Studio Code veya Visual Studio


kullanıyorsanız F5 Tuşu ile projenizi çalıştırabilirsiniz.
Bunun yerine komut satırı veya terminalden dotnet run
komutunu da kullanabilirsiniz elbette. Eğer kodunuzda
hata yoksa sunucunuz varsayılan port olan 5000
portundan çalışmaya başlayacaktır.

Eğer tarayıcınız otomatik olarak açılmadıysa


`http://localhost:5000/todo' adresine giderek,
hazırladığımız sahte(fake) veri tabanı katmanından
verileri çektiğini ve ekranda gösterdiğini göreceksiniz.

Tebrikler! Sırada yaptığımız bu projeye 3. parti paketler


ekleme ve gerçek veri tabanına bağlama var.

51
Ekstra paket Ekleme

Ekstra Paket Ekleme


.NET ekosistemini kullanmanın en büyük avantajlarından
biriside 3.Parti eklentilerdir. Diğer paket sistemleri gibi (
npm, Maven, RubyGems ) .NET paketleri indirebilir ve
kullanabilirsiniz.

NuGet hem paket yönetim aracı hemde kaynağıdır. (


https://www.nuget.org ) Nuget paketlerini internette aratıp
kendi bilgisayarınıza terminal veya komut satırı
aracılığıyla veya Visual Studio yardımıyla indirip
kurabilirsiniz. Nitekim biz de bu projemizde Humanizer
paketini indirip kuracağız

Humanizer paketi kurma


Bir önceki bölümde yapılacaklar uygulaması ekrana
aşağıdaki gibi bir çıktı vermişti :

52
Ekstra paket Ekleme

Ekranda görülen tarih bölümü makineler için okunaklı


olabilir ( ISO 8601 ), fakat insanlar için okuması zordur.
Mesela bunu "X gün kaldı" şekilde yapsak daha okunaklı
olmaz mı? Elbette bunu kendimiz kodlayarak yapabiliriz.
Daha hızlı yolu ise hazırda yazılmışı ile yapmak,

NuGette bulunan Humanizer


paketi(https://www.nuget.org/packages/Humanizer)
isminden de belli olduğu gibi insanların okuyabilirliklerini
artırma amacıyla yapılmıştır. Bunlar tarih, saat, süreç,
sayılar vs. için kullanılır. MIT lisansına sahip harika bir
açık kaynak kodlu projedir.

Bu paketi projenize eklemek için aşağıdaki kodu


terminalde veya komut satırında çalıştırınız.

dotnet add package Humanizer.Core

AspNetCoreTodo.csproj dosyasını inceleyecek olursanız,

Humanizer.Core adında yeni bir PackageReference

göreceksiniz.

Humanizer'ı Görüntü
dosyamızda ( View ) kullanma

53
Ekstra paket Ekleme

Bir paketi kodumuzda kullanabilmek için her zamanki gibi


using cümlesiyle belirtmemiz gerekmektedir.

Humanizer'i biz kontrolör veya servis katmanında değilde


sadece Görüntü katmanında kullanacağımızdan dolayı
doğrudan Görüntü dosyasının içerisinde bunu
belirtebiliriz.
Views/Todo/Index.cshtml

@model TodoViewModel;
@using Humanizer;

// ...

Daha sonra DueAt yazan özelliği Humanize metoduyla


aşağıdaki gibi yazdığımızda

<td>@item.DueAt.Humanize()</td>

Tarihlerin daha okunabilir olduğunu göreceksiniz.

Nuget'te XML parçalayan paketten, makine öğrenmesi


yapabilen veya twitter'a yeni konu giren pakete kadar her
türlü paket bulmak mümkündür.

54
Ekstra paket Ekleme

dotnet new mvc ile oluşturulan proje dosyası tek bir

referans içerir, Microsoft.AspNetCore.All paketi. Bu


paket aslında diğer referansları tutan bir
"metapakettir". Böylece biz tek bir paket
indiriyormuşuz gibi dursa da aslında bize gerekli olan
tüm paketler arkada indirilir.

Bir sonraki konuda, Entity Framework Core adında yeni


bir NuGet paketi göreceğiz. Bu paket bizim veri tabanı ile
iletişimimizi sağlayacaktır.

55
Veri Tabanı Kullanma

Veri Tabanı Kullanma


Database kodu yazma alengirli olabilir. Ne yaptığınızı
bilmiyorsanız doğrudan SQL sorgusunu kodlarınız arasına
yazmak iyi bir firir değildir. object-relational mapper
(ORM) bu olayı yeni bir katman ekleyerek daha kolay hale
getirir. Bunun karşılığında Java'da Hibernate veya
Ruby'de ActiveRecord bilinen ORM'dir.

.NET için de bir çok ORM bulunmaktadır, Microsoft


bunlardan biri olan Entity Framework'ü doğrudan
ASP.NET Core içerisine entegre etmiştir. Entity
Framework diğer veri tabanlarına bağlanmayı
kulaylaştırdığı gibi veri tabanı sorgularını oluşturma veya
tekrar obje haline çevirme işini yapar. (POCOs)

Hatırlarsanız servis arayüzünü oluşturduğumuzda


kontrolörden gerçek servisi ayırabilmiştik. Entity
Framework'ü veri tabanı üzerine konulmuş büyük bir
arayüz olarak görebiliriz. Bundan dolayı çalışan veri
tabanı teknolojisinin hangisi olduğu önemsizdir.

Entity Framework SQL veri tabanlarına bağlanabilir.


Örneğin MySQL veya SQL Server. Ayrıca Mongo gibi
NoSQL veri tabanları ile de çalışabilir. Bu projede SQLite

56
Veri Tabanı Kullanma

kullanacağız. Sonrasında siz isterseniz farklı veri


tabanlarına da bağlayabilirsiniz.

57
Veri Tabanına Bağlanma

Veri tabanına bağlanma


Entity Framework Core kullanarak veri tabanına
bağlanmak için bilmeniz gereken birkaç şey var. dotnet
new kullanarak yeni bir MVC + kişi doğrulama projesi

oluşturduğunuzdan dolayı;

Entity Framework Core paketleri. Bu paketler


varsayılan olarak ASP.NET Core projesinde bulunur.

Veri tabanı (doğal olarak). dotnet new komutu ile


kurulan projenizde ayrıca app.db adında bir dosya
oluşturuldu. Bu küçük bir SQLite veri tabanıdır.
SQLite, başka birşey kurma olmaksızın
kullanabileceğiniz küçük bir veri tabanıdır. Böylece
prototip için veya küçük projeler için ayrıca bir veri
tabanına gerek kalmaz.

Veri tabanı kaynak(context) sınıfı. Veri tabanına


bağlanmak için bir başlangıç noktası sınıfı oluşturulur.
Burada veri tabanı ile nasıl iletişime geçileceği,
okunacağı veya yazılacağı belirlenir. Bu sınıf
Data/ApplicationDbContext.cs dosyası içerisinde

bulunur.

58
Veri Tabanına Bağlanma

Bağlantı Yolu. İsterseniz kendi bilgisayarınızda veya


ağ üzerindeki başka bir bilgisayarda bulunan veri
tabanına bağlanmak için bir yol belirlenemeniz
gerekmektedir. Şu anda varsayılan bir veri tabanı
kullandığımızdan dolayı bunlar otomatik olara
appsettings.json içerisine yazılmış durumda. Dikkat

ederseniz DataSource=app.db olarak gözünüze


çarpacaktır.

Entity Framework Core veri tabanını bağlantı yolu ile


kullanarak veri tabanına bağlanır. Bundan dolayı Entity
Framework Core'a hangi kaynağı(context), hangi bağlantı
yolunu kullanmak istediğinizi söylemelisiniz. Bunu
ConfigureServices metodundan değiştirebilirsiniz. Bu

metod Startup sınıfında bulunur. Bu halihazırda sizin


için oluşturulmuş kod.

services.AddDbContext<ApplicationDbContext>(options
=>
options.UseSqlite(Configuration.GetConnectionStr
ing("DefaultConnection")));

Bu kod ile; ApplicationDbContext service konteynerına


eklenir ve Entity Framework Core'a SQLite veri tabanına
appsettings.json 'da bulunan bağlantı yolunu kullanarak

bağlanır.

59
Veri Tabanına Bağlanma

Gördüğünüz gibi dotnet new sizin için birçok şeyi hazır


hale getiriyor. Veri tabanımız kuruldu ve artık kullanılmaya
hazır. Fakat şu anda hiç bir tablomuz bulunmamakta.
TodoItem modellerini tutmak için kaynağı(context)

güncellemeli ve veri tabanını taşımalıyız(migrate).

60
İçeriği Güncelleme

İçeriği Güncelleme
Şu anda veri tabanı bağlamında çok bir değişiklik yok
Data/ApplicationDbContext.cs

public class ApplicationDbContext : IdentityDbContex


t<ApplicationUser>
{
public ApplicationDbContext(DbContextOptions<App
licationDbContext> options)
: base(options)
{
}

protected override void OnModelCreating(ModelBui


lder builder)
{
base.OnModelCreating(builder);
// Customize the ASP.NET Identity model and
override the defaults if needed.
// For example, you can rename the ASP.NET I
dentity table names and more.
// Add your customizations after calling bas
e.OnModelCreating(builder);
}
}

61
İçeriği Güncelleme

ApplicationDbContext 'ine aşağıdaki gibi DbSet ekleyin.

Add a DbSet property to the ApplicationDbContext , right


below the constructor:

public ApplicationDbContext(DbContextOptions<Applica
tionDbContext> options)
: base(options)
{
}

public DbSet<TodoItem> Items { get; set; }

// ...

DbSet veri tabanında bir tablo veya koleksiyonu ifade

eder. Items adında bir DbSet<TodoItem> oluşturarak


Entity Framework Core'a Items adında bir tabloya
TodoItem modellerini saklamak istediğinizi

söylüyorsunuz.

Tüm yapmanız gereken bu kadar. Fakat küçük bir


problem var. Şu anda veri tabanı ve
ApplicationDbContext bir biriyle senkronize değil. (

Sadece kodu değiştirmek veri tabanını


değiştirmemektedir.)

Veri tabanının yapılan değişikliği uygulayabilmesi için


migration(Göç) oluşturmamız gerekmektedir.

62
İçeriği Güncelleme

63
Göç Oluşturma

Migration(Göç) Oluşturma
Migration(Göç) veri tabanı yapısındaki değişklikleri
tutmaya yarar. Böylece veri tabanı üzerinde geriye
dönüç(roll back) veya aynı yapıya sahip ikinci bir veri
tabanı yapmak mümkündür. Böylece değişen her kolon'u
takip edebilmek mümkündür.

Kaynak(Context) ve veri tabanı şu anda senkronize


olmadığından yeni bir Göç oluşturarak veri tabanını
güncellememiz gerekmektedir. Bunu Items alanı için
yapacağız.

dotnet ef migrations add AddItems

Bu Kaynak ta yaptığınız değişiklikleri inceleyerek


AddItems adında yeni bir Göç oluşturur

Eğer şöyle bir hata alıyorsanız : No executable found


matching command "dotnet-ef" Doğru klasörde

olduğunuza emin olun. Bu komutlar ana klasörden


çalıştırılmalıdır. Ana klasör Program.cs nin
bulunduğu klasördür.

Eğer Data/Migration klasörünü açarsanız, bir kaç dosya


göreceksiniz.

64
Göç Oluşturma

Bizim için ilk oluşturulan göç dosyası


00_CreateIdentitySchema.cs adı ile oluşturulmuş ve veri

tabanı bu şekilde güncellenmiştir. AddItems dosyasının


önüne timestamp eklenmiş ve bu şekilde göç dosyası
oluşturulmuştur.

Tavsiye: Göç listesini dotnet ef migrations list ile


görebilirsiniz.

**Göç dosyasını açtığınızda Up ve Down adında iki


metod göreceksiniz.

Data/Migrations/<date>_AddItems.cs

protected override void Up(MigrationBuilder migratio


nBuilder)
{
// (... some code)

migrationBuilder.CreateTable(
name: "Items",
columns: table => new

65
Göç Oluşturma

{
Id = table.Column<Guid>(type: "BLOB", nu
llable: false),
DueAt = table.Column<DateTimeOffset>(typ
e: "TEXT", nullable: true),
IsDone = table.Column<bool>(type: "INTEG
ER", nullable: false),
Title = table.Column<string>(type: "TEXT"
, nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_Items", x => x.Id);
});

// (some code...)
}

protected override void Down(MigrationBuilder migrat


ionBuilder)
{
// (... some code)

migrationBuilder.DropTable(
name: "Items");

// (some code...)
}

Up metodu Göçü çalıştırdığımızda veri tabanına etki

edecek metoddur. Kaynağa DbSet<TodoItem>


eklediğinizden dolayı Entity Framework Core Items

66
Göç Oluşturma

adında bir tablo oluşturacak ve TodoItem modelinde


bulunan alanlara uyacak kolonlar oluşturacaktır.

Down metodu ise tersini yapar. Göç geri alındığında

Items tablosunu siler.

SQLite Sınırlılığına Geçici Çözüm


Projede SQLite kullanmanın bazı sınırlılıkları var. Bu
sınırlılıklar düzelene kadar şu şekilde geçici bir çözüm
yolu kullanabilirsiniz.

Up metodundaki migrationBuilder.AddForeignKey

satırlarını yorum olarak değiştirin. ( //


kullanabilirsiniz.)
Down metodundaki

migrationBuilder.DropForeignKey satırlarını yorum

olarak değiştirin.

Eğer MySQL veya SQL Server gibi tam teşekküllü bir SQL
veri tabanı kullanıyor olsaydınız böyle bir değişikliğe
ihtiyacınız olmayacaktı. Göç dosyasını çalıştırdığınızda
hiç bir sorun almayacaktınız.

Göçü uygulayın
Göçü oluşturduktan sonraki son basamak uygulanmasıdır
:

67
Göç Oluşturma

dotnet ef database update

Yukarıdaki komut Entity Framework Core'un Items


adında bir tablo oluşturmasına neden olacaktır.

Eğer bu güncellemeyi geri almak istiyorsanız. Daha


önceki bir göç ismini vererek bu işlemi
gerçekleştirebilirsiniz. dotnet ef database update
CreateIdentitySchema Bu komut

CreateIdentitySchema dan sonra oluşturulmuş tüm

*göçlerin Down metodunu çalıştıracaktır.

Eğer veri tabanını tamamen silmek istiyorsanız.


dotnet ef database drop kullanabilirsiniz. Ardından

dotnet ef database update yazdığınızda son Göç e

kadar tüm göçler çalıştırılır.

Şu anda veri tabanı ayağını tamamlamış durumdayız.


Sonraki adımımız bu kaynağı servis içinde kullanmak.

68
Yeni Servis Sınıfı Oluşturma

Yeni Servis Sınıfı Oluşturma


MVC Temelleri bölümünde FakeTodoItemService adında
bir sınıf oluşturup doğrudan yapılacaklar listesi eklemiştik.
Artık veri tabanı kaynağına sahip olduğumuzadan dolayı
Entity Framework Core ile gerçek verileri kullanabiliriz.

FakeTodoItemsService.cs 'yi silin ve TodoItemService.cs

adında yeni bir dosya oluşturun.

Services/TodoItemService.cs

69
Yeni Servis Sınıfı Oluşturma

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using AspNetCoreTodo.Data;
using AspNetCoreTodo.Models;
using Microsoft.EntityFrameworkCore;

namespace AspNetCoreTodo.Services
{
public class TodoItemService : ITodoItemService
{
private readonly ApplicationDbContext _conte
xt;

public TodoItemService(ApplicationDbContext
context)
{
_context = context;
}

public async Task<IEnumerable<TodoItem>> Get


IncompleteItemsAsync()
{
var items = await _context.Items
.Where(x => x.IsDone == false)
.ToArrayAsync();

return items;
}
}
}

70
Yeni Servis Sınıfı Oluşturma

Dikkat ederseniz MVC Temelleri Bölümünde


oluşturduğumuz bağımlı enjeksiyon burada da var.
(Constructor'a içerik ekleme) Fakat bu defa
ApplicationDbContext servise enjekte edilmiştir. Daha

öncesinde ApplicationDbContext servis konteynerına


eklendiğinden dolayı burada servise enjekte edilebilir.
Aksi halde ConfigureServices içerisinde bunu
belirtmemiz gerekirdi.

GetIncompleteItemAsync metodunu daha yakından

inceleyecek olursak. Öncelikle DbSet içerisindeki tüm


verilere ulaşabilmek için Items özelliğini(property)
kullanmaktadır.

var items = await _context.Items

Ardından, Where metodu ile bunlardan sadece


tamamlanmamış olanları seçmektedir.

.Where(x => x.IsDone == false)

Where metodu C# LINQ'e ait bir metoddur. Fonksiyonel

programlamadan bazı esintiler almıştır. LINQ bizim daha


kolay veri tabanı sorguları yazmamıza yardımcı olur.
Aslında yukarıda oluşturduğumuz sorgu şu şekildedir:
SELECT * FROM Items where IsDone=0 veya bunun eşiti

NoSQL sorgusudur.

71
Yeni Servis Sınıfı Oluşturma

Sonunda ToArrayAsync metodu bu verileri asenkron dizi


haline getirmeye yarar. Yani bu metodun await
kelimesiyle çağırılması gerekmektedir.

Metodu daha kusa yazmak için items değişkenini


silebilirsiniz. Böylece gelen değeri doğrudan değişken
olmadan dönderebilirsiniz.

public async Task<IEnumerable<TodoItem>> GetIncomple


teItemsAsync()
{
return await _context.Items
.Where(x => x.IsDone == false)
.ToArrayAsync();
}

Servis Konteynerını Güncelleme


FakeTodoItemService 'i sildiğimizden dolayı,

ConfigureServices 'i güncellememiz gerekmektedir.

services.AddScoped<ITodoItemService, TodoItemService
>();

TodoController ITodoItemService 'e bağımlıdır. Bu

değişiklikten hiç haberi olmayacaktır. Fakat arka planda


sahte servisten gerçek servise geçmiş bulunmaktayız.

72
Yeni Servis Sınıfı Oluşturma

Test
Uygulamayı tekrar çalıştırın ve
http://localhost:5000/todo 'a gidin. Sahte verilerin

gittiğini göreceksiniz. Artık uygulamanız veri tabanından


gerçek verileri çekmektedir.

Bir sonraki bölümde uygulamaya yeni yapılacka ekleme


gibi özellikler ekleyeceksiniz.

73
Yeni Özellikler Ekleme

Yeni Özellikler Eklem


Artık Entity Framework Core ile veri tabanını
bağladığımıza göre yeni özellikler ekleyebiliriz. İlk olarak
yapılacakları tamamlandı olarak işaretleyebileceğimiz
onay kutusu ile başlayacağız.

74
Yeni Yapılacak Maddesi Ekleme

Yeni Yapılacak Maddesi


Ekleme
Kullanıcılar yeni yapılacak maddesini aşağıdaki gibi
yapılacaklar listesinin altındaki form'dan ekleyecekler.

Bu özelliği eklemek için şu adımları tamamlamak


gerekmektedir:

Görüntü(View) dosyasını eğiştirerek form


elementlerini ihtiyacımıza göre düzenlemek
Javascript kodu ile butona tıklandığında bunu arka-
uç'a göndermek.
Kontrolörde yeni aksiyon oluşturarak gelecek
taleplere cevap vermek.
Servis katmanına veri tabanını güncelleyecek
metodları yazmak.

Görüntüye Form Eklemek.

75
Yeni Yapılacak Maddesi Ekleme

Views/Todo/Index.cshtml in en altına aşağıdaki formu

ekleyiniz.

<div class="add-item-form">
<form>
<div id="add-item-error" class="text-danger">
</div>
<label for="add-item-title">Add a new item:</
label>
<input id="add-item-title">
<button type="button" id="add-item-button">A
dd</button>
</form>
</div>

Arka-uç'a POST talebiyle veri gönderebilmek için jQuery


kullanacağız. Butona tıklandığında bu işlem
gerçekleşecek.

Add JavaScript code


wwwroot/site.js dosyasını açın ve aşağıdaki kodu

ekleyin.

76
Yeni Yapılacak Maddesi Ekleme

$(document).ready(function() {

// Add butonuna tıklanıldığında addItem fonksiyo


nu çalışacak.
$('#add-item-button').on('click', addItem);

});

addItem fonksiyonunu dosyanın altına aşağıdaki gibi

ekleyiniz.

function addItem() {
$('#add-item-error').hide();
var newTitle = $('#add-item-title').val();

$.post('/Todo/AddItem', { title: newTitle }, fun


ction() {
window.location = '/Todo';
})
.fail(function(data) {
if (data && data.responseJSON) {
var firstError = data.responseJSON[Object
.keys(data.responseJSON)[0]];
$('#add-item-error').text(firstError);
$('#add-item-error').show();
}
});
}

77
Yeni Yapılacak Maddesi Ekleme

Bu fonksiyon http://localhost:5000/Todo/AddItem 'a post


talebi ile kullanıcının metin kutusuna girdiği değeri paslar.
$.post fonksiyonundaki üçüncü parametre eğer işlem

başarı ile gerçekleşirse çalışacak fonksiyondur. Eğer


kontrolör başarı ile çalışır ve cevap dönerse bu cevapla
birlikte 200 OK döner. Başarılı olduğunda sayfa
windwos.location ile yönlendirilerek yeniden yüklenir.

Eğer sunucu 400 Bad Request cevabı verseydi fail


fonksiyonu çalışacaktı. Bu da hatayı ekrana yazaktı.

Aksiyon Ekleme
Şu anda yukarıdaki Javascript kodu çalışmayacak çünkü
/Todo/AddItem yolunda henüz bu talebi karşılacak bir

aksiyonumuz bulunmamaktadır. Eğer şu anda denerseniz,


ASP.NET Core 404 Not Found hatası dönecektir.

TodoController içerisine AddItem adında yeni bir

aksiyon oluşturun.

78
Yeni Yapılacak Maddesi Ekleme

public async Task<IActionResult> AddItem(NewTodoItem


newItem)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}

var successful = await _todoItemService.AddItemA


sync(newItem);
if (!successful)
{
return BadRequest(new { error = "Could not a
dd item" });
}

return Ok();
}

Sizinde dikkat edeceğiniz üzere bu metod NewTodoItem


isminde bir parametre beklemektedir. Şu anda bu model
tanımlı değil. Öyleyse bunu tanımlayalım.

Models/NewTodoItem.cs

79
Yeni Yapılacak Maddesi Ekleme

using System;
using System.ComponentModel.DataAnnotations;

namespace AspNetCoreTodo.Models
{
public class NewTodoItem
{
[Required]
public string Title { get; set; }
}
}

Bu modelde sadece Title isminde yeni bir özelliklik


tanımladık. Bu özelliğin ismi Jquery ile gönderdiğimiz
talepte bulunan isimle aynıdır.

$.post('/Todo/AddItem', { title: newTitle } // ...

// Bir özellikli JSON Objesi:


// {
// title: tipi
// }

ASP.NET Core model ile bu gelen parametreyi eşlemek


için model bağlama işlemini kullanır. Eğer isimleri aynı
ise veri modelin içerisine alınır.

Model Bağlamanın yanı sıra ASP.NET Core ayrıca


model doğrulama işlemini de yapar. Modelde yazdığımız
[Required] özelliği Title 'ın boş olamayacağı bilgisini

80
Yeni Yapılacak Maddesi Ekleme

verir. Bu doğrulama doğrudan hata vermyecektir fakat


durumu kaydedilecektir. Ardından kontrolör içerisinden bu
kontrol edilebilir.

Aslında TodoItem 'ı tekrar kullanabilirdik. En


nihayetinde bu modelde de Title özelliği mevcuttu.
Fakat bu modelde ön yüzden hiç bir zaman
gönderilmeyecek(ID ve done) gibi özellikleri
içermektedir. Dolayısıyla sadece gelecek değerleri
tanımlayan bir model yaratmak daha yararlı olacaktır.

AddItem aksiyonuna bakacak olursanız ilk yaptığımız iş

model doğrulamadır. Eğer bu parametre doğrulandıysa


işleme devam edecek aksi halde BadRequest(ModelState)
döndürecektir.

if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}

Burada ModelState tam olarak neyin yanlış olduğunu


açıklamasıyla birlikte dönmeye yarar. BadRequest ise
400 Bad Request dönürür. Böylece hatanın tam olarak

neyden kaynaklandığını anlayabilirsiniz.

81
Yeni Yapılacak Maddesi Ekleme

var successful = await _todoItemService.AddItemAsync


(newItem);
if (!successful)
{
return BadRequest(new { error = "Yeni madde ekle
nemedi" });
}

AddItemAsync metodu sadece true veya false

döndürür. Herhangi bir nedenden dolayı hata aldıysa 400


Bad Request ve bununla error özelliğini döner.

Eğer hiç bir hata almadıysa sonuçta Ok() yani 200 OK


döner.

Servis Metodu Ekleme


Eğer C# anlayan bir kod editörü kullanıyorsanız
AddItemAsync altında çizgi göreceksiniz. Daha bu servis

metodunu yazmadığımızdan bu gayet normal.

Önce arayüze( ITodoItemService ) bu tanımı ekleyin.

82
Yeni Yapılacak Maddesi Ekleme

public interface ITodoItemService


{
Task<IEnumerable<TodoItem>> GetIncompleteItemsAs
ync();

Task<bool> AddItem(NewTodoItem newItem);


}

Ardından gerçek uygulamasını TodoItemService


içerisinde yapın:

public async Task<bool> AddItem(NewTodoItem newItem)


{
var entity = new TodoItem
{
Id = Guid.NewGuid(),
IsDone = false,
Title = newItem.Title,
DueAt = DateTimeOffset.Now.AddDays(3)
};

_context.Items.Add(entity);

var saveResult = await _context.SaveChangesAsync


();
return saveResult == 1;
}

Bu metod yeni bir TodoItem ( veri tabanı modeli )


oluşturup NewTodoItem modelinden gelen Title alanını
kopyalamaktadır. Sonrasında SaveChangeAsync ile bunu

83
Yeni Yapılacak Maddesi Ekleme

kalıcı olarak veri tabanına yazar.

Yukarıdaki kod istediğimizi yapan bir yoldur. Bu


şekilde farklı yollar da bulunmaktadır. Örneğin daha
kompleks bir yapıyı düşünün, modelinizde birçok
alan var ve bunu ekrana gösterip talep ediliğinde
kaydetmek istiyorsunuz. Bunun için ASP.NET Core
tag helpers adında modelden görüntü yaratabilecek
özellikler barındırır. Bu konu ile ilgili örneklere
https://docs.asp.net üzerinden erişebilirsiniz.

Test Edin
Uygulamayı çalıştırın ve birkaç yeni yapılacak ekleyin.
Girdiğiniz değerler veri tabanına kaydedildiğinden sayfa
tekrardan yüklense de, tarayıcınızı kapatıp açsanız da
verilerin aynı kaldığını göreceksiniz.

Buna ek olarak tarih seçici kullanarak HTML ve


Javascript ile kullanıcının DueAt özelliğini girmesini
sağlayabilirsiniz. Eğer tarih boş ise son tarihi 3 gün
sonraya atayabilirsiniz.

84
Maddeleri Onay Kutusu ile Tamamlama

Maddeleri onay kutusu ile


tamamlama
Yapılacaklar listesine ekleme işini yaptınız çokta güzel
oldu. Fakat bir de bu işin tamamlanması var.
Views/Todo/Index.cshtml sayfasına göre her yapılacak

için onay kutusunu da gösterilecek.

<input type="checkbox" name="@item.Id" value="true"


class="done-checkbox">

Her maddenin bir benzersiz ID( guid )'si olduğundan bunu


bullanabiliriz. Bunu name alanında belirterek kontrolöre
bunu paslayarak bu maddeyi güncelleyebiliriz.

Akışın tamamı aşağıdaki gibi olacak

Kullanıcı onay kutusunu işaretler ve Javascript


fonksiyonu tetiklenir.

Javascript kontrolöre talep gönderir.

Kontrolörde bulunan aksiyon servis katmanına


çağrıda bulunarak bu maddenin güncellenmesini
sağlar.

Güncelleme sonunda cevap Javascript fonksiyonuna

85
Maddeleri Onay Kutusu ile Tamamlama

geri gönderilir.
HTML kodu güncellenir.

Javascript Kodu Ekleme


Önce site.js 'yi açık ve $(document).ready bloğunun
içerisini aşağıdaki gibi düzenleyin.

wwwroot/js/site.js

$(document).ready(function() {

// ...

// .done-checkbox sınıfına ait tüm onay kutuları


aşağıdaki kodu çalıştırır.
$('.done-checkbox').on('click', function(e) {
markCompleted(e.target);
});

});

Sonrasında sayfanın sonuna aşağıdaki kodu ekleyin:

86
Maddeleri Onay Kutusu ile Tamamlama

function markCompleted(checkbox) {
checkbox.disabled = true;

$.post('/Todo/MarkDone', { id: checkbox.name },


function() {
var row = checkbox.parentElement.parentEleme
nt;
$(row).addClass('done');
});
}

Bu kod daha önceki yapılacak oluşturma fonksiyonu ile


aynı yapıya sahip. Fakat bu defa HTTP POST ile
http://localhost:5000/Todo/MarkDone adresine talep

gönderip bunun içerisinde id olarak veri tabanından gelen


ve onay kutusunun name kısmında belirttiğimiz id yi
paslıyoruz.

Herhangi bir onay kutusunu işaretleyip tarayıcınızın


Network Araçlarına bakacak olursanız talebi aşağıdaki
gibi görebilirsiniz.

POST http://localhost:5000/Todo/MarkDone
Content-Type: application/x-www-form-urlencoded

id=<some guid>

87
Maddeleri Onay Kutusu ile Tamamlama

$.post başarılı bir şekilde döndüğünde onay kutusunun

bulunduğu satır'a done sınıfı eklenir. Bu sınıfa has stil


değişikliği uygulanmış olur.

Kontrolöre aksiyon ekleme


Sizin de tahmin edeceğiniz gibi, TodoController içerisine
MarkDone aksiyonunu eklemeliyiz.

public async Task<IActionResult> MarkDone(Guid id)


{
if (id == Guid.Empty) return BadRequest();

var successful = await _todoItemService.MarkDone


Async(id);

if (!successful) return BadRequest();

return Ok();
}

Şimdi bu adımların üzerinden geçelim. Öncelikle id


isminde bir Guid argümanı ekledik. Yani bu metodun
Guid beklediğini bildirdik. Daha önce yazdığımız AddItem
aksiyonunda NewTodoItem modeli kullanmıştık. Fakat bu
defa beklentimiz sadece id olduğundan böyle bir sınıf
yapmaya gerek yok. ASP.NET Core gelen değeri guid
olarak ayrıştırmaya çalışacak.

88
Maddeleri Onay Kutusu ile Tamamlama

Herhangi bir model oluşturmadığımızdan ve doğruluk


tanımını [Required] yapmadığımızdan dolayı
metodumuz içerisinde ModelState kelimesini
kullanamıyoruz. Fakat bunun yerine Guid.Empty gibi bir
kontrol ile boş olup olmadığını kontrol edebiliriz. Eğer boş
ise BadRequest yani 400 Bad Request dönderebiliriz.

if (id == Guid.Empty) return BadRequest();

Sırada kontrolörün servisi veri tabanı güncellemesi için


çağırması kaldı. Bu yeni bir metod olan MarkDoneAsync ile
ITodoItemService üzerinde yapılmakta. Sonuç olarak

eğer veri tabanı güncellemesi başarılı olursa true aksi


halde false değeri döndürecek.

var successful = await _todoItemService.MarkDoneAsyn


c(id);
if (!successful) return BadRequest();

Sonuç olarak herşey düzgün bir şekilde çalıştıysa


javascript'e Ok() yani 200 OK değerini döndüreceğiz.
Daha kompleks bir yapı ile JSON yapısında farklı veriler
göndermekte mümkün fakat şimdilik buna ihtiyacımız yok.

Servis Metodu Ekleme


İlk olarak MarkDoneAsync 'i arayüze ekleyin:

89
Maddeleri Onay Kutusu ile Tamamlama

Services/ITodoItemService.cs

Task<bool> MarkDoneAsync(Guid id);

Sonra bunun uygulamasını TodoItemService üzerinde şu


şekilde tamamlayın:
Services/TodoItemService.cs

public async Task<bool> MarkDoneAsync(Guid id)


{
var item = await _context.Items
.Where(x => x.Id == id)
.SingleOrDefaultAsync();

if (item == null) return false;

item.IsDone = true;

var saveResult = await _context.SaveChangesAsync


();
return saveResult == 1; // One entity should hav
e been updated
}

Bu metod Entity Framework Core'un Where komutunu


kullanarak ID kolonuna göre arama yapmaktadır.
SingleOrDefaultAsync metodu bulduğu satırı item'a atar

eğer bulamaz ise bu durumda item null olur. Bunun

90
Maddeleri Onay Kutusu ile Tamamlama

kontrolünü yaptıktan sonra doğrudan false dönebiliriz.


Eğer item boş değilse bunun sadece IsDone özelliğini
ayarlayarak güncelleyebiliriz.

item.IsDone = true;

SaveChangesAsync metodu uygulanana kadar yaptığımız

değişiklikler sadece yerelde değişti. SaveChangesAsync


çalıştıktan sonra kaç satırı güncellediyse onu dönderir. Bu
durumda ya 1 tane gönderecek veya 0, ama 0 olursa bir
yanlışlık olduğunu söyleyebileceğiz. Eğer 1 ise true aksi
halde false döndereceğiz.

Test
Uygulamayı çalıştırıp bazı onay kutularını işaretleyin.
Sayfayı yenilediğinizde bu maddelerin yapılacaklar
listesinden silindiğini göreceksiniz. Bunun nedeni
GetIncompleteItemsAsync te bulunan Where filtresidir.

Şu anda, uygulama herkesçe görülebilen yapılacaklar


listesi durumundadır. Eğer kişiye özel bir liste olsa daha
kullanışlı olabilir. Bir sonraki bölümde, ASP.NET Core
Identity kullanarak güvenlik ve kimlik denetleme
özelliklerini entegre edeceğiz.

91
Maddeleri Onay Kutusu ile Tamamlama

92
Güvenlik ve Kimlik

Güvenlik ve Kimlik
Çoğu modern ağ uygulamasında veya Uygulama
Program Arayüzü(API) üzerinde en önemli endişelerden
biri güvenliktir. Uygulamanızı saldırılara karşı korumanız
ve kullanıcıların girdikleri bilgileri güvende tutmanız çok
önemlidir. Bu güvenli önemleri şu şekilde sıralanabilir.

Kullanıcıdan alınan verilerdeki sterilize edip SQL


enjeksiyonu ataklarından korunma.
Cross-Domain (XSRF) saldırılarından korunma
HTTPS (TLS) kullanarak verinin internette giderken
başkalarının araya girmesini engellemek.
Kullanıcılara güvenli bir giriş ortamı ayarlama. Sosyal
ağ hesapları ile de olabilir.
Şifreyi unuttum veya birçok adımda giriş seçenekleri
sunma

ASP.NET Core tüm yukarıdaki işlemleri kolayca


yapmanızı sağlar. İlk iki made varsayılan olarak projede
bulunmaktadır.(SQL enjeksiyonu ve Cross Domain).
HTTPS desteği ise birkaç satır kod ile çözülebilir. Bu
bölüm genel olarak kimlik üzerinde durmaktadır. Bunlar
kullanıcı kaydetme ve giriş yapmasını sağlama.
Sonrasında ise bu kullanıcıları yetkilendirebilmedir.

93
Güvenlik ve Kimlik

Yetki ve kimlik doğrulama genel olarak birbirleri ile


karıştırılan iki terimdir. Kimlik doğrulama
kullanıcının giriş yapıp yapamayacağıdır. Buna
karşılık Yetki ise kullanıcı giriş yaptıktan sonra nelere
yetkisi olduğuyla alakalıdır. Şu şekilde
düşünebilirsiniz. Kimlik Doğrulama "Ben bu
kullanıcıyı tanıyor muyum?" diye sorarken. Yetki "Bu
kullanıcının şunu yapmaya yetkisi var mı?" diye
sorar.

projemizi MVC + Kimlik doğrulama şablonu ile


oluşturmuştunuz. Bu şablon içerisinde Kimlik Doğrulama
ve Yetki için birçok sınıf doğrudan eklenmiştir.

ASP.NET Core Kimlik ne işe


yarar?
ASP.NET Core Kimlik, ASP.NET Core ile birlikte gelir.
ASP.NET CORE ekosisteminde bulunan eklentiler gibi bu
da bir NuGet paketidir ve böylece her projeye kurulabilir. (
Eğer varsayılan temayı kullanıyorsanız doğrudan yüklü
gelmektedir)

ASP.NET Core Kimlik, kullanıcı bilgilerini tutar şifreleri


karıştırarak güvenliğini sağlar ve kullanıcı rollerini yönetir.
Email/Şifre bilgileri ile giriş yapılmasını sağlar, sosyal ağ

94
Güvenlik ve Kimlik

ile kullanıcı girişi yapılmasına zemin hazırlar. Böylece


Google, Facebook veya Twitter bilgileri ile kullanıcılar
servisinize giriş yapabilir. Bu servisler OAuth 2.0 veya
OpenId kullanmaktadırlar.

Kayıt olma ve Giriş ekranları da varsayılan şablon ile


gelmektedir. Hatta şu anda kayıt olabilir ve giriş
yapabilirsiniz.

95
Facebook Girişi Ekleme

Facebook Login Ekleme


Projeyi oluşturduğumuzda kişisel izin şablonu doğrudan
kullanıcın kayıt olmasına ve giriş yapmasına olanak
sağlar. Bu özelliğe ek olarak farklı servisleri de
kullanabiliriz. Bunlar Google, Facebook veya Twitter
olabilir.

Harici servisleri kullanmak için aşağıdaki iki maddeyi


uygulamalısınız.

1. Harici servis sağlayıcıda App veya client


oluşturmalısınız.

2. Oluşturduğunuz App sonucunda size sağlanacak


secret ve ID'yi kodunuzda kullanmalısınız.

Facebook'ta App Oluşturma


https://developers.facebook.com/apps adresinden yeni bir
Facebook uygulaması oluşturabilirsiniz. Add a New App'
tıklayarak yönergeleri uygularsanız uygulamayı oluşturur
ve app ID sahibi olursunuz.

Eğer Facebook hesabınız yoksa Google veya


Twitter'da da bu işlemler aynıdır. Tabi basamaklar
aynı olabilir ama sonucunda size bir kod verecektir.

96
Facebook Girişi Ekleme

Sonrasında Facebook Login'e tıklayarak sol tarafta


bulunan Settings bölümüne girin.

Aşağıdaki URL'i Valid OAuth redirect URIs bölümüne


yapıştırın.

http://localhost:5000/signin-facebook

Save Changes ile değişiklikleri kaydedip Dashboard


sayfasına geçin. Burada sizin uygulamanız için
oluşturulmuş App ID ve Secret'i göreceksiniz.

97
Facebook Girişi Ekleme

Facebook Login'i etkinleştirmek için Aşağıdaki kodu


Startup sınıfı ConfigureService metodu içerisine

yapıştırın

services
.AddAuthentication()
.AddFacebook(options =>
{
options.AppId = Configuration["Facebook:AppI
d"];
options.AppSecret = Configuration["Facebook:
AppSecret"];
});

Burada bulunan şifreleri doğrudan yazmak yerine bunların


appsettings.json ayar dosyasından çekilmesi

sağlanabilir. Fakat bu dosya kaynak kontrolü(Source


Control) üzerinde tutulduğundan dolayı. Bu özel bilgilerin
taşınması iyi değildir. Örneğin bunu Github üzerinde
herkesin görebileceği bir proje olarak yayınlar isek bu
durumda başka birisi sizin adınıza işlem yapabilir.

Gizli bilgileri Gizlilik Yönetici(Secrets


Manager) ile saklama
Hassas bilgilerin kayıt edilmesi için Gizlilik Yöneticisi
Aracını kullanabilirsiniz. Bunun için komut satırını veya
terminali açın ( ana klasörde olduğunuza emin olun ) ve

98
Facebook Girişi Ekleme

aşağıdaki komutu yazın.

dotnet user-secrets --help

Facebook sayfasından ID ve secret'i kopyalayarak


aşağıdaki gibi iki defa set komutunu çalıştırarak bu
bilgileri sistmee girin.

dotnet user-secrets set Facebook:AppId <App Id'yi ya


pıştırın>
dotnet user-secrets set Facebook:AppSecret <App Secr
et'i yapıştırın>

Gizlilik yöneticisi bu bilgileri Configuration içerisine


yazdığında ve uygulama ayağa kalktığında
ConfigureService içerisinde doğrudan kullanılabilir hale

gelir.

Uygulamanızı çalıştırın ve navigasyonda bulunan giriş


butonuna tıklayın. Facebook butonunu göreceksiniz.

99
Facebook Girişi Ekleme

Facebook bilgileri ile giriş yapmaya çalışın. Facebook'tan


izinleri alacak ve sonrasında sisteminize kullanıcı girişi
yapacaksınız.

100
Kimlik Kontrolü

Kimlik Doğrulama
Çoğu uygulamada kullanıcı girişi mevcuttur. Kullanıcı giriş
yaparak kendine ait bölümleri gezebilir veya işlem
yapabilir. Ana sayfayı herkese göstermek mantıklı olabilir,
fakat yapılacaklar listesini sadece giriş yapmış olan
kişilerin görmesi gerekmekte.

ASP.NET Core [Authorize] özelliğini kullanarak


kullanıcının bazı aksiyon veya kontrolörün tamamına giriş
kontrolü ekleyebiliriz. Aşağıdaki kimlik kontrolü bu
kontrolör içerisindeki tüm aksiyonlara uygulanır.

[Authorize]
public class TodoController : Controller
{
// ...
}

Aşağıdaki using cümlesini de sınıfınız üzt tarafına


yapıştırın.

using Microsoft.AspNetCore.Authorization;

101
Kimlik Kontrolü

Uygulamanızı çalıştırıp kullanıcı girişi yapmadan /todo


sayfasına gidin. Giriş sayfasına otomatik olarak
yönlendirildiğinizi göreceksiniz.

Doğrulama ve Yetki birbiri ile karıştırılan özellikler.


Burada doğrulama yapıyorsunuz. Kullanıcının neye
yetkisi olduğunu belirtmedik. Yani sadece giriş yapıp
yapmadığı bilgisine bakıyorsunuz.

102
Uygulama İçerisinde Kimlik Kullanma

Uygulama İçerisinde Kişiye


Has Özellikleri Kullanma
Yapılacaklar listesinde hala kullanıcı bazında bir ayrım
bulunmamaktadır. [Authorize] özelliği sayesinde
kullanıcı giriş yaptıysa tüm listeyi görebiliri. Bunun ile
birlikte giren kullanıcıya göre bu filtreyi genişletmek
gerekmekte.

Öncelikle UserManager<Applicationuser> 'ı


TodoController a enjekte edin.

Controllers/TodoController.cs

103
Uygulama İçerisinde Kimlik Kullanma

[Authorize]
public class TodoController : Controller
{
private readonly ITodoItemService _todoItemServi
ce;
private readonly UserManager<ApplicationUser> _u
serManager;

public TodoController(ITodoItemService todoItemS


ervice,
UserManager<ApplicationUser> userManager)
{
_todoItemService = todoItemService;
_userManager = userManager;
}

// ...
}

Üst taraftaki using cümlesine dikkat edin.

using Microsoft.AspNetCore.Identity;

UserManager sınıfı ASP.NET Core Kimlik'e ait bir sınıftır.

Bu sınıf sayesinde kullanıcıya ait bilgileri alabilirsiniz.

104
Uygulama İçerisinde Kimlik Kullanma

public async Task<IActionResult> Index()


{
var currentUser = await _userManager.GetUserAsyn
c(User);
if (currentUser == null) return Challenge();

var todoItems = await _todoItemService.GetIncomp


leteItemsAsync(currentUser);

var model = new TodoViewModel()


{
Items = todoItems
};

return View(model);
}

Aksiyon içerisinden User özelliğini kullanarak


UserManager vasıtası ile kullanıcıların özelliklerine

erişebilirsiniz.

var currentUser = await _userManager.GetUserAsync(Us


er);

Şimdi sorabilirsiniz:"Madem User objemiz zaten


aksiyondan erişilebilir durumda, peki neden
UserManager 'a çağrı yapmamız gerekiyor". Burada

bulunan User objesinin içerisinde çok basit veriler


tutulur. UserManager ise bu kullanıcıya ait tim bilgilerin veri
tabanından getirilmesini sağlar.

105
Uygulama İçerisinde Kimlik Kullanma

currentUser değeri hiç bir zaman null olmamalıdır. Zaten

daha önce [Authorize] ile kontrolörü ile giriş yapılmadan


kullanılamayacağını belirtmiştiniz. Fakat yine de eğer
kullanıcı giriş yapmadıysa veya her hangi bir şekilde o an
bir problem olduysa onu giriş sayfasına yönlendirmek iyi
bir pratiktir. Bunun için Challenge() metodunu
kullanabilirsiniz.

if (currentUser == null) return Challenge();

Artık ApplicationUser parametresini


GetIncompleteItemsAsync 'e gönderdiğinize göre,

ITodoItemService arayüzünde de değişiklik yapmalısınız.

Services/ITodoItemService.cs

public interface ITodoItemService


{
Task<IEnumerable<TodoItem>> GetIncompleteItemsAs
ync(ApplicationUser user);

// ...
}

Sıradaki işlem veri tabanını güncelleyerek sadece giriş


yapan kullanıcıya has verileri getirmektir.

Veri tabanını güncelleme

106
Uygulama İçerisinde Kimlik Kullanma

TodoItem modeline yeni bir özellik ekleyerek

kullanıcıların bilgilerinin tutulmasını sağlamalısınız.

public string OwnerId { get; set; }

Tabi şu anda sadece modelde değişiklik yaptınız ve bunu


henüz uygulamadınız. Bundan dolayı daha önce
yaptığımız gibi göç ettirmeniz gerekmekte. Bunun için
terminalde veya komut satırında dotnet ef komutunu
kullanarak aşağıdaki işlemi yapın.

dotnet ef migrations add AddItemOwnerId

Bu yeni bir göç oluşturarak bunun adını AddItemOwner


verir. Modelde yaptığımız değişiklikler burada da
görülebilir.

Eğer SQLite kullanıyorsanız bu göç'ü otomatik


yapamazsınız. Bunun için Migration Oluşturma
bölümüne bakabilirsiniz. Tekrar dotnet ef komutu
ile bu göçü uygulayabilirsiniz.

dotnet ef database update

Servis sınıfını güncelleme

107
Uygulama İçerisinde Kimlik Kullanma

Veri tabanı ve veri tabanı konteksi güncellendi, artık


TodoItemService içerisinde bulunan

GetIncompleteItemsAsync metodunu değiştirebilir ve

Where sorgusu ekleyebilirsiniz.

Services/TodoItemService.cs

public async Task<IEnumerable<TodoItem>> GetIncomple


teItemsAsync(ApplicationUser user)
{
return await _context.Items
.Where(x => x.IsDone == false && x.OwnerId =
= user.Id)
.ToArrayAsync();
}

Yeni bir kullanıcı oluşturur ve giriş yaparsanız,


yapılacaklar listesini boş göreceksiniz. Malesef yeni bir
yapılacak eklediğinizde de bu değişmeyecek, çünkü yeni
yapılacak ekleme olayının içerisinde kullanıcı bilgilerini
kaydetme işlemini yapmanız henüz.

Yeni Yapılacak Olayı Güncellemesi ve


Tamamlandı Operasyonu
UserManager kullanarak giriş yapmış kullanıcının

bilgilerini AddItem ve MarkDone aksiyonları içerisine


paslayabilirsiniz. Bunu daha önce Index aksiyonunda

108
Uygulama İçerisinde Kimlik Kullanma

yapmıştınız. Buradaki tek fark bu metodların kullanıcıyı


eğer giriş yapmamışsa giriş ekranına yönlendirmek yerine
401 Unauthorized döndürmesidir.

TodoController içerisini aşağıdaki gibi güncelleyin.

public async Task<IActionResult> AddItem(NewTodoItem


newItem)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}

var currentUser = await _userManager.GetUserAsyn


c(User);
if (currentUser == null) return Unauthorized();

var successful = await _todoItemService.AddItemA


sync(newItem, currentUser);
if (!successful)
{
return BadRequest(new { error = "Could not a
dd item." });
}

return Ok();
}

public async Task<IActionResult> MarkDone(Guid id)


{
if (id == Guid.Empty) return BadRequest();

var currentUser = await _userManager.GetUserAsyn

109
Uygulama İçerisinde Kimlik Kullanma

c(User);
if (currentUser == null) return Unauthorized();

var successful = await _todoItemService.MarkDone


Async(id, currentUser);
if (!successful) return BadRequest();

return Ok();
}

İki serviste ApplicationUser parametresini kabul etmeli.


ITodoItemService içerisini aşağıdaki gibi güncelleyin.

Task<bool> AddItemAsync(NewTodoItem newItem, Applica


tionUser user);

Task<bool> MarkDoneAsync(Guid id, ApplicationUser us


er);

Son olarak TodoItemService içerisinde bulunan servis


metodunu güncelleyin. AddItemAsync metodu için Owner
özelliğini yeti TodoItem oluştururken ayarlayın.

110
Uygulama İçerisinde Kimlik Kullanma

public async Task<bool> AddItemAsync(NewTodoItem new


Item, ApplicationUser user)
{
var entity = new TodoItem
{
Id = Guid.NewGuid(),
OwnerId = user.Id,
IsDone = false,
Title = newItem.Title,
DueAt = DateTimeOffset.Now.AddDays(3)
};

// ...
}

MarkDoneAsync metodu içerisinde kullanıcıyı kontrol

etmelisiniz. Böylece sadece yapılacak ID'si ile güncelleme


yapılamayacaktır.

public async Task<bool> MarkDoneAsync(Guid id, Appli


cationUser user)
{
var item = await _context.Items
.Where(x => x.Id == id && x.OwnerId == user.
Id)
.SingleOrDefaultAsync();

// ...
}

111
Uygulama İçerisinde Kimlik Kullanma

Bunu da tamamladık. Bundan sonra her kullanıcı kendi


adına yapılacak ekleyebilecek ve başkası bunu
göremeyecek veya tamamlandı olarak belirtemeyecek.

112
Role Göre Yetkilendirme

Role göre yetkilendirm


Yetki ve izin mekanizmasını genel olarak Roller
vasıtasıyla gerçekleştirilmektedir. Örneğin Admin roünde
bir kişi tüm kullanıcıları görüp bu kullanıcılar üzerinde
işlem yapabilirken. Normal kullanıcı rolune sahip birisi
sadece kendine ait bilgileri görebilir.

Yönetici Ekleme
Önce aşağıdaki gibi bir kontrolör oluşturun

Controllers/ManageUsersController.cs

using System;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using AspNetCoreTodo.Models;
using Microsoft.EntityFrameworkCore;

namespace AspNetCoreTodo.Controllers
{
[Authorize(Roles = "Administrator")]
public class ManageUsersController : Controller
{
private readonly UserManager<ApplicationUser

113
Role Göre Yetkilendirme

> _userManager;

public ManageUsersController(UserManager<App
licationUser> userManager)
{
_userManager = userManager;
}

public async Task<IActionResult> Index()


{
var admins = await _userManager
.GetUsersInRoleAsync("Administrator"
);

var everyone = await _userManager.Users


.ToArrayAsync();

var model = new ManageUsersViewModel


{
Administrators = admins,
Everyone = everyone
};

return View(model);
}
}
}

Dikkat ettiyseniz [Authorize] bölümünde Rol belirtilmiş.


Bu kullanıcının hem giriş yaptığını hem de Administrator
rolüne sahip olduğunu garanti eder. Aksi halde kullanıcı
bu kontrolöre giriş yapamaz.

114
Role Göre Yetkilendirme

Şimdi bir Görüntü Modeli oluşturun


Models/ManageUsersViewModel.cs

using System.Collections.Generic;
using AspNetCoreTodo.Models;

namespace AspNetCoreTodo
{
public class ManageUsersViewModel
{
public IEnumerable<ApplicationUser> Administ
rators { get; set; }

public IEnumerable<ApplicationUser> Everyone


{ get; set; }
}
}

Son olarak Görüntüyü oluşturun


Views/ManageUsers/Index.cshtml

@model ManageUsersViewModel

@{
ViewData["Title"] = "Manage users";
}

<h2>@ViewData["Title"]</h2>

<h3>Administrators</h3>

<table class="table">

115
Role Göre Yetkilendirme

<thead>
<tr>
<td>Id</td>
<td>Email</td>
</tr>
</thead>

@foreach (var user in Model.Administrators)


{
<tr>
<td>@user.Id</td>
<td>@user.Email</td>
</tr>
}
</table>

<h3>Everyone</h3>

<table class="table">
<thead>
<tr>
<td>Id</td>
<td>Email</td>
</tr>
</thead>

@foreach (var user in Model.Everyone)


{
<tr>
<td>@user.Id</td>
<td>@user.Email</td>
</tr>
}
</table>

116
Role Göre Yetkilendirme

Uygulamayı başlatın, normal kullanıcı olarak giriş yapın


ve /ManageUsers sayfasına gidin. Aşağıdaki gibi bir Giriş
Engellendi sayfasını göreceksiniz.

Bu sayfayı görmenizin nedeni web uygulamanıza


Administrator olarak giriş yapmadığınızdan dolayıdır.

Test Admin Hesabı Oluşturma


Kullanıcılara kayıt olurken bir onay kutusu ile admin olmak
istiyormusunuz demek çokta mantıklı değidir. Bunun
yerine Startup sınıfına yazacağımız kod ile test admin
hesabı oluşturmak mümkündür. Startup sistem ayağa
kalkarken çalışacak ve böylece bir admin hesabı
uygulama başladığında kullanımınıza hazır hale
gelecektir.

Aşağıdaki değişikliği Configure metodu içinde yer alan


if(env.IsDevelopment()) kontrolünün içerisine yazın.

117
Role Göre Yetkilendirme

Startup.cs

if (env.IsDevelopment())
{
// (... some code)

// Make sure there's a test admin account


EnsureRolesAsync(roleManager).Wait();
EnsureTestAdminAsync(userManager).Wait();
}

EnsureRoleAsync ve EnsureTestAdminAsync metodları

RoleManager ve UserManager servislerine ihtiyaç duyar.

Bundan dolayı bu servisleri Startup sınıfına enjekte


etmemiz gerekmekte. Bunun için aşağıdaki değişikliği
yapın

public void Configure(IApplicationBuilder app,


IHostingEnvironment env,
UserManager<ApplicationUser> userManager,
RoleManager<IdentityRole> roleManager)
{
// ...
}

Aşağıdaki iki metodu Configure metodunun altına


yapıştırın Önce EnsureRolesAsync metodu

118
Role Göre Yetkilendirme

private static async Task EnsureRolesAsync(RoleManag


er<IdentityRole> roleManager)
{
var alreadyExists = await roleManager.RoleExists
Async(Constants.AdministratorRole);

if (alreadyExists) return;

await roleManager.CreateAsync(new IdentityRole(C


onstants.AdministratorRole));
}

Bu metod veri tabanında Administrator rolünün olup


olmadığını kontrol eder. Eğer yoksa oluşturur. Her yerde
Administrator 'ü elle yazmaktansa Constants adında

yeni bir sınıf oluşturup onun içerisine sabit değerlerimizi


yazabiliriz.
Constants.cs

namespace AspNetCoreTodo
{
public static class Constants
{
public const string AdministratorRole = "Adm
inistrator";
}
}

119
Role Göre Yetkilendirme

Daha önce oluşturduğumuz ManageUsersController


kontrolörünü de bu sabit ile değiştirebilirsiniz.

EnsureTestAdminAsync metodunu yazın

Startup.cs

private static async Task EnsureTestAdminAsync(UserM


anager<ApplicationUser> userManager)
{
var testAdmin = await userManager.Users
.Where(x => x.UserName == "admin@todo.local"
)
.SingleOrDefaultAsync();

if (testAdmin != null) return;

testAdmin = new ApplicationUser { UserName = "ad


min@todo.local", Email = "admin@todo.local" };
await userManager.CreateAsync(testAdmin, "NotSec
ure123!!");
await userManager.AddToRoleAsync(testAdmin, Cons
tants.AdministratorRole);
}

Eğer admin@todo.local isminde bir kullanıcı yoksa, bu


metod yeni bir kullanıcı oluşturacak ve geçici şifre
atayacak. Giriş yaptıktan sonra şifreyi daha güvenli bir
şifre ile değiştirmeniz gerekmekte.

120
Role Göre Yetkilendirme

Bu iki metodda asenkron ve Task


döndürdüklerinden bunlar Configure içerisinde
wait ile kullanılmalılar. Aksi halde Configure

metodu çalışmaya devam eder ve bu değişiklikleri


atlar. Bunun için normalde await kullanmamız
gerekmekte. Fakat teknik nedenlerden dolayı
Configure içerisinde await kelimesini

kullanamıyoruz. Bundan dolayı başka bir yerde


await kullanmamız gerekmekte.

Uygulamayı çalıştırdığınızda admin@todo.local hesabı


oluşturulacak ve Administrator rolü atanacak. Bu
kullanıcı ile giriş yapaıp
http://localhost:5000/ManageUsers sayfasına

gidin.Uygulamaya kayıt olmuş tüm kullanıcıların olduğu


listeyi göreceksiniz.

Bunun yanında kendiniz yeni bir özellik


ekleyebilirsiniz. Örneğin bir buton ile sadece Admin'in
kullanıcıları silebileceği değişikliği yapabilirsiniz.

Görüntü sayfasında kullanıcı


kontrolü
[Authorize] özelliği ile kontrolör veya aksiyonda kullanıcı

kontrolü yapmak mümkün, fakat ya Görüntü dosyasına


buna ihtiyacımız olursa? Örneğin "Kullanıcı Yönetimi"

121
Role Göre Yetkilendirme

bağlantısı menüde olsa çok işimize yarar. Fakat bu linkin


sadece Admin kullanıcısına görünmesi gerekmekte.

UserManager 'i doğrudan Görüntü dosyasına enjekte

etmek mümkün, bunun vasıtasıyla istediğiniz kontrolleri


yapabiliriz. Görüntü dosyamızı daha temiz tutmak için
yeni bir kısmi görüntü dosyası ( partial view ) oluşturun.
Bu kısmi görüntü içerisinde kontrollerimizi yapacağız ve
eğer giren kullancıcı Administrator rolüne sahipse
"Kullanıcı Yönetimi" bağlantısını görebilir.

Views/Shared/_AdminActionsPartial.cshtml

122
Role Göre Yetkilendirme

@using Microsoft.AspNetCore.Identity
@using AspNetCoreTodo.Models

@inject SignInManager<ApplicationUser> SignInManager


@inject UserManager<ApplicationUser> UserManager

@if (SignInManager.IsSignedIn(User))
{
var currentUser = await UserManager.GetUserAsync
(User);

var isAdmin = currentUser != null


&& await UserManager.IsInRoleAsync(currentUs
er, Constants.AdministratorRole);

if (isAdmin) {
<ul class="nav navbar-nav navbar-right">
<li><a asp-controller="ManageUsers" asp-
action="Index">Manage Users</a></li>
</ul>
}
}

Kısmi Görüntü küçük bir görüntü parçasıdır, genelde


diğer Görüntü dosyalarına gömülürler. Bundan dolayı
belirleyici olarak başlangıçlarına _ işareti koyularak
kısmi olduğu belirtilir. Tabi bu zorunlu değildir.

Bu kısmi görüntü önce SignInManager ile kullanıcının giriş


yapıp yapmadığını kontrol eder. Eğer giriş
yapmamışlarsa, kodun geri kalanının önemi yoktur. Eğer

123
Role Göre Yetkilendirme

giriş yapmış kullanıcı varsa, UserManager ile


IsInRoleAsync metodu çağırılarak bu kullanıcının diğer

özelliklerine bakılır. Eğer bu kontrol başarılı ise


navigasyona "Kullanıcı Yönetimi" eklenir.

Tabi bu kısmi görüntünün ana görüntüye gömülmesi


gerekmekte. Bunun için _Layout.cshtml içerisine gidin ve
aşağıdaki değişikliği yapın.
Views/Shared/_Layout.cshtml

<div class="navbar-collapse collapse">


<ul class="nav navbar-nav">
<li><a asp-area="" asp-controller="Home" asp
-action="Index">Home</a></li>
<li><a asp-area="" asp-controller="Home" asp
-action="About">About</a></li>
<li><a asp-area="" asp-controller="Home" asp
-action="Contact">Contact</a></li>
</ul>
@await Html.PartialAsync("_LoginPartial")
@await Html.PartialAsync("_AdminActionsPartial")
</div>

Admin kullanıcısı ile girdiğinizde sağ üst tarafta aşağıdaki


gibi "Kullanıcı Yönetimi" (Manage Users) bağlantısını
göreceksiniz.

124
Role Göre Yetkilendirme

Özet
ASP.NET Core Kimlik oldukça güçlü bir güvenlik ve kimlik
sistemidir. Bu sistem ile kimlik doğrulaması ve izin
kontrolleri yapabilir. Buna harici kimlik servis
sağlayıcılar(facebook, google, twitter) ekleyebilirsiniz.
dotnet new şablonu size kullanıcı girişi ve kayıdı gibi

genel senaryoları içeren projeyi oluşturur.

ASP.NET Core Kimlik ile ilgili daha detaylı bilgiyi


https://docs.asp.net adresinde bulabilirsiniz.

125
Otomatik Test

Otomatik Test
Test uygulama yazmanın vazgeçilmez unsurlarından
biridir. Test yazmak problemlerden sakınmanızı sağlar
ayrıca ileride kodlarınızı düzenlediğinizde veya yeni
eklentiler yaptığında eski yaptıklarınızı etkileyip
etkilemediğini kontrol edebilisiniz.

Bu bölümde ASP.NET Core uygulamalarında Unit testi


veya Entegrasyon testi nasıl yazılır bunu
öğreneceksiniz. Unit testi bir metod veya birkaç satır
kodun doğru çalışıp çalışmadığını kontrol etmek için
yazılır. Entegrasyon Testi bazen Fonksiyonel test
olarak da adlandırılır daha büyük test paketidir. Gerçek
senaryolara göre sistemin katmanları göz önüne alınarak
test projenin test edilmesidir.

126
Unit Testi

Unit Testi
Unit testi tek metodun veya kısa bir mantıksal akışı test
etmek için kullanılır. Sistemin tamamını test etmek yerine,
unit testi sahte veriler kullanarak gerçek metodun
yapması gereken işi doğru yapıp yapmadığını test eder.

Örneğin TodoController 'in iki bağımlılığı vardır. Bunlar :


ItodoItemService ve UserManager 'dır. TodoItemService

ise ApplicationDbContext 'e bağımlıdır. ( Şu şekilde


bağlımlılık grafiğini çizebiliriz. TodoController -
> TodoItemService -> ApplicationDbContext )

Normalde ASP.Core bağımlılık enjeksiyon sistemi


TodoController veya TodoItemService

oluşturulduğundayukarıda bulunan tüm bağımlılık


grafiğini enjekte eder.

Fakat unit tesi yazarken bu enjeksiyonları elle yapmak


gerekmektedir. Bu da mantığı sınıf veya method bazında
izole edebileceğiniz anlamına gelir. (Test yazarken
yanlışlıkla veri tabanına yazmak istemezsiniz.)

Test Projesi Oluşturma

127
Unit Testi

Test için genelde ayrı bir proje oluşturulur. Yeni test projesi
eskiden oluşturduğunuz ana projenin yanına ( içine değil)
oluşturulur.

Eğer şu anda projenizin ana dizinindeyseniz cd ile bir


üst klasöre çıkın. Sonra aşağıdaki komutları uygulayarak
yeni bir test projesi oluşturun.

mkdir AspNetCoreTodo.UnitTests
cd AspNetCoreTodo.UnitTests
dotnet new xunit

xUnit.NET unit ve entegrasyon testi için kullanılan popüler


bir test iskelettir. Her projemizde olduğu gibi bu proje de
aslında NuGet paketlerinden oluşur. dotnet new xunit
test için gerekli olan herşeyi sizin için oluşturur.

Klasör yapınız şu anda aşağıdaki gibi olmalı:

AspNetCoreTodo/
AspNetCoreTodo/
AspNetCoreTodo.csproj
Controllers/
(etc...)

AspNetCoreTodo.UnitTests/
AspNetCoreTodo.UnitTests.csproj

128
Unit Testi

Test projesinde kullanacağınız sınıflar ana projede


olduğundan dolayı bunları ana projeye atfetmeniz
gerekmekte.

dotnet add reference ../AspNetCoreTodo/AspNetCoreTod


o.csproj

Ardından UnitTest1.cs dosyasınız silin. Artık test


yazmaya hazırsınız.

Servis testi Yazma


TodoItemService içindeki AddItemAsync metodunun

mantığına bakın:

129
Unit Testi

public async Task<bool> AddItemAsync(NewTodoItem new


Item, ApplicationUser user)
{
var entity = new TodoItem
{
Id = Guid.NewGuid(),
OwnerId = user.Id,
IsDone = false,
Title = newItem.Title,
DueAt = DateTimeOffset.Now.AddDays(3)
};

_context.Items.Add(entity);

var saveResult = await _context.SaveChangesAsync


();
return saveResult == 1;
}

Bu metod gelen verilere bakarak yeni bir nesne üretir ve


bunu veri tabanına yazar.

OwnerId giriş yapmış kullanının ID'si olmalı.

Yeni madde her zaman başlangıçta tamamlanmamış


olmalı. ( IsDone = false )
Başlık gelen objeden kopyalanmalı : newItem.Title
Her yeni gelen madde bitiş zamanı( DueAt ) 3 gün
sonra olarak ayarlanmalı.

130
Unit Testi

Bu tipte alınan karalara iş mantığı(Business Logic)


denir. Tabi bunun yanında iş mantığı hesaplamaları
da içerir.

Bu karalar mantığın oluşmasına yardımcı olur. Örneğin


başka birisi AddItemAsync içerisinde değişiklik yapıyor
fakat daha önceki kararlardan haberi yok. Bu basit
servisler için problem oluşturmayabilir. Fakat büyük
servislerde bunun problem oluşturduğunu söylenebilir.

TodoItemService metodunu test eden sınıfı yeni

oluşturduğunuz projede şu şekilde oluşturun:


AspNetCoreTodo.UnitTests/TodoItemServiceShould.cs

131
Unit Testi

using System.Threading.Tasks;
using AspNetCoreTodo.Data;
using AspNetCoreTodo.Models;
using AspNetCoreTodo.Services;
using Microsoft.EntityFrameworkCore;
using Xunit;

namespace AspNetCoreTodo.UnitTests
{
public class TodoItemServiceShould
{
[Fact]
public async Task AddNewItem()
{
// ...
}
}
}

[Fact] özelliği xUnit.NET paketinden gelmektedir, ve bu

özelliği ekleyerek bu metodun test metodu olduğunu


belirtmiş oldunuz.

Testleri isimlendirmek için bir çok yöntem


bulunmaktadır. Hepsinin pozitif ve negatif yanları
vardır. Bu projede sondan ekleme ile test sınıflarını
oluşturacaksınız. Örnek TodoItemServiceShould ,
elbette siz istediğiniz yöntemi kullanabilirsiniz.

132
Unit Testi

TodoItemService ApplicationDbContext 'e bağımlıdır, yani

canlıda bulunan veri tabanına. Bunu test için kullanmak


doğru bir yöntem değildir. Bunun yerine Entity İskeletinde
bulunan in-memory veri tabanı sağlayıcısını
kullanabilirsiniz. Tüm veri tabanı hafıza'da yer aldığından.
Test her başladığında temizlenir. Fakat TodoItemService
bunun farkını anlamayacaktır.

DbContextOptionsBuilder ile in-memory veri tabanı

oluşturup sonrasında AddItem 'a çağrı yapabilirsiniz.

133
Unit Testi

var options = new DbContextOptionsBuilder<Applicatio


nDbContext>()
.UseInMemoryDatabase(databaseName: "Test_AddNewI
tem").Options;

// Set up a context (connection to the DB) for writi


ng
using (var inMemoryContext = new ApplicationDbContex
t(options))
{
var service = new TodoItemService(inMemoryContex
t);

var fakeUser = new ApplicationUser


{
Id = "fake-000",
UserName = "fake@fake"
};

await service.AddItemAsync(new NewTodoItem { Tit


le = "Testing?" }, fakeUser);
}

Son satır Testing? adında yeni bir yapılacak maddesi


ekler ve in-memory veri tabanına bunu kaydetmesini
söyler. Bu iş mantığının doğru çalışabilmesi için kaydılan
veriyi veri tabanından almalı ve kontrol etmelisiniz.

134
Unit Testi

// Use a separate context to read the data back from


the DB
using (var inMemoryContext = new ApplicationDbContex
t(options))
{
Assert.Equal(1, await inMemoryContext.Items.Coun
tAsync());

var item = await inMemoryContext.Items.FirstAsyn


c();
Assert.Equal("Testing?", item.Title);
Assert.Equal(false, item.IsDone);
Assert.True(DateTimeOffset.Now.AddDays(3) - item
.DueAt < TimeSpan.FromSeconds(1));
}

Öncelikle mantık testi: Sadece 1 tane veri olmalı,


sonrasında bu verileri FirstAsync ile almalı ve beklenen
değerler uygunmu bu kontrol edilmeli.

Tarihi kontrol etmek biraz çetrefilli, iki tarihin eşitli


milisaniye bazında neredeyse her zaman yanlış
olacağından. Bunu aralık kontrolç olarak yapmak daya
bantıklıdır. Son satırda bunu görebilirsiniz.

Hem unit testi hem de entegrasyon testi AAA kalıbını


takip eder: objeler ve veriler önce ayarlanır, sonra
bazı işlemler yapılır ve son olarak Assert ile bunların
kontrolleri, beklenen değerleri karşılayıp
karşışamadıkları kontrol edilir.

135
Unit Testi

AddNewItem 'ın son hali:

AspNetCoreTodo.UnitTests/TodoItemServiceShould.cs

public class TodoItemServiceShould


{
[Fact]
public async Task AddNewItem()
{
var options = new DbContextOptionsBuilder<Ap
plicationDbContext>()
.UseInMemoryDatabase(databaseName: "Test
_AddNewItem")
.Options;

// Set up a context (connection to the DB) f


or writing
using (var inMemoryContext = new Application
DbContext(options))
{
var service = new TodoItemService(inMemo
ryContext);
await service.AddItemAsync(new NewTodoIt
em { Title = "Testing?" }, null);
}

// Use a separate context to read the data b


ack from the DB
using (var inMemoryContext = new Application
DbContext(options))
{
Assert.Equal(1, await inMemoryContext.It
ems.CountAsync());

var item = await inMemoryContext.Items.F

136
Unit Testi

irstAsync();
Assert.Equal("Testing?", item.Title);
Assert.Equal(false, item.IsDone);
Assert.True(DateTimeOffset.Now.AddDays(3
) - item.DueAt < TimeSpan.FromSeconds(1));
}
}
}

Testi çalıştırma
Terminalde ( şu anda AspNetCoreTodo.UnitTests
klasöründe olduğunuzu varsayarak) aşağıdaki kodu
çalıştırın.

dotnet test

Test komutu tüm projeyi kontrol ederk test olarak

tanımladığınız ( [Fact] ) metodları veya sınıfları bulur ve


aşağıdaki gibi bir çıktı veririr.

137
Unit Testi

Starting test execution, please wait...


[xUnit.net 00:00:00.7595476] Discovering: AspNetCo
reTodo.UnitTests
[xUnit.net 00:00:00.8511683] Discovered: AspNetCo
reTodo.UnitTests
[xUnit.net 00:00:00.9222450] Starting: AspNetCo
reTodo.UnitTests
[xUnit.net 00:00:01.3862430] Finished: AspNetCo
reTodo.UnitTests

Total tests: 1. Passed: 1. Failed: 0. Skipped: 0.


Test Run Successful.
Test execution time: 1.9074 Seconds

TodoItemService 'i kapsayan (Coverage) bir test sunmuş

oldunuz. Artık aşağıdaki testleri yazabilirsiniz.

MarkDoneAsync eğer paslanan ID yok ise false

dönsün.
MarkDoneAsync eğer gerçekten var olan bir maddeyi

tamamlandı olarak işaretler ise true dönsün.


GetIncompleteItemsAsync sadece belirli bir kullanıcıya

ait verileri döndürür.

138
Integration Testi

Entegrasyon Testi
Unit teste nazaran entegrasyon testi yönlendirme,
kontrolörler, servisler ve veri tabanlarının tamamını test
etmeyi amaçlar. Bir sınıfı izole etmek yerine tüm
komponentler düzgün bir şekilde senkronize çalışıyor mu
bunun kontrol edilmesini sağlar.

Entegrasyon testleri daha yavaş ve unit testlere göre


daha agrasif yani herşeyi test etme amacındadır. Bir
projenin bir çok unite testi olsa da entegrasyon desti
oldukça azdır.

Yönlendirme de dahil herşeyin test edilmesi gerektiğini


savunduğundan, genelde web tarayıcı gibi HTTP çağrısı
yapabilecek yapılar içerir.

Entegresyon testini yapabilmek için sunucunuzun çalışır


durumda olması gerekir. ASP.NET Core test etme
amacıyla bu sunucunun TestServer sınıfıyla çalıştırılıp
test bittikten sonra otomatik olarak kapatılmasını
sağlayabilir.

Yeni bir test projesi oluşturun

139
Integration Testi

Entegrasyon testi ile unite testini birlikte aynı projede


çalıştırabilirsiniz. Fakat daha güzenli olması açısından
farklı projeler oluşturmak daha mantıklı.

Eğer proje dizininde iseniz bir üst klasöre cd ile çıkın ve


şimdi AspNetCoreTodo klasöründe olmanız gerekmekte.
Aşağıdaki komutlar ile yeni bir proje oluşturun.

mkdir AspNetCoreTodo.IntegrationTests
cd AspNetCoreTodo.IntegrationTests
dotnet new xunit

Klasör yapınız aşağıdaki gibi olmalı:

AspNetCoreTodo/
AspNetCoreTodo/
AspNetCoreTodo.csproj
Controllers/
(etc...)

AspNetCoreTodo.UnitTests/
AspNetCoreTodo.UnitTests.csproj

AspNetCoreTodo.IntegrationTests/
AspNetCoreTodo.IntegrationTests.csproj

Unit testte yaptığımız gibi ana projeye referans vermemiz


gerekmekte.

140
Integration Testi

dotnet add reference ../AspNetCoreTodo/AspNetCoreTod


o.csproj

Ayrıca Microsoft.AspNetCore.TestHost NuGet paketini de


aşağıdaki gibi ekleyin:

dotnet add package Microsoft.AspNetCore.TestHost

En son olarak UnitTest1.cs dosyasını silebilirsiniz. Artık


entegrasyon testi yazmaya hazırsınız.

Write an integration test


Her testten önce sunucu üzerinde bazı ayarlamaların
yapılması gerekmekte. Bunu test kurulum sınıfı yerine
farklı bir sınıfta gerçekleştirerek kurulum sınıfını daha
temiz bırakabiliriz. TestFixture adında yeni bir sınıf
oluşturun

AspNetCoreTodo.IntegrationTests/TestFixture.cs

using System;
using System.Collections.Generic;
using System.IO;
using System.Net.Http;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.TestHost;
using Microsoft.Extensions.Configuration;

141
Integration Testi

namespace AspNetCoreTodo.IntegrationTests
{
public class TestFixture : IDisposable
{
private readonly TestServer _server;

public TestFixture()
{
var builder = new WebHostBuilder()
.UseStartup<AspNetCoreTodo.Startup>(
)
.ConfigureAppConfiguration((context,
configBuilder) =>
{
configBuilder.SetBasePath(Path.C
ombine(
Directory.GetCurrentDirector
y(), "..\\..\\..\\..\\AspNetCoreTodo"));

configBuilder.AddJsonFile("appse
ttings.json");

// Add fake configuration for Fa


cebook middleware (to avoid startup errors)
configBuilder.AddInMemoryCollect
ion(new Dictionary<string, string>()
{
["Facebook:AppId"] = "fake-a
pp-id",
["Facebook:AppSecret"] = "fa
ke-app-secret"
});
});
_server = new TestServer(builder);

142
Integration Testi

Client = _server.CreateClient();
Client.BaseAddress = new Uri("http://loc
alhost:5000");
}

public HttpClient Client { get; }

public void Dispose()


{
Client.Dispose();
_server.Dispose();
}
}
}

Bu sınıf TestServer 'ın kurulmasını ve test yapılacak


sınıfın bu şekilde sunucu kurulum kodlarından ayrı
olmasını sağlar.

Güvenlik ve Kimlik bölümünde Facebook ile girişi


ayarladıysanız, Facebook app ID ve secret için
yukarıdaki kodda ConfigureAppConfiguration
bölümünde sahte veriler girmelisiniz. Aksi halde
Gizlilik Yönetici'de bu değerler olmadığından
sunucunuz başlamayacaktır.

Artık entegrasyon testi yazmaya hazırsınız.


TodoRouteShould adında yeni bir sınıf oluşturun

AspNetCoreTodo.IntegrationTests/TodoRouteShould.cs

143
Integration Testi

using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using Xunit;

namespace AspNetCoreTodo.IntegrationTests
{
public class TodoRouteShould : IClassFixture<Tes
tFixture>
{
private readonly HttpClient _client;

public TodoRouteShould(TestFixture fixture)


{
_client = fixture.Client;
}

[Fact]
public async Task ChallengeAnonymousUser()
{
// Ayarla
var request = new HttpRequestMessage(Htt
pMethod.Get, "/todo");

// Cagir
var response = await _client.SendAsync(r
equest);

// kontrol et
Assert.Equal(HttpStatusCode.Redirect, re
sponse.StatusCode);
Assert.Equal("http://localhost:5000/Acco
unt/Login?ReturnUrl=%2Ftodo",
response.Headers.Location.To

144
Integration Testi

String());
}
}
}

Bu test kullanıcı girişi yapmamış birisinin /todo yoluna


talep gönderdiğinde geri gelen cevabı kontrol eder. Bu
durumda giriş ekranına yönlendirmesi gerekmekte.

Bu senaryo entegrasyon testi için iyi bir örnektir.


Yönlendirmeleri göz önüne alır, kontrölörlerde bulunan
[Authorize] sınıflarını kontrol eder. Böylece birisi

[Authorize] özelliğini kontrolörden silerse hata döner.

Testi terminalde, bir önceki bölümde olduğu gibi, dotnet


test komutu ile çalıştırın

Starting test execution, please wait...


[xUnit.net 00:00:00.7237031] Discovering: AspNetCo
reTodo.IntegrationTests
[xUnit.net 00:00:00.8118035] Discovered: AspNetCo
reTodo.IntegrationTests
[xUnit.net 00:00:00.8779059] Starting: AspNetCo
reTodo.IntegrationTests
[xUnit.net 00:00:01.5828576] Finished: AspNetCo
reTodo.IntegrationTests

Total tests: 1. Passed: 1. Failed: 0. Skipped: 0.


Test Run Successful.
Test execution time: 2.0588 Seconds

145
Integration Testi

Özet
Test çok geniş bir alan ve öğrenilecek çok şey var.
Örneğin bu bölümde Ön-yüz tesine hiç girmedik, bu
konuyu tek başına kitap olacak kadar geniştir. Fakat testin
nasıl yapılması gerektiğine dair bir fikriniz var. Bunun için
kendinizi geliştirebilir ve daha fazla örnek yapabilirsiniz.

Her zamanki gibi, ASP.NET Core dökümanı en iyi


yardımcınız olacaktır. Bunun yanında StackOverflow bir
sorun ile karşılaştığınızda başvurabileceğiniz iyi bir
kaynaktır.

146
Uygulamanın Dağıtılması

Uygulamanın Dağıtılması
Uzun bir yol geldiniz, fakat henüz tam anlamıyla bitti
diyemeyiz. Artık bu harika uygulamanızı yaptığınıza göre
dünya ile paylaşma zamanı geldi demektir.

ASP.NET uygulamaları Windows, Mac veya Linux


ortamlarında çalışabildiğinden dolayı bir çok farklı yöntem
ile uygulamanızı dağıtabilirsiniz. Bu bölümde en fazla
kullanılan yöntemleri göreceksiniz.

Dağıtma Yöntemleri
ASP.NET Core uygulamaları genel olarak aşağıdaki
ortamlardan birisine kurulur.

Herhangi bir Docker Hostuna Docker koşabilecek


herhangi bir makineye ASP.NET uygulaması
dağıtılabilir. Docker ile daha önceden çalıştıysanız
çok hızlı bir şekilde imaj oluşturup uygulamanızı
dağıtabilirsiniz.

Azure Microsoft Azure ASP.NET Core uygulamaları


için doğrudan destek verir. Eğer Azure servisine
kayıtlıysanız, yeni bir web uygulaması oluşturup

147
Uygulamanın Dağıtılması

dosyalarınızı kopyalarsanız projenizi doğrudan


çalıştırabilirsiniz. Bunun nasıl yapılacağını ileride
Azure CLI üzerinden göreceksiniz.

Linux ( Nginx ile) Eğer Docker ile çalışmak


istemiyorsanız, uygulamnızı yine de herhangi bir
Linux Sunucusu üzerinden koşabilirsiniz. Bunlar
Amazon Ec2 veya DigitalOcean sanal makineleri
olabilir. Genelde ASP.NET Core ve Nginx reverse
proxy yapılarak kullanılırlar. Aşağıda daha geniş bir
şekilde göreceksiniz.

Windows Windows IIS web sunucusunu kullanarak


ASP.NET Core uygulaması çalıştırabilirsiniz. Genelde
Azure'a kurmak daha kolay ve ucuz yöntem olsa da
Windows Sunucularını kendiniz kontrol etmek
istiyorsanız bu yöntemi kullanabilirsiniz.

Kestrel ve reverse proxy.


Eğer ASP.NET Core uygulamasını internet ortamına
taşımayı istemiyorsanız bundan sonraki bölümleri
atlayabilirsiniz.

ASP.NET Core Kestrel adında hızlı ve oldukça hafif bir


geliştirme sunucusu kullanır. Projede aslında şimdiye
kadar hep bu sunucuyu kullandınız. Uygulamayı

148
Uygulamanın Dağıtılması

kullanılacak(production - live - canlı ) ortama taşıdığınızda


yine Kestrel kullanarak çalışabilir. Fakat önerilen yöntem;
reverse proxy yaparak Kestrel'in önüne daha yetkin ( load
blancing özelliği olan ) bir sunucu kurmaktır.

Linuxta ve Docker Konteynırında, Nginx veya Apache


web server kurarak gelen taleplerin Kestrel'e iletilmesini
sağlayabilirsiniz. Windows sunucuda IIS aslında aynı işi
yapmaktadır.

Eğer uygulamanızı Azure üzerinde tutuyorsanız, bunların


hepsi otomatik olarak sizin yerinize yapılmaktadır. Nginx
ile reverse proxy yapma olayını Docker ile Dağıtma
bölümünde göreceksiniz.

149
Azure'a Dağıtma

Azure'a Dağıtma
ASP.NET uygulamasının Azure'a dağıtılması sadece bir
kaç adımdan oluşur. Bu işlem Azure web ortamından
yapılacağı gibi, komut satırı üzerinden de yapılabilir. Bunu
daha sonra göreceksiniz.

Ne yapmalıyız
Komut satırından ( git --version kullanarak git'in
kurulu olup olmadığını kontrol ediniz.)

Azure CLI'ı kurun (https://github.com/Azure/azure-cli)

Azure bedava paketine kayıt olun.


Dağıtma için projenin ana klasöründeki dosyayı
değiştirin.

Yeni bir dağıtım yapılandırma dosyası


oluşturun.
Bizim şu anda 3 tane farklı projemiz olduğundan Azure
bunlardan hangisinin dünyaya gösterileceğini
bilmemektedir. Bunu düzelmek için bu klasörlerin
bulunduğu klasöre .deployment adında bir dosya oluştun.
.deployment

150
Azure'a Dağıtma

[config]
project = AspNetCoreTodo/AspNetCoreTodo.csproj

.deployment olarak adlandırıldığına emin olur. ( Windows

işletim sisteminde dosyayı ".deployment" şeklinde


kaydederek sağlayabilirsiniz.)

Eğer komut satırından ls veya dir ile bu klasöre


giderseniz şunları göreceksiniz:

.deployment
AspNetCoreTodo
AspNetCoreTodo.IntegrationTests
AspNetCoreTodo.UnitTests

Azure kaynaklarının ayarlanması


Eğer Azure CLI'ı ilk defa kurduysanız aşağıdaki komutu
çalıştırın

az login

Adrından kullanıcı adı ve şifrenizi yazıp giriş


yapın.Sonrasında uygulamamız için yeni bir Kaynak
Grubu oluşturun

az group create -l westus -n AspNetCoreTodoGroup

151
Azure'a Dağıtma

Sonrasında yeni bir App Service Plan (Uygulama Servis


Planı) oluşturmanız gerekmekte

az appservice plan create -g AspNetCoreTodoGroup -n


AspNetCoreTodoPlan --sku F1

F1 bedava servis planı, eğer kendi domaininizde

kullanmak istiyorsanız bunun yerine aylık 10$


vererek D1 planını seçebilirsiniz.

Şimdi Uygulama Servis Planı içerisine web uygulamanızı


oluşturun

az webapp create -g AspNetCoreTodoGroup -p AspNetCor


eTodoPlan -n MyTodoApp

Uygulamanın ismi ( MyTodoApp ) Azure içerisinde eşsiz


olmalı. Uygulama çalıştığında varsayılan URL bu
uygulama ismi olacaktır. Örneğin :
http://mytodoapp.azurewebsites.net

Uygulama ayarlarını güncelleme


Bu sadece eğer Güvenlik ve Kimlik bölümünde
Facebook ile giriş işlemini yaptıysanız gerekmektedir.

152
Azure'a Dağıtma

Uygulamanız Facebook:AppId ve Facebook:AppSecret


ayarları olmadan çalışamayacaktır. Bunları Azure web
portalını kullanarak eklemeniz gerekmektedir.

1. Azure hesabı ile https://portal.azure.com sitesine


girin.
2. Oluşturduğunuz web uygulamasını açın : MyTodoApp
3. Application Settings (Uygulama ayarları) tabına
tıklayın.
4. App settings bölümün altına Facebook:AppId ve
Facebook:AppSecret değerlerini facebooktan aldığınız

değerlere göre değiştirin.


5. Üstte bulunan Save butonuna tıklayarak değişiklikleri
kaydedin.

Proje dosyalarının Azure'a


dağıtılması
Bunun için Git kullanabilirsiniz. Hala local klasöreleri Gir
ile takip edilmiyorsa aşağıdaki komutları yazın. Bu
komutları tüm projeleri içeren klasöre içinde yapabilirsiniz.

git init
git add .
git commit -m "First commit!"

Sonrasında Azure kullanıcı adı ve şifresi oluşturun.

153
Azure'a Dağıtma

az webapp deployment user set --user-name nate

Buradaki komutları inceleyerek config-local-git ile bir


github linki çıktısı almanız lazım.

az webapp deployment source config-local-git -g AspN


etCoreTodoGroup -n MyTodoApp --out tsv

https://nate@mytodoapp.scm.azurewebsites.net/MyTodoA
pp.git

Bu URL'i kopyalayın ve sonra tekrar projenin ana


klasöründe bulunan git reposuna aşağıdaki gibi
remote(uzak) olarak gösterin

git remote add azure <yapıştır>

Bu adımları sadece bir defa yapmanız lazım. Eğer ileride


dosyalarınızı tekrar Azure'a göndermek isterseniz.
Commit ettikten sonra tek yapmanız gereken

git push azure master

Önünüze bir çok log mesaji dökülecek ve en sonunda


Azure'a dağıtıldığını göreceksiniz. Uygulama adresinize (
http://uygulamaismi.azuerwebsites.net) eriştiğinizde

154
Azure'a Dağıtma

projenizin çalıştığını göreceksiniz.

155
Docker ile Dağıtma

Docker ile Dağıtma


Docker gibi konteyner teknolojileri web uygulamalarının
dağıtımını oldukça kolaylaştırmıştır. Sunucu ayarlama,
gerekli eklentilerin yüklenmesi vs. dosyların
kopyalanması, sunucunun her defasında yeniden
başlatılması gibi işlemler tek bir seferde Docker imaji
alınarak ortadan kaldırılabilir. Yapmanız gereken imajı
Docker çalıştırabilecek bir sunucuya yerleştirmek.

Projenizi ölçeklendirme açısından da Docker oldukça


yararlıdır. Eğer imaj oluşturduysanız 1 konteyner ile 100
konteyner oluşturma sırasında aynı işlemi yaparsınız.

Başlamadan önce Docker CLI'ın bilgisayarına kurulu


olması gerekmekte. Bunun için internetten işletim
sisteminize göre Docker'ı indirip kurun, resmi web sitesi
en güvenilir kaynaktır.

docker --version

156
Docker ile Dağıtma

Eğer Güvenlik ve Kimlik bölümünde Facebook ile


giriş bölümünü kodladıysanız Docker'a Facebook ID
ve appSecret'i bildirmeniz gerekmekte. Docker
ayarlarının bu değerler için nasıl yapılacağı bu
kitabın konusu değildir. Eğer istiyorsanız
ConfigureServices içerisindeki AddFacebook

bölümünü komut satırı yaparak Facebook ile girişi


şimdilik iptal edebilirsiniz.

Docker dosyası oluşturma


İlk yapmanız gereken Docker dosyası oluşturma, Docker
dosyası uygulanızın nelere ihtiyacı olduğunu belirtmenizi
sağlar.

Eklentisiz bir şekilde Dockerfile adında bir dosya


oluşturun. Bunu uygulama içerisinde yapmanız lazım yani
Program.cs nin olduğu yerde.

bu dosyayı açın ve aşağıdaki satırı ekleyin

FROM microsoft/dotnet:latest

Bu Docker'a başlangıç imajı olarak Microsoft'un


yayınladığı imajı al demek. Bu ASP.NET Core uygulaması
için gerekli herşeyin alınmasını sağlar.

157
Docker ile Dağıtma

COPY . /app

COPY komutu bilgisayarınızda bulunan klasörün proje

klasörünüzün /app adında bir klasörün içine


kopyalanmasını sağlar.

WORKDIR /app

WORKDIR Windows komut satırında bulunan cd 'ye denk

gelir. Yani bundan sonraki yazacağımız komutlar /app


klasörü içerisinde çalışacaktır.

RUN ["dotnet", "restore"]


RUN ["dotnet", "build"]

Bu komutlar dotnet restore ( Nuget paketlerinin


indirilmesine yarar ) ve dotnet build ( uygulamayı derler

EXPOSE 5000/tcp

Normalde Docker konteynırı hiç bir porttan dışarıya veri


alıp vermeye izin vermez bunun için EXPOSE ile hangi
portu açması gerektiğini söylediniz. Kestrel sunucunun
varsayılan portu 5000 olduğundan 5000 kullandınız.

158
Docker ile Dağıtma

ENV ASPNETCORE_URLS http://*:5000

ENV komutu çevre değişkenlerini tanımlar. Uygulamanın

ASPNETCORE_URLS ile hangi porttan çalışacağını

ayarlamanız lazım

ENTRYPOINT ["dotnet", "run"]

Son olarak uygulamanın dotnet run komutu ile


çalışması için yukarıdaki satırı eklediniz.

Bu işlemler sonunda docker dosyası aşağıdaki gibi


görünecektir.

Dockerfile

FROM microsoft/dotnet:latest
COPY . /app
WORKDIR /app
RUN ["dotnet", "restore"]
RUN ["dotnet", "build"]
EXPOSE 5000/tcp
ENV ASPNETCORE_URLS http://*:5000
ENTRYPOINT ["dotnet", "run"]

İmaj Oluşturma

159
Docker ile Dağıtma

Dockerfile 'i kaydettiğinize emin olun. Sonra docker

build ile imajı şu şekilde oluşturabilirsiniz :

docker build -t aspnetcoretodo .

En sondaki . 'yı unutmayın bu dockerfile 'in aynı


klasörde olduğunu belirtiyor.

İmaj oluştuktan sonra docker images ile tüm oluşturulmuş


imajları görebilirsiniz. Artık bu imajı test edebilirsiniz.
Bunun için aşağıdaki satırı çalıştırın:

docker run -it -p 5000:5000 aspnetcoretodo

-it bayrağı Docker'a konteynerın interaktif modda

çalıştırılacağını söyler.

Nginx'in kurulması
Bu bölümün başında reverse proxy ile Nginx veya apache
kullanarak Kerstel'e gelen talepleri reverse proxy
yapmamız gerektiğini söylemiştim.

Genel mimarimiz iki tane konteynerdan oluşacak: Birincisi


80 portinu dinleyen ve 5000 portuna yönlendiren Nginx ve
5000 portundan bu taleplere cevap veren Kerstel.

160
Docker ile Dağıtma

Nginx kendi Dockerfile'ına ihtiyaç duyar. Daha önce


yazdığınız Dockerfile ile karışmasın diye web
uygulamasının ana dizininde bir tane klasör oluşturun.

mkdir nginx

içerisine yeni bir Dockerfile oluşturup şu satırları


ekleyin.

nginx/Dockerfile

FROM nginx
COPY nginx.conf /etc/nginx/nginx.conf

Sonra bu klasör içinde nginx.conf dosyasını oluşturun


ve aşağıdaki satırları yapıştırın

nginx/nginx.conf

161
Docker ile Dağıtma

events { worker_connections 1024; }

http {

server {
listen 80;

location / {
proxy_pass http://kestrel:5000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgra
de;
proxy_set_header Connection 'keep-al
ive';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
}
}

Bu ayar dosyası Nginx'e gelen taleplerin


http://kestel:5000 adresine yönlendirilmesi gerektiğini

söyler. (Neden kerstel:5000` kullandığımızı az sonra

göreceksiniz.)

Docker düzenleyici oluşturma


Web uygulamasının ana klasörüne docker-compose.yml
adında bir dosya oluşturun.
docker-compose.yml

162
Docker ile Dağıtma

nginx:
build: ./nginx
links:
- kestrel:kestrel
ports:
- "80:80"
kestrel:
build: .
ports:
- "5000"

Docker Compose birden fazla containerin birlikte


oluşturulmasını ve çalışmasını sağlar. Yukarıda 2 tane
konteyner tanımlanır. Bunlar ./nginx/Dockerfile 'da
bulunan nginx ve ./Dockerfile da bulunan kestrel .
Doğrudan ikisiyle bağlantılı olarak çalıştırıldığından
iletişimleri sağlanmış olur.

Birden fazla konteyner içeren uygulamalar aşağıdaki gibi


çalıştırılır

docker-compose up

Tarayıcıyı açıp http://localhost 'a gidin(dikkat ederseniz


5000 portunu yazmadık). Nginx 80 portunu dinlediğinden
doğrudan bu telebi proxy ile ASP.NET Core uygulamasına
yönlendirir.

163
Docker ile Dağıtma

Docker sunucusu kurma


Docker sunucusu kurma adımları bu kitabın kapsamı
içeriisnde değildir. Fakat modern bir linux sürümüne
Docker servisi kurulabilir. Örneğin Amazon EC2 sanal
makinesine Docker kurmal mümkündür. Bunun için
internete "amazon ec2 set up docker" diye aratırsanız
birçok yönerge bulabilirsiniz.

Dijital Ocean dökümanları ve sistemi EC2'ye göre daha


kolay olduğundan öncelikle DigitalOcean tercih edilebilir.

164
Sonuç

Sonuç
Öncelikle ASP.NET Core el kitabını bitirdiğiniz için
teşekkürler. Üzerinden geçilmesi gereken ve bu kitaba
sığmayacak daha çok başlık var. Bunlardan bazıları şu
şekilde;

RESTful API'ler ve microservisler.


ASP.NET Core'u teksayfa uygulamalarında kullanma.
( Angular ve React gibi)
Razor Şablon Dili
Statik dosyaları birleştirme ve minify etme.
Web Socket

Kitabın ingilizce yazarını buradan takip edebilirsiniz :


https://www.recaffeinate.co

Beni ise : http://www.sahinyanlik.com.tr adresinden takip


edebilir. Açık kaynak kodlu projelerime
http://github.com/sahinyanlik adresinden ulaşabilirsiniz.

Bu projeyi daha da geliştirmek istiyorsanız lütfen benimle


iletişime geçin. Pull request gönderirseniz seve seve
üzerinde çalışmak isterim.

Bu kitabı Türkçeleştirme amacım C# ve ASP.NET Core'a


yeni başlayan arkadaşlara yardımcı olmaktır.

165
Sonuç

Lisans uyarınca bu kitabı indirebilir, üzerinde değişiklik


yapabilir ve yeniden başkalarına bedava dağıtabilirsiniz.
Fakat kitaptan gelir elde edemezsiniz. Üzerinde değişiklik
yapacağınız kitabın lisansı yine Creative Commons
Attribution 4.0 International Public License olmak
zorundadır. Daha fazla bilgi için lütfen lisans'ı kontorl
ediniz.

Resmi ASP.NET Core dölümantasyonu bu konuları ve


daha bir çok konuyu kapsamakta. Siteye
https://docs.asp.net üzerinden erişebilirsiniz.

Yazar Hakkında
Merhaba, Ben Nate. Bu kitabı bir haftasonunda .NET
topluluğuna yardımcı olmak için yazdım. Umarım birşeyler
öğrenebilmişsinizdir.

Benimle iletişim için Twitter (@nbarbettini) veya blog


adresim (https://www.recaffeinate.co) üzerinden
ulaşabilirsiniz.

Çevirmen Hakkında

166
Sonuç

Merhaba ben Şahin, kitabı yazış amacım Türkçe dilinde


programlama dilleri ile alakalı pek bedava kitap
olmamasından dolayıdır. Umarım size bu kitap size
yardımcı olmuştur. Bana twitter ( @sahinyanlik )
üzerinden ulaşabilirsiniz.

Teşekkür
Bana İngilizceyi öğrenmemde ve bunu kullanabileceğim
yerlere gelmemde yardımı olan öğretmenlerime
teşekkürlerimi sunarım.

167

You might also like