18 |
19 |
38 | ```
39 |
40 | - Ajax ile gelen veri birden çok yapıda olabilir ve kullanımı da gelen bu yapıya göre değişir.
41 | - String döndüren ajax
42 | - Html döndüren ajax
43 | - Model döndüren ajax
44 | - Model Listesi döndüren ajax
45 | - PartialView döndüren ajax
46 | - $.each metodu
47 |
48 | ```js
49 | $.each(data, function (index, value) {
50 | liste.append("
" + value.Isim"
");
51 | });
52 | ```
53 |
54 | - Kaynak :
55 | - http://api.jquery.com/jQuery.ajax/
56 |
57 |
58 | #### $.load() fonksiyonu
59 |
60 | - load() metodu ile veri gönderirsek “post” olarak otomatik gönderir. Veri göndermediğimiz ve sadece veri çektiğimiz durumlarda, “get” gönderimi yapar.
61 |
62 | ```js
63 | yazi.load("/Home/ajax1", { data: input.val() },
64 | function (responseTxt, statusTxt, xhr) {
65 | if (statusTxt == "success")
66 | alert("External content loaded successfully!");
67 | if (statusTxt == "error")
68 | alert("Error: " + xhr.status + ": " + xhr.statusText);
69 | });
70 | ```
71 |
72 | - Kaynaklar :
73 | - https://www.w3schools.com/jquery/ajax_load.asp
74 | - http://api.jquery.com/load/
75 |
76 | #### $.get ve $.post işlemleri
77 |
78 | ```js
79 | // Kullanımı
80 | $.get(URL,callback);
81 | $.post(URL,data,callback);
82 |
83 | // Örnek
84 | $.post("/Home/ajax2", function (data, status) {
85 | $.each(data, function (index, value) {
86 | liste.append("
" + value.Isim + "
");
87 | });
88 | }).done(function () {
89 | alert("Ajax method başarıyla çalıştı!");
90 | }).fail(function () {
91 | alert("Bir hata oluştu!");
92 | }).always(function () {
93 | alert("Her zaman gösterilecek!")
94 | });
95 | ```
96 |
97 | - Deferred Object Yapıları
98 | - .done(), .fail() ve .always()
99 | - Kaynaklar :
100 | - https://www.w3schools.com/jquery/jquery_ajax_get_post.asp
101 | - https://api.jquery.com/category/deferred-object/
102 |
--------------------------------------------------------------------------------
/OnionArchitectureProjectExample/OnionArchitectureProjectExample.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 15
4 | VisualStudioVersion = 15.0.27428.2005
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Project.Data", "Project.Data\Project.Data.csproj", "{3E20360B-BBB9-4A75-9BE6-AC8AB8F73C23}"
7 | EndProject
8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Project.Core", "Project.Core\Project.Core.csproj", "{10891C0E-CAA3-41E1-885D-3AB92E10C34A}"
9 | EndProject
10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Project.Services", "Project.Services\Project.Services.csproj", "{C511B695-2BE4-4FCD-8209-621DC77C3AE6}"
11 | EndProject
12 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Project.Entities", "Project.Entities\Project.Entities.csproj", "{E965BA69-7F2C-468A-976E-46640F87F44A}"
13 | EndProject
14 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Project.WebUI", "Project.WebUI\Project.WebUI.csproj", "{C5EB061D-426B-4B1E-8E65-A6336C46B8A9}"
15 | EndProject
16 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Project.Logger", "Project.Logger\Project.Logger.csproj", "{24F8BDDA-8FCE-46A9-AD04-AE2CF22A1CBC}"
17 | EndProject
18 | Global
19 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
20 | Debug|Any CPU = Debug|Any CPU
21 | Release|Any CPU = Release|Any CPU
22 | EndGlobalSection
23 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
24 | {3E20360B-BBB9-4A75-9BE6-AC8AB8F73C23}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
25 | {3E20360B-BBB9-4A75-9BE6-AC8AB8F73C23}.Debug|Any CPU.Build.0 = Debug|Any CPU
26 | {3E20360B-BBB9-4A75-9BE6-AC8AB8F73C23}.Release|Any CPU.ActiveCfg = Release|Any CPU
27 | {3E20360B-BBB9-4A75-9BE6-AC8AB8F73C23}.Release|Any CPU.Build.0 = Release|Any CPU
28 | {10891C0E-CAA3-41E1-885D-3AB92E10C34A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
29 | {10891C0E-CAA3-41E1-885D-3AB92E10C34A}.Debug|Any CPU.Build.0 = Debug|Any CPU
30 | {10891C0E-CAA3-41E1-885D-3AB92E10C34A}.Release|Any CPU.ActiveCfg = Release|Any CPU
31 | {10891C0E-CAA3-41E1-885D-3AB92E10C34A}.Release|Any CPU.Build.0 = Release|Any CPU
32 | {C511B695-2BE4-4FCD-8209-621DC77C3AE6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
33 | {C511B695-2BE4-4FCD-8209-621DC77C3AE6}.Debug|Any CPU.Build.0 = Debug|Any CPU
34 | {C511B695-2BE4-4FCD-8209-621DC77C3AE6}.Release|Any CPU.ActiveCfg = Release|Any CPU
35 | {C511B695-2BE4-4FCD-8209-621DC77C3AE6}.Release|Any CPU.Build.0 = Release|Any CPU
36 | {E965BA69-7F2C-468A-976E-46640F87F44A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
37 | {E965BA69-7F2C-468A-976E-46640F87F44A}.Debug|Any CPU.Build.0 = Debug|Any CPU
38 | {E965BA69-7F2C-468A-976E-46640F87F44A}.Release|Any CPU.ActiveCfg = Release|Any CPU
39 | {E965BA69-7F2C-468A-976E-46640F87F44A}.Release|Any CPU.Build.0 = Release|Any CPU
40 | {C5EB061D-426B-4B1E-8E65-A6336C46B8A9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
41 | {C5EB061D-426B-4B1E-8E65-A6336C46B8A9}.Debug|Any CPU.Build.0 = Debug|Any CPU
42 | {C5EB061D-426B-4B1E-8E65-A6336C46B8A9}.Release|Any CPU.ActiveCfg = Release|Any CPU
43 | {C5EB061D-426B-4B1E-8E65-A6336C46B8A9}.Release|Any CPU.Build.0 = Release|Any CPU
44 | {24F8BDDA-8FCE-46A9-AD04-AE2CF22A1CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
45 | {24F8BDDA-8FCE-46A9-AD04-AE2CF22A1CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU
46 | {24F8BDDA-8FCE-46A9-AD04-AE2CF22A1CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU
47 | {24F8BDDA-8FCE-46A9-AD04-AE2CF22A1CBC}.Release|Any CPU.Build.0 = Release|Any CPU
48 | EndGlobalSection
49 | GlobalSection(SolutionProperties) = preSolution
50 | HideSolutionNode = FALSE
51 | EndGlobalSection
52 | GlobalSection(ExtensibilityGlobals) = postSolution
53 | SolutionGuid = {381BE8F1-D376-4664-9019-F51B6ED03893}
54 | EndGlobalSection
55 | EndGlobal
56 |
--------------------------------------------------------------------------------
/RoadMap.txt:
--------------------------------------------------------------------------------
1 | - ASP.NET Core Application Lifecycle
2 | - https://docs.microsoft.com/en-us/aspnet/core/fundamentals/?view=aspnetcore-2.1&tabs=aspnetcore2x
3 | - .NET Core çalışma alt yapısı
4 | - MIL - JIT Compiler vs.
5 |
6 | - External class içinde database bağlantısı nasıl yapılır?
7 | - Burada yapılacak şeylerden biri, context kısmını direk olarak dışarıdan almak.
8 | - Kullanılacağı yerde db bağlantısı yapılıp buraya verilebilir.
9 |
10 | - Startup.cs içindeki metotların ayrıntılı anlamları
11 | - Giriş kısmındaki kullanılabilir paket yöneticileri notu tamamlanacak.
12 | - asp.net kısmına ado.net ile ilgili notlar eklenecek.
13 |
14 | - Settings dosyalarının yapılandırılması
15 | - https://www.c-sharpcorner.com/article/all-about-appsettings-json-in-asp-net-core-2-0/
16 | - DB bağlantı stringinin appsettings içinden çekilmesi ve notlara eklenmesi
17 |
18 | - BundleConfig ayarlamalarının yapılması (db önceki notlar)
19 |
20 | - Program başlangıcında çalışacak metotlar oluşturma.
21 |
22 | - IApplicationBuilder, IHostingEnvironment, IServiceCollection metotlarının ayrıtıları.
23 | - WebHostBuilder ayarlamaları.
24 | - https://wildermuth.com/2017/07/06/Program-cs-in-ASP-NET-Core-2-0
25 |
26 | - Root dizini alma yöntemleri. wwwroot dizininin nasıl alınacağı? Bu dizinin publish edildiğinde root files olarak bulunup bulunmadığı
27 |
28 | - Formlar ile ilgili bir not oluşturulacak.
29 | - Form elemanları
30 | - Form elemanlarının ayrı ayrı olarak kullnaılması ile ilgili CEO çıkarımları
31 | - Bunla ilgili bir medium yazısı vardı.
32 | - Formlar üzerinden dosya gönderim örnekleri
33 | - Tag helperlar dahil edilebilir.
34 | - Formların post ve get ile gönderimleri
35 | - Form bilgilerinin arka planda çekilmesi
36 | - Bunlardan model binding ve normal yöntem arasıdaki fark.
37 |
38 | - DI Liftime
39 | - https://docs.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection#service-lifetimes-and-registration-options
40 |
41 | - RoadMap dosyası içinde konu listesi yazılacak.
42 | - Aralara projeler ve front end konuları yereştirilecek
43 | - Örnek proje planlaması yapılacak, projeler yapılıp yüklenebilir.
44 |
45 | - Projelere, ASP dışında ek konular eklenecek.
46 | - Github
47 | - PDF belgesi
48 | - TFS
49 | - Temiz kod yazma
50 | - https://hackernoon.com/tips-from-the-book-every-programmer-should-read-425fb77873f8
51 | - Youtube videosu
52 |
53 | - Scaffolding nedir?
54 |
55 | - IDesignTimeDbContext sınıfı araştırılacak.
56 |
57 | - Idendity Core ile ilgili not hazırlanacak
58 | - 155. Setting up Idendity Core notuna bir daha bakılacak.
59 | - Cookie Authentication In ASP.net Core 2.0
60 |
61 | - Metadata olarak UIHint araştırılacak.
62 |
63 | - dotnet CLI commands
64 | - https://docs.microsoft.com/en-us/dotnet/core/tools/?tabs=netcore2x
65 |
66 | - App Secure kısmına bakılacak
67 | - https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/linux-nginx?view=aspnetcore-2.0&tabs=aspnetcore2x#securing-the-app
68 |
69 | - Core ile Docker kullanımı
70 | - Vagrant üzerinde deployment
71 | - Azure ve Amazon üzerinde deployment
72 | - https://www.vagrantup.com/
73 | - https://github.com/sprosin/vagrant-dotnet-core
74 |
75 | - microsoftun kendi kütüphanesine bakılacak referans olarak :
76 | - https://docs.microsoft.com/en-us/aspnet/core/index
77 | - https://docs.microsoft.com/en-us/ef/#pivot=efcore&panel=efcore-all
78 | - https://www.tektutorialshub.com/asp-net-core-tutorial/
79 |
80 | // GENEL
81 | - Design patterns ( % Strategy Pattern )
82 | - Class yapısındaki base yapısı
83 | - Derinlemesine C# ile ilgil araştırma yapılacak.
84 | - İsimlendirme kuralları
85 | - http://wiki.c2.com/?CapitalizationRules
86 | - Generic Ifadeler (T ve T ile kullanılan where kavramları)
--------------------------------------------------------------------------------
/15 - Area Kavramı.md:
--------------------------------------------------------------------------------
1 | ## AREA KAVRAMI
2 |
3 | - Area'lar, farklı namespace'ler ile ayrılmış, kendine ait routing ayarlarına ve MVC yapısına sahip alanlardır.
4 | - Farklı amaçlar için farklı app gruplarına ihtiyacımız olduğu durumlarda kullanırız.
5 | - Örneğin, bir sitenin ayrı bir müşteri paneli, admin paneli vb varsa, bu yapılar ayrı ayrı arealar oluşturularak kullanılabilir.
6 | - Bir Core projesi içinde istenilen sayıda area açılabilir.
7 | - Büyük projelerin bağımsız parçalarını birbirinden ayırarak yönetilebilirliği kolaylaştırır.
8 | - Aynı isimle controller açılması, bu controller farklı arealar altında olduğu müddetçe sorun oluşturmaz.
9 | - Core, area içindeki bir view'ı render ederken, default olarak aşağıdaki yollara bakar :
10 |
11 | ```cs
12 | /Areas//Views//.cshtml
13 | /Areas//Views/Shared/.cshtml
14 | /Views/Shared/.cshtml
15 | ```
16 |
17 | - Bu default yolları değiştirmek mümkündür.
18 | - Bunun için `Microsoft.AspNetCore.Mvc.Razor.RazorViewEngineOptions` üzerinden `AreaViewLocationFormats` kısmı override edilebilir. (Servise yapısı içinde)
19 | - Buradaki numaralandırma :
20 | - `{0}` > Action Name
21 | - `{1}` > Controller Name
22 | - `{2}` > Area Name
23 |
24 | ```cs
25 | services.Configure(options =>
26 | {
27 | options.AreaViewLocationFormats.Clear();
28 | options.AreaViewLocationFormats.Add("/Categories/{2}/Views/{1}/{0}.cshtml");
29 | options.AreaViewLocationFormats.Add("/Categories/{2}/Views/Shared/{0}.cshtml");
30 | options.AreaViewLocationFormats.Add("/Views/Shared/{0}.cshtml");
31 | });
32 | ```
33 |
34 | ### 01 - Area Ayarlamalarının Yapılması
35 |
36 | - Area kullanımı için basit olarak iki adımlı bir ayarlama yapmak gerekmektedir.
37 | - Route ayarlaması
38 | - Controllers ayarlaması
39 |
40 | #### Route ayarlaması
41 |
42 | - Route ayarlaması için, yeni bir routeMap eklenmesi gerekmektedir.
43 | - Bu yeni ayarlamanın diğer alanlarla çakışmaması için onların üstünde olması daha sağlıklıdır.
44 |
45 | ```cs
46 | app.UseMvc(routes =>
47 | {
48 | routes.MapRoute(
49 | name: "areaRoute",
50 | template: "{area:exists}/{controller=Home}/{action=Index}/{id?}");
51 |
52 | routes.MapRoute(
53 | name: "default",
54 | template: "{controller}/{action}/{id?}",
55 | defaults: new { controller = "Home", action = "Index" });
56 | });
57 | ```
58 |
59 | #### Controllers ayarlaması
60 |
61 | - Controller kullanımında, her controller sınıfının üstüne `Area` attribute'ü ile, hangi areaya ait olduğu belirtilmelidir.
62 | - Eğer bu ayarlama yapılmazsa, root dizinden türetilmiş gibi algılanır ve çakışma yaratır.
63 |
64 | ```cs
65 | namespace MyStore.Areas.Products.Controllers
66 | {
67 | [Area("Products")]
68 | public class HomeController : Controller
69 | {
70 | // GET: /Products/Home/Index
71 | public IActionResult Index()
72 | {
73 | return View();
74 | }
75 |
76 | // GET: /Products/Home/Create
77 | public IActionResult Create()
78 | {
79 | return View();
80 | }
81 | }
82 | }
83 | ```
84 |
85 | ### 02 - Area Link Yaratma
86 |
87 | - Program içinde başka bir action'a link yaratırken kullanacağımız yöntemler şunlardır:
88 | - Örnek olarak gideceğimiz link : `/Products/Home/Index`
89 |
90 | - Aynı area ve controller, farklı action :
91 | - HtmlHelper:
92 | - `@Html.ActionLink("Go to Product's Home Page", "Index")`
93 | - TagHelper:
94 | - `Go to Product's Home Page`
95 |
96 | - Aynı area, farklı controller ve action :
97 | - HtmlHelper:
98 | - `@Html.ActionLink("Go to Product's Home Page", "Index", "Home")`
99 | - TagHelper:
100 | - `Go to Product's Home Page`
101 |
102 | - Farklı area, controller ve action :
103 | - HtmlHelper:
104 | - `@Html.ActionLink("Go to Product's Home Page", "Index", "Home", new {area = "Products"})`
105 | - TagHelper:
106 | - `Go to Product's Home Page`
--------------------------------------------------------------------------------
/03 - Razor View Engine.md:
--------------------------------------------------------------------------------
1 | ## RAZOR VIEW ENGINE
2 |
3 | ### 01 - Razor Syntax
4 | - View Engine nedir?
5 | - View’lerinizi HTML çıktısı olarak render etmek için kullanılan bir mekanizma/teknoloji’dir.
6 | - Razor View Engine nedir?
7 | - Razor blogu açma ve Razor içinde C# kodlarının kullanımı
8 | - Razor blogu içindeki değişkenlere Razor dışından ulaşım
9 | - Razor açıklama satırı
10 | - Parantez içinde iki değişkenin birleştirilmesi
11 | - View içinde Razor ile döngülerin ve karar yapılarının kullanımı
12 |
13 | #### Razor içinde html yapısı kullanırken oluşan hatalar
14 | - Razor yapılarından biri kullanıldığında, hemen içine normal yazı yazıldığında bunu C# dili olarak algılar. Eğer yazdığımız bu kısım C# değilse bize hata verecektir.
15 |
16 | ```cs
17 | @if (true)
18 | {
19 | Lorem Ipsum // Bu kısım hata verecektir!
20 | }
21 | ```
22 |
23 | - Bu hatayı vermemesini sağlamak için bir kaç yöntem vardır:
24 | - Bir html tagı kullanmak
25 | - `@:` Razor hata bastırma kodu kullanmak
26 | - `` taglarını kullanmak.
27 |
28 | ```html
29 | @if (true)
30 | {
31 | Lorem Ipsum
32 |
33 | @: Lorem Ipsum
34 |
35 |
36 | Lorem Ipsum
37 | Lorem Ipsum
38 |
39 | }
40 | ```
41 |
42 | ### 02 - HTML Helpers
43 | - Html Helper'lar adından da anlaşılacağı gibi, html kodlarını kısa yoldan, metotlarla oluşturmamız için bize yardımcı olur.
44 | - Yazdığımız bu metotlar daha sonrasında render edilirken, normal html kodlarına çevilir.
45 | - **NOT! :** Yazdığımız tüm html helper'ların son çıktısı html'dir.
46 | - **Bu nedenle helpers'lar ile yapılan her şey, normal html ile de yapılabilir!**
47 | - **NOT2! :** ASP .NET Core ile Html Helper yapıları kısmen bulunsa da artık bu yapılar kullanılmamaktadır. Bunların yerine şu yeni yapılar kullılmaktadır:
48 | - TagHelpers
49 | - ViewComponents
50 |
51 | #### General Helpers
52 | - Html.ActionLink()
53 | - Html.Partial()
54 |
55 | #### URL Helpers
56 | - Html.ActionLink()
57 | - Url.Action()
58 | - Url helperların otomatik link oluşturması ve bunun önemi
59 | - .NET Core içinde bunlar yerine tag helperlar kullanılıyor.
60 |
61 | #### System.Net.WebUtility Helpers
62 | - .HtmlEncode()
63 | - .HtmlDecode()
64 | - .UrlEncode()
65 | - .UrlDecode()
66 | - Html.Raw()
67 |
68 | #### Form Helpers
69 | - Form elemanlarını oluşturmak için kullandığımız helper metotları.
70 | - Her elemanın aynı zamanda bir de model ile kullanıma olanak tanıyan `-for` son ekli başka bir metodu da var.
71 | - Form helpers'ların yerini Core 2.0'da `tag helpers`'lar almıştır.
72 | - Form helperslar:
73 | - Html.BeginForm()
74 | - Html.TextBox() - Html.TextBoxFor()
75 | - Html.Label() - Html.LabelFor()
76 | - ...
77 | - Html.BeginForm metodu kullanıldığında .NET Core'da otomatik olarak bir AntiForgeryToken oluşturulur.
78 |
79 | ### 03 - Custom Html Helpers
80 | - `IHtmlHelper` sınıfı içerisine extension metotlar yazarak istediğimiz custom helper'ları ekleyebiliriz.
81 | - Bunun için yapılması gereken adımlar şunlardır:
82 | - Bir klasör içinde ( Library ) class dosyası ( extensions ) oluşturulur.
83 | - Bu kısım zorunlu değil, fakat dosyaların düzgün bir düzende olması için böyle yapılması daha uygun olur.
84 | - Model static yapılmak zorunda
85 | - Class içine statik bir fonksiyon oluşturulur.
86 | - Fonksiyonun return’ü -> `IHtmlContent`
87 | - Fonksiyonun kalıtımı -> `this IHtmlHelper helper`
88 | - String olarak html hazırlanır.
89 | - `return new HtmlString( )`
90 | - Kullanılacak sayfada using ile Library klasörüne link verilir.
91 |
92 | ```cs
93 | namespace Project.Library.Helpers
94 | {
95 | public static class CustomHelper
96 | {
97 | public static IHtmlContent Submit(this IHtmlHelper helper, string value)
98 | {
99 | string html = $"";
100 | return new HtmlString(html);
101 | }
102 | }
103 | }
104 | ```
105 |
106 | - - Html içinde kullanımı:
107 |
108 | ```html
109 |
110 | @using WebApplication1.Library.Helpers
111 | ...
112 |
113 |
116 | ```
--------------------------------------------------------------------------------
/09 - Database CRUD İşlemleri.md:
--------------------------------------------------------------------------------
1 | ## DATABASE CRUD İŞLEMLERİ
2 |
3 | ### Controller
4 |
5 | ```cs
6 | using Microsoft.AspNetCore.Mvc;
7 | using Project.Models.Entities;
8 | using Project.Models.Interfaces;
9 |
10 | namespace Project.Controllers
11 | {
12 | public class HomeController : Controller
13 | {
14 | private IPersonRepository context;
15 |
16 | public HomeController(IPersonRepository repo)
17 | => context = repo;
18 |
19 | public IActionResult Index()
20 | => View(context.Persons);
21 |
22 | public IActionResult Create()
23 | => View();
24 |
25 | [HttpPost]
26 | public IActionResult Create(Person person)
27 | {
28 | context.CreatePerson(person);
29 | return RedirectToAction("Index");
30 | }
31 |
32 | public IActionResult Edit(int id)
33 | => View(context.GetById(id));
34 |
35 | [HttpPost]
36 | public ActionResult Edit(Person person)
37 | {
38 | context.UpdatePerson(person);
39 | return RedirectToAction("Index");
40 | }
41 |
42 | public ActionResult Delete(int id)
43 | {
44 | context.DeletePerson(id);
45 | return RedirectToAction("Index");
46 | }
47 | }
48 | }
49 | ```
50 |
51 | ### Views
52 |
53 | #### Index.cshtml
54 |
55 | ```html
56 | @model IEnumerable
57 |
58 |
59 |
60 |
61 |
62 |
63 | Index
64 |
65 |
66 |
67 |
80 | }
81 |
82 |
83 | ```
84 |
85 | #### Create.cshtml
86 |
87 | ```html
88 | @model Person
89 |
90 |
91 |
92 |
93 |
94 |
95 | Create
96 |
97 |
98 |
99 |
112 |
113 |
114 |
115 | ```
116 |
117 | #### Edit.cshtml
118 |
119 | - Update işlemleri yapılırken, Crate html dosyasından farklı olarak, ID property'sinin hidden olarak alınacağı unutulmamalıdır.
120 | - Update yapılırken, update yapılacak db elemanı bu gelen ID'ye göre bulunur.
121 | - Eğer ID kısmını hidden olarak formdan göndermezsek, ID = 0 olarak gidecektir ve güncelleme yapılmayacaktır.
122 | - Update işleminde post edilen nesne içinde, sadece form içinde çağırılan elemanların gönderileceği unutulmamalıdır.
123 | - Örneğin `CreatedDate`, `IsActive` gibi property'ler varsa ve biz bunları form içinden göndermezsek, bu kısımların default değeri alınarak gönderilen nesne oluşturulur ve update yapıldığında eski değerler yerine bu default değerler gelir.
124 | - Bu durumda bizim db üzerinde istenmeyen değişiklikler yapılmasına neden olur.
125 | - Bu sorunu çözmek için iki yöntem vardır:
126 | 1. Form içinine bu parametreler hidden olarak gönderilir, böylece yeni nesne oluşurken, bu bilgiler eskisiyle aynı olarak gönderilecektir.
127 | 2. Update yapılırken `_context.Update(person);` fonksiyonunu kullanmak yerine, gelen ID'ye göre db elemanı çekilip sadece ilgili alanlar replace edilerek tekrar db'ye gönderilir.
128 |
129 | ```html
130 | @model Person
131 |
132 |
133 |
134 |
135 |
136 |
137 | Create
138 |
139 |
140 |
141 |
156 |
157 |
158 |
159 | ```
160 |
161 | #### SQL Sorgusu Çalıştırma
162 |
163 | - .NET üzerinde, LinQ ile yapılan işlemler sınırlı kaldığında, direk SQL komutları çalıştırılabilir.
164 | - Bunun için `.Database.ExecuteSqlCommand()` metodunu kullanıyoruz.
165 | - Metot, geri dönüş olarak `int` tipinde, üzerinde değişiklik yapılan `row` sayısını döndürür.
166 | - Hata olması durumunda `SqlException` fırlatır.
167 | - **NOT:** Bu yöntem kullanılırken `SQLInjection` açığı yaratmamasına dikkat edilmelidir!
168 |
169 | ```cs
170 | public int DeletePersonWithSQLCommand(int personID)
171 | {
172 | string cmd = $"DELETE FROM Persons WHERE ID={personID}";
173 | return efProjectContext.Database.ExecuteSqlCommand(cmd);
174 | }
175 | ```
--------------------------------------------------------------------------------
/06 - Controller ve View Arasındaki İletişim.md:
--------------------------------------------------------------------------------
1 | ## CONTROLER VE VİEW ARASI İLETİŞİM
2 |
3 | ### 1) Controller'dan View'e Veri Gönderme
4 | - 3 Yöntem vardır:
5 | - `ViewData`
6 | - Daha sonra daha kolay kullanım için `ViewBag` yapısına geçmiştir.
7 | - `ViewDataDictionary` yapısındadır.
8 | - `ViewBag`
9 | - Controller'dan bilgilerin View üzerine gönderilmesi için kullanılır.
10 | - Arka planda `ViewData` kullanır.
11 | - `Dynamic` yapıdadır.
12 | - Okunduğu zaman veri tipini kazanır.
13 | - Bu yapısı nedeniyle kullanıldığı zaman tür dönüşümü yapılması *zorunlu değildir.*
14 | - Süresi yoktur, sadece view'a kadar ilerleyebilir.
15 | - `TempData`
16 | - `ITempDataDictioanry` yapısındadır.
17 | - Bilgileri, okunana kadar saklar.
18 | - Okuma yapıldıktan sonra `silinecek` olarak işaretlenip, **request sonunda** silinir.
19 | - `Silinecek` olarak işaretlenmesini engellemek için `Peek()`, işaretlendikten sonra işaretini kaldırmak için `Keep()` metotları kullanılabilir.
20 | - Saklanması:
21 | - `CookieTempDataProvider` nesnesi yaratılır.
22 | - Bu nesne `Base64UrlTextEncoder` ile encode edilir.
23 | - Encode edilen nesne default olarak `Cookie`'de saklanır.
24 | - `Cookie-Based TempData Provider` default gelir, istenilirse bu yapı, `Session-Based TempData Provider` olarak değiştirilebilir.
25 |
26 | ```cs
27 | public void ConfigureServices(IServiceCollection services)
28 | {
29 | // Session-Based
30 | services.AddMvc().AddSessionStateTempDataProvider();
31 |
32 | // Cookie-Based (Default)
33 | // services.AddMvc().AddCookieTempDataProvider();
34 |
35 | services.AddSession();
36 | }
37 | public void Configure(IApplicationBuilder app, IHostingEnvironment env)
38 | {
39 | app.UseSession();
40 | app.UseMvcWithDefaultRoute();
41 | }
42 |
43 | ```cs
44 | //second request, PEEK value so it is not deleted at the end of the request
45 | object value = TempData.Peek("value");
46 |
47 | //third request, read value and mark it for deletion
48 | object value = TempData["value"];
49 | ```
50 |
51 | ```cs
52 | //second request, get value marking it from deletion
53 | object value = TempData["value"];
54 | //later on decide to keep it
55 | TempData.Keep("value");
56 |
57 | //third request, read value and mark it for deletion
58 | object value = TempData["value"];
59 | ```
60 |
61 | ### 2) View’dan Controller’a Veri Gönderme
62 | - View'dan Controller'a veri gönderim 3 şekilde olur (Request):
63 | - 1. Route Values : `/Course/Details/2`
64 | - 2. Query String : `/Course/Details?ID=2`
65 | - 3. Form Data : `ID=2`
66 | - GET ve POST Yöntemleri
67 | - GET Metodu:
68 | - Raw request içinde url içinde taşınır.
69 | - Query String yapısındadır.
70 | - POST Metodu:
71 | - Raw request içinde body kısmında taşınır.
72 | - Form Data yapısındadır.
73 | - GET ve POST ile gönderilen veriler, action üzerinden nasıl alınır.
74 | - Her iki metotla gönderilen veri de, parametre olarak alınabilir.
75 | - Parametre olarak almanın dışında her iki metotla gelen bilgi de `Request` üzerinde taşındığı için, bu sınıf içinden alınabilir.
76 | - GET -> `Request.Query["key"]`
77 | - POST -> `Request.Form["key"]`
78 | - Route -> `RouteData.Values["key"]`
79 |
80 | ```html
81 |
82 |
88 | ```
89 |
90 | ```cs
91 | // Metot 01
92 | [HttpPost]
93 | public ActionResult GetInfo()
94 | {
95 | string name = Request.Form["name"];
96 | string surname = Request.Form["surname"];
97 | int age = int.Parse(Request.Form["age"]);
98 | return RedirectToAction("Index");
99 | }
100 | ```
101 |
102 | ```cs
103 | // Metot 02
104 | [HttpPost]
105 | public ActionResult GetInfo(string name, string surname, int age)
106 | {
107 | return RedirectToAction("Index");
108 | }
109 | ```
110 |
111 | - If( IsPost ) Kullanımı
112 | - Using ( Html.BeginForm ) Kullanımı
113 | - Başka bir controller'a veri gönderme yöntemi
114 |
115 | #### AntiForgeryToken Kontrolü
116 | - [XSRF/CSRF](https://docs.microsoft.com/en-us/aspnet/core/security/anti-request-forgery?view=aspnetcore-2.0) zaafiyetlerini engellemek için kullanılan yöntemdir.
117 | - Bu yöntem kullanıldığında, form client tarafına gönderilirken bir token oluşturulur ve bu token session üzerinde kaydedilir.
118 | - Kullanıcı formu post ettiğinde, bu token app tarafına gönderilir.
119 | - Controller çalışmadan önce bu token'ın, session üzerinde kaydedilen token ile eşleştirilmesi yapılır. Eğer token uyumsuz ise geçişe izin verilmez.
120 | - Böylece başka bir sayfadan veya harici olarak form gönderimi engellenmiş, sadece uygulamının render edip gönderdiği html üzerindeki form gönderimi kabul edilmiş olunur.
121 | - Bu yöntemi kullanmak için yapılması gereken şudur:
122 | - Öncelikle html tarafındaki form içine bir `__RequestVerificationToken` adında bir token üretilmelidir.
123 | - Eğer projeye `TagHelpers`'lar dahilse, form tagları açılıp **form metodu post olarak işaretlenirse** bu token otomatik olarak üretilir.
124 | - Eğer TagHelper'lar kullanılmıyorsa, htmlde form taglarının arasına `@Html.AntiForgeryToken()` yazılarak, Html Helper'lar yardımıyla bu token üretilebilir.
125 | - Daha sonra bu formun post edildiği controller üzerinde attribute olarak şu satır eklenir. Bu attribute'ün amacı, token kontrolü yapıp, uyumsuzluk durumda controller'a erişimi engellemektir.
126 | - `[ValidateAntiForgeryToken]`
--------------------------------------------------------------------------------
/11 - View Components.md:
--------------------------------------------------------------------------------
1 | ## VIEW COMPONENTS
2 |
3 | - Daha önceki .NET Framework sürümünde kullanılan `Partial View` yapısına benzer fakat biraz daha esnek ve geniş kullanım özellikleri sunan yapıdır.
4 | - `Partial View` kavramı .NET Core sürümünde halen olsa da, daha kullanışlı olması nedeniyle `View Components` yapısını kullanmayı tercih ederiz.
5 | - Buradaki temel mantık, bir kere yazılan html parçasının, modelli veya modelsiz (dinamik veya statik) bir yapıda tekrar tekrar kullanılmasını sağlamaktır. Bu sayede, bu html parçasında yapılan tek bir değişiklik, bu parçanın kullanıldığı tüm yerlerde değişecektir.
6 | - View Componentleri, View yapısının fonksiyonları gibi düşünebiliriz. Parametre alabilen ve geriye html döndüren bir yapısı vardır.
7 | - View Components kullanmak `DRY` prensibi gereği kod tekrarının önleyecektir.
8 | - View Componentler, parametre alımına izin verir, bu Partial View'da olmayan bir özelliktir.
9 | - Componentler, direk olarak `Layout` içinde kullanılabilir. Bu kullanım için, View dosyalarının shared dizini altında bulunması gereklidir.
10 |
11 | ### 01 - View Component Oluşturma
12 |
13 | - Projemiz içinde bir class oluşturuyoruz.
14 | - Class'ı bir klasör içinde oluşturmamız projenin toplu durması açısından daha iyi olur.
15 | - Açtığımız class yapısının component olduğunu belirtmek için üç yol vardır :
16 | 1. Herhangi bir isimle oluşturulup `ViewComponent` sınıfından kalıtım verebiliriz.
17 | 2. Herhangi bir isimle oluşturulup `[ViewComponent]` attribute'ü eklenebilir.
18 | 3. İsimlendirme yaparken isminin sonuna `-ViewComponent` ekleyebiliriz.
19 | - Her üç durumda da, oluşturduğumuz class artık bir View Component olacaktır.
20 | - **NOT:** Bu üç durumdan sadece 1. durumda View Component `View()` döndürecektir. Bu sınıfın özelliklerine ulaşabilmek için en doğru olan metot kalıtım vermektir.
21 | - View Component sınıfı, Controller sınıflarında olduğu gibi; public, non-nested ve non-abstract olmalıdır.
22 | - Oluşturduğumuz component sınıfı içinde bir tane `Invoke()` adlı metot oluşturuyoruz.
23 | - Component'i çağırdığımız zaman çalışan ve bize geri dönüş veren metot bu metottur.
24 |
25 | ```cs
26 | public class Hello : ViewComponent
27 | {
28 | public string Invoke()
29 | {
30 | return "Hello World!";
31 | }
32 | }
33 | ```
34 |
35 | ### 02 - Oluşturulan View Component'i View İçinde Kullanma
36 |
37 | - View Component'i çağırmak için `Component.InvokeAsync()` metodunu `await` olarak çağırmak yeterlidir.
38 |
39 | ```cs
40 | @await Component.InvokeAsync("Hello")
41 | ```
42 |
43 | **NOT:** Componentler, Controller içinden de direk olarak return edilebilirler:
44 |
45 | ```cs
46 | public IActionResult IndexVC()
47 | {
48 | return ViewComponent("PriorityList", new { maxPriority = 3, isDone = false });
49 | }
50 | ```
51 |
52 | ### 03 - ViewComponent Result Türleri
53 |
54 | - ViewComponent sınıfı içindeki Invoke() metodunun genel geri dönüş tipi `IViewComponentResult`'tur. Bunun dışında aşağıdaki gibi bilinen C# veri türleri döndürlürse, bu tipler de geri dönüş tipi olarak verilebilir.
55 |
56 | #### Bilinen türleri döndürme
57 |
58 | - Yazılan `Invoke()` metodu sonucunda, C# yapısındaki bilinen türleri döndürebilir. Örn: string, int, double vs.
59 |
60 | ```cs
61 | public class Hello : ViewComponent
62 | {
63 | public string Invoke()
64 | {
65 | return "Hello World!";
66 | }
67 | }
68 | ```
69 |
70 | #### Html Döndürme
71 |
72 | - String olarak veri döndürdüğümüzde ve bunu view içinde kullandığımda, html taglarının string olarak algılanıp biçimlendirme yapılmadığını görürüz. Bunların html olarak algılanması için `HtmlString` türünde bir geri dönüş kullanmamız gerekmektedir.
73 |
74 | ```cs
75 | [ViewComponent]
76 | public class Hello
77 | {
78 | public HtmlString Invoke()
79 | {
80 | return new HtmlString("Hello World!");
81 | }
82 | }
83 | ```
84 |
85 | #### ViewComponent ile View Döndürme
86 |
87 | - ViewCompoenentler, Invoke metotları içinde statik veya model ile dinamik bir view döndürebilirler.
88 | - Oluşturulacak view dosyalarının aranacağı dizinler :
89 | - `Views//Components//`
90 | - `Views/Shared/Components//`
91 | - **NOT:** Eğer return edilen View() metodu içinde, html dosyasının adı belirtilmemişse, default olarak arayacağı view ismi `default.cshtml`'dir.
92 |
93 | ```cs
94 | public IViewComponentResult Invoke(){
95 | return View(); // Default.cshtml dosyasını arar
96 | }
97 |
98 | public IViewComponentResult Invoke(){
99 | return View("foo"); // foo.cshtml dosyasını arar
100 | }
101 | ```
102 |
103 | - View Component'lerde View ile model kullanımı, Views'larda olduğu gibidir.
104 | - Örnek vermek gerekirse:
105 |
106 | ```cs
107 | // View Component Sınıfı
108 | using Microsoft.AspNetCore.Mvc;
109 | using System.Collections.Generic;
110 |
111 | namespace Project.Library.Components
112 | {
113 | public class SideBar : ViewComponent
114 | {
115 | public IViewComponentResult Invoke()
116 | {
117 | var model = new Dictionary();
118 | for (int i = 0; i < 5; i++)
119 | model.Add($"www.example{i}.com", $"Web Site {i}");
120 |
121 | return View(model);
122 | }
123 | }
124 | }
125 | ```
126 |
127 | ```html
128 |
129 | @model Dictionary
130 |
131 |
138 | ```
139 |
140 | ```html
141 |
142 | @await Component.InvokeAsync("SideBar")
143 |
144 |
156 | ```
157 |
158 | ### 04 - ViewComponent Üzerine Parametre Gönderme
159 |
160 | ```cs
161 | // Component Model
162 | public HtmlString Invoke(string message)
163 | {
164 | return new HtmlString($"{message}");
165 | }
166 |
167 | // Html
168 | @await Component.InvokeAsync("Hello", new { message = "Test Message" })
169 | ```
170 |
--------------------------------------------------------------------------------
/19 - ASP.NET Core In-Depth - Dependecy Injection.md:
--------------------------------------------------------------------------------
1 | ## DEPENDENCY INJECTION NEDİR?
2 |
3 | #### DI Nedir ?
4 |
5 | - Bir sınıfın bağımlı olduğu sınıfların, bu sınıfın içine dışarıdan enjekte edilmesi işlemidir.
6 | - Temel amaç, sınıfın bağımlılığını ortadan kaldırmaktır.
7 | - Nesne tabanlı programanın en önemli prensiplerinden biri olan SOLID ilkesinin içinde bulunur.
8 |
9 | #### Var olan bir kodu neden değiştirmek istemeyiz?
10 |
11 | - Program içindeki bir kodu zorunlu kalmadıkça değiştirmek istemeyiz.
12 | - Bunun en büyük nedeni, mevcut kodun uzun zamandır test edilmiş ve sorunları devamlı düzeltilmiş olup, yapacağınız ufak değişiklikler bile, hem kodu hem de bağlı olan yapıları tekrar test etmeyi gerektirecektir.
13 | - Eğer yazdığımız unit testler yeterince kapsamlı değilse, kodun güvenirliliğine emin olmak için, uzun bir süre daha kullanıp hatalarını gidermek gerekecektir.
14 | - Bunun içindir ki SOLID prensiplerinden bir tanesi de Open/Closed Principle (OCP) olarak geçmektedir. OCP kısaca bir kod değişime kapalı ama genişletmeye açık olmalıdır demektir. Yani bir koda yeni şeyler ekleyebilirsiniz, mesela yeni sınıflar gibi. Ama var olan sınıfları ve sınıflar içinde ki methodların içeriğini değiştirmeniz yukarıda açıkladığım sebeplerden dolayı çoğu zaman tavsiye edilmez.
15 | - DI ile bağımlılıklar genel olarak ortadan kaldırıldığı için, genişletilebilirlik ve her genişletmenin bağımsız olarak test edilebilirliği sağlanmış olur.
16 |
17 | #### DI Kullanmadan programlama
18 |
19 | - Örnek olarak elimizde aşağıdaki gibi bir sınıf olsun.
20 |
21 | ```cs
22 | public class Creator {
23 |
24 | private Checker _checker = new Checker();
25 | // işlemler
26 | }
27 |
28 | // veya aynı class'ı constructor ile yazarsak;
29 |
30 | public class Creator {
31 |
32 | private Checker _checker;
33 |
34 | public Creator() {
35 | _checker = new Checker();
36 | }
37 | // işlemler
38 | }
39 | ```
40 |
41 | - Yukarıdaki sınıf incelenirse, dışarıdan `Checker` adında başka bir sınıftan türeyen nesneye sahip. Yani bu sınıfa bağlı.
42 | - Eğer ileride farklı bir checker kullanılmak isteniyorsa veya birden fazla müşteri için birden fazla checker yazmak gerekiyorsa, yapılabilecek iki çözüm yolu vardır :
43 | 1. olarak, Checker adlı sınıf değiştirilebilir.
44 | - Böyle bir değişim yapılırsa, eski sınıfın bilgilerinin üzerine override edilmesi gerektiği gibi, yeniden kodun test edilmesi ve bağlı yapılarla uyumunun kontrol edilmesi gerekmektedir.
45 | - Üstelik böyle bir düzenlemenin geri dönüşü olmayacaktır, çünkü eski yapı silinip yeni bir yapıya geçilmiştir.
46 | 2. olarak, Checker benzeri başka sınıflar oluşturulabilir ( Öneğin; NetChecker, AISChecker vb. ).
47 | - Böyle bir durumda, Creator sınıfı ve buna benzer Checker sınıfından kalıtım alan diğer tüm sınıflar içinde değişiklik yapılmalıdır.
48 | - Bu durum önceki duruma göre geri dönüşüme izin verir, fakat birden çok class yapısı etkilendiği için sağlıklı değildir. Üstelik gözden kaçırılan bağımlılıkların olmaması için, tüm bağımlı sınıfların loglarının tutulması da gereklidir.
49 | - Bunlar dışında oluşturulacak yeni sınıfların belli bir düzenini tutturmak zor olacaktır ve eksik metot veya property ekleme olasılığı artacaktır. Bu da sınıfı kullanan diğer yerlerdeki hata olasılığını arttıracaktır.
50 |
51 | #### Interface kullanarak programlama
52 |
53 | - Türetilen sınıfların belli bir düzende olmasını ve hepsinde ortak property ve metotların olmasını sağlamak için interface'ler kullanılır.
54 | - Interface'lerin implament edildiği tüm sınıflar aynı düzende olacaktır.
55 |
56 | ```cs
57 | public interface IChecker
58 | {
59 | bool Exists(AccountNumber accountNumber);
60 | }
61 |
62 | public class DatabaseAccountChecker: IChecker {}
63 |
64 | public class AzureAccountChecker: IChecker {}
65 |
66 | public class XmlAccountChecker: IChecker {}
67 | ```
68 |
69 | - Creator sınıfını artık aşağıdaki gibi yazabiliriz:
70 |
71 | ```cs
72 | public class Creator {
73 |
74 | private IChecker _checker;
75 |
76 | public Creator() {
77 | _checker = new DatabaseAccountChecker();
78 | }
79 | // işlemler
80 | }
81 | ```
82 |
83 | - Bu durumda sadece ctor içindeki nesneyi değiştirmemiz yeterli olacaktır.
84 | - Bu şekilde yeni sınıflar için düzen sağlanmış ve bağımlılık kısmen azaltılmış olacaktır.
85 | - Fakat bu durumda bile halen Creator sınıfı IChecker interface'inden türetilen sınıflara bağımlı durumdadır.
86 |
87 | #### Dependency Injection kullanarak programlama
88 |
89 | - Oluşturacağımız sınıflarda, interface'ler dışında başka hiçbir sınıfa bağımlılık bulunmamalıdır.
90 | - Yukarıdaki kodları aşağıdaki gibi değiştirebilir:
91 |
92 | ```cs
93 | public class Creator {
94 |
95 | private IChecker _checker;
96 |
97 | public Creator(IChecker checker) {
98 | _checker = checker;
99 | }
100 | // işlemler
101 | }
102 | ```
103 |
104 | - Son durumda, sınıf içinde interface dışında herhangi bir bağımlılık bulunmamaktadır.
105 | - Interface'ten türetilen sınıflar, Creator sınıfı kullanıldığında (ctor metot parametresi olarak tanımlandığından) dışarıdan zorunlu olarak girilmesi istendiğinden, her kullanıldığı yerde parametre olarak almak zorundadır.
106 | - Fakat DI kütüphaneleri kullanarak (örn: Ninject DI framework), global olarak bu ilişkilendirme işlemi yapılabilir. Örneğin, her `IChecker` interface'i kullanıldığı zaman `DatabaseAccountChecker` sınıfından bir nesne gönder gibi.
107 | - Bu kütüpnanelere örnek olarak [Ninject](http://www.ninject.org/) verilebilir. Ayarlaması basit olarak aşağıdaki gibidir:
108 |
109 | ```cs
110 | public class CheckerModule : NinjectModule
111 | {
112 | public override void Load()
113 | {
114 | this.Bind().To();
115 | }
116 | }
117 | ```
118 |
119 | - .NET Core içinde DI otomatik olarak gelir ve `Startup.cs` içinde birleştirme yapılabilir.
120 | - Örnek olarak `IPersonRepository` interface'i ile `SQLPersonRepository` sınıfı birleştirilmek isteniyorsa, aşağıdaki gibi bir kod, `Startup.cs` içine eklenir.
121 |
122 | ```cs
123 | services.AddTransient();
124 | ```
125 |
126 | ### Sonuç : DI Faydaları
127 |
128 | - Projede somut sınıflar üzerine olan bağımlılığı azaltacaktır ve dolayısıyla genel olarak bakımın daha kolay olmasını sağlayacak.
129 | - Daha düzgün tasarlanmış modüller arası dependency tree’ye sahip olacaksınız.
130 | - Programda istenilen sınıfların rahatlıkla değiştirilmesine yardımcı olacak. Çünkü bağımlılık olmadığı için bir sınıf yerine başka bir sınıf kullanmak istediğinizde değiştirmek zorunda olacağınız kod miktarı ya sıfır yada çok daha az olacak.
131 | - Uygulama çalışma anında sizlere daha rahat configuration yapma şansı tanıyacak. Mesela, XML kullanarak hangi interface ile hangi sınıfın birbirlerine bağlı olacağını kolayca tanımlayabilirsiniz. Bu farklı müşterilen farklı isteklerini kodunuzu değiştirmek zorunda kalmadan rahatlıkla sunabilmeniz demektir.
132 | - Hangi sınıfları kullanacağınızı bir yerden kontrol etmiş olacaksınız.
133 | - Belkide en önemlisi çok daha rahat Unit Test ler yazmanızı sağlamış olacağı. İstediğiniz sınıfı rahatlıkla mock ederek farklı kod kısımlarını test edebileceksiniz.
--------------------------------------------------------------------------------
/22 - ASP.NET Core In-Depth - ORM Mimarileri.md:
--------------------------------------------------------------------------------
1 | ## BAĞLI TABLOLARIN ÇEKİLMESİ İÇİN KULLANILAN ORM DESENLERİ
2 |
3 | - Entity Framework Core ile database üzerinde sorgu işlemleri yapılırken, bağlı tablolar otomatik olarak gelmez
4 | - Kullanılan desenler 3 tanedir:
5 | - **Eager Loading**
6 | - Sorgu içinde bağlı tabloların gelmesini sağlar.
7 | - **Explicit Loading**
8 | - Bağlı tabloların daha sonra sorgu içine yüklenmesini sağlar
9 | - **Lazy Loading**
10 | - Bağlı tabloların sanal olarak yüklenmesini ve istendiğinde dahil edilmesini sağlar.
11 |
12 | ### 01 - Eager Loading
13 |
14 | #### Bağlı tabloyu eklemek
15 |
16 | - Sorgu içine bağlı tabloları da dahil etmek için `Include` fonksiyonu kullanılır.
17 | - Bu fonksiyon .NET Core üzerinde yazılırken otomatik olarak çıkmayacağından aşağıdaki using ifadesine ihtiyaç duyar.
18 | - `using Microsoft.EntityFrameworkCore;`
19 |
20 | ```cs
21 | using (var context = new BloggingContext())
22 | {
23 | var blogs = context.Blogs
24 | .Include(blog => blog.Posts)
25 | .ToList();
26 | }
27 | ```
28 |
29 | - Birden fazla `Include()` ifadesi aynı sorgu içinde kullanılabilir.
30 |
31 | ```cs
32 | using (var context = new BloggingContext())
33 | {
34 | var blogs = context.Blogs
35 | .Include(blog => blog.Posts)
36 | .Include(blog => blog.Owner)
37 | .ToList();
38 | }
39 | ```
40 |
41 | #### Bağlı tabloların bağlı tablolarını eklemek
42 |
43 | - Sorgumuzu çektiğimiz tablonun bağlı tablolarının da bağlı olduğu tabloları çekmek istiyorsak, kullanmamız gereken yapı `ThenInclude()` metodudur.
44 |
45 | ```cs
46 | using (var context = new BloggingContext())
47 | {
48 | var blogs = context.Blogs
49 | .Include(blog => blog.Posts)
50 | .ThenInclude(post => post.Author)
51 | .ToList();
52 | }
53 | ```
54 |
55 | - İstenilen tablo yapısına ulaşana kadar, istenilen kadar `ThenInclude()` metodu kullanılabilir.
56 |
57 | ```cs
58 | using (var context = new BloggingContext())
59 | {
60 | var blogs = context.Blogs
61 | .Include(blog => blog.Posts)
62 | .ThenInclude(post => post.Author)
63 | .ThenInclude(author => author.Photo)
64 | .ToList();
65 | }
66 | ```
67 |
68 | - `Include()` ve `ThenInclude()` metotları birlikte combine edilebilir.
69 |
70 | ```cs
71 | using (var context = new BloggingContext())
72 | {
73 | var blogs = context.Blogs
74 | .Include(blog => blog.Posts)
75 | .ThenInclude(post => post.Author)
76 | .ThenInclude(author => author.Photo)
77 | .Include(blog => blog.Owner)
78 | .ThenInclude(owner => owner.Photo)
79 | .ToList();
80 | }
81 | ```
82 |
83 | #### Sonradan tablo bağlama
84 |
85 | - Bir sorgu yaptıktan sonra, bağlı tablolar istenildiği zamanda çekilebilir. Bu duruma `ayrık sorgulama` denir.
86 | - Ayrık sorgulama yapılırken, ihtiyaç olduğu yerde, lazım olan tabloların sorgulaması yapılıp, context nesnesi üzerine `Load()` metoduyla eklenir.
87 | - EF bu eklemeyi otomatik olarak algılayıp tabloları bağlar.
88 |
89 | ```cs
90 | // Single Query
91 | var addresses = _context.Addresses.Include(k => k.Persons);
92 | foreach (var address in addresses)
93 | {
94 | foreach (var person in address.Persons)
95 | {
96 | liste.Add(person.Name);
97 | }
98 | }
99 | ```
100 |
101 | ```cs
102 | // Separated Query
103 | var addresses = _context.Addresses;
104 | foreach (var address in addresses)
105 | {
106 | _context.Persons.Where(k => k.AddressID == address.ID).Load();
107 | foreach (var person in address.Persons)
108 | {
109 | liste.Add(person.Name);
110 | }
111 | }
112 | ```
113 |
114 | ### 02 - Explicit Loading
115 | - Sorgulama esnasında bağlı tabloların çekilmeyip, gerektiği yerde sorgunun tekrar yapılıp bağlı tabloların çekilmesine dayalı sorgulama biçimidir.
116 | - Eager Loading'teki ayrık sorgulamaya benzer bir yapıda, db üzerine birden fazla sorgu gönderir. Tek farkı kod yapısındadır.
117 | - EF Core 1.1 sürümü ve sonrasında desteklenmektedir.
118 |
119 | ```cs
120 | // Example 1
121 | var addresses = _context.Addresses;
122 | foreach (var address in addresses)
123 | {
124 | _context.Entry(address).Collection(k => k.Persons).Load();
125 | foreach (var person in address.Persons)
126 | {
127 | liste.Add(person.Name);
128 | }
129 | }
130 | ```
131 |
132 | ```cs
133 | // Example 2
134 | using (var context = new BloggingContext())
135 | {
136 | var blog = context.Blogs
137 | .Single(b => b.BlogId == 1);
138 |
139 | context.Entry(blog)
140 | .Collection(b => b.Posts)
141 | .Load();
142 |
143 | context.Entry(blog)
144 | .Reference(b => b.Owner)
145 | .Load();
146 | }
147 | ```
148 |
149 | - Explicit Loading ile bağlı tablolar sorgulandığında, sadece sorgu sonrasında geri dönen değerler ram üzerinde depolanır ve tüm bağlı tablo verileri depolanmaz.
150 |
151 | ```cs
152 | using (var context = new BloggingContext())
153 | {
154 | var blog = context.Blogs
155 | .Single(b => b.BlogId == 1);
156 |
157 | var postCount = context.Entry(blog)
158 | .Collection(b => b.Posts)
159 | .Query()
160 | .Count(); // => Bu işlem sonucunda sadece sayısal değer ataması yapılır.
161 | }
162 | ```
163 |
164 | ```cs
165 | using (var context = new BloggingContext())
166 | {
167 | var blog = context.Blogs
168 | .Single(b => b.BlogId == 1);
169 |
170 | var goodPosts = context.Entry(blog)
171 | .Collection(b => b.Posts)
172 | .Query()
173 | .Where(p => p.Rating > 3)
174 | .ToList();
175 | // => Bu işlem sonucunda sadece değeri 3ten büyük
176 | // olan değerler çekilip ram üzerine yazılır.
177 | }
178 | ```
179 |
180 | ### 03 - Lazy Loading
181 | - Sorgu ilk yapıldığında, bağlı tablolar çekilmez.
182 | - Bağlı tablolara ait property'lere ilk ulaşılmaya çalışıldığı zaman, database sorgusu otomatik olarak gerçekleştirilir ve bağlı tablo bilgileri çekilir.
183 | - Bağlı tablolara her ulaşılmaya çalışıldığı ilk sefer, ayrı bir sorgu ile database sorgusu yapılır.
184 | - Lazy loading kullanmak için [Microsoft.EntityFrameworkCore.Proxies](https://www.nuget.org/packages/Microsoft.EntityFrameworkCore.Proxies/) paketi projeye dahil edilmeli ve `UseLazyLoadingProxies` aktif edilmelidir.
185 |
186 | ```cs
187 | protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
188 | => optionsBuilder
189 | .UseLazyLoadingProxies()
190 | .UseSqlServer(myConnectionString);
191 | ```
192 |
193 | - Veya AddDbContext kullanılıyorsa;
194 |
195 | ```cs
196 | .AddDbContext(
197 | b => b.UseLazyLoadingProxies()
198 | .UseSqlServer(myConnectionString));
199 | ```
200 |
201 | - Lazy loading kullanıldığında tüm bağlı tablolar `virtual` olarak tanımlanmalıdır.
202 |
203 | ```cs
204 | public class Blog
205 | {
206 | public int Id { get; set; }
207 | public string Name { get; set; }
208 |
209 | public virtual ICollection Posts { get; set; }
210 | }
211 |
212 | public class Post
213 | {
214 | public int Id { get; set; }
215 | public string Title { get; set; }
216 | public string Content { get; set; }
217 |
218 | public virtual Blog Blog { get; set; }
219 | }
220 | ```
221 |
222 |
223 | ### Performans Kıyaslaması
224 | - Genel olarak Eager Loading kullanılması daha uygundur. Çünkü tüm işlemlerin bir database sorgusu üzerinde yapılması, ayrı ayrı database'e bağlanılıp sorgu yapılmasından daha performanslıdır. Bu yüzden bağlı tablolarla fazla işlem yapılmasının gerekli olmadığı durumda bu yöntem tercih edilir.
225 | - Fakat bazı zamanlarda ayrık sorgulama, tek sorgulamadan daha performanslıdır. Özellikle bağlı tabloların hepsini çekmek yerine bir kısmının çekilmesi durumunda veya bağlı tablolar arasındaki ilişkilerin çok karmaşık olduğu durumda bu yöntemin tercih edilmesi daha uygundur.
226 | - Performansın çok kritik olduğu durumlarda bu iki durumda, o anki duruma uygun olarak mix edilir.
227 |
228 | ### Kaynaklar
229 | - https://docs.microsoft.com/en-us/ef/core/querying/related-data
230 | - https://docs.microsoft.com/en-us/aspnet/core/data/ef-mvc/read-related-data
--------------------------------------------------------------------------------
/BONUS - FTP Server Kurulumu.md:
--------------------------------------------------------------------------------
1 | ## FTP SERVER KURULUMU (Google Cloud)
2 |
3 | #### ADIM 1 - Google Cloud Hesabından Yeni Bir Sanal Makine Oluşturma ve Bağlanma
4 |
5 | - Google Cloud hesabına giriş yapılıp yeni bir proje eklenir.
6 | - `Compute Engine` > `Sanal makine örnekleri` yolundan yeni bir sanal makine eklenir.
7 | - `Ubuntu 16.04 LTS` versiyonunu seçiyoruz.
8 | - Giriş için SSH key aktifleştirmesi yapılabilir.
9 | - Kurulum yapıldıktan sonra çıkan ekrandaki `Harici IP` bizim ssh bağlantısını yapacağımız IP adresidir.
10 | - SSH ile giriş yapıyoruz.
11 |
12 | ```
13 | $ ssh -i .ssh/id_rsa ssonmez@
14 | ```
15 |
16 | #### ADIM 2 - Kurulum Yapma
17 |
18 | - Sistem üzerinde güncelleştirmeleri kontrol ediyoruz.
19 |
20 | ```
21 | $ sudo apt-get update
22 | $ sudo apt-get upgrade
23 | ```
24 |
25 | - FTP server kurulumunu gerçekleştiriyoruz
26 |
27 | ```
28 | $ sudo apt-get install vsftpd -y
29 | ```
30 |
31 | #### ADIM 3 - Local Firewall Kuralları
32 |
33 | - FTP bağlantısında hata olmaması için, server üzerinde bulunan firewall kısmının kapalı veya gerekli izinleri barındırmış olması gerekmektedir.
34 | - Firewall durumunu kontrol etmek için :
35 |
36 | ```
37 | $ sudo ufw status
38 | ```
39 |
40 | ```bash
41 | # OUTPUT : Firewall kapalı
42 |
43 | Status: inactive
44 |
45 | # OUTPUT : Firewall açık
46 |
47 | Status: active
48 |
49 | To Action From
50 | -- ------ ----
51 | OpenSSH ALLOW Anywhere
52 | OpenSSH (v6) ALLOW Anywhere (v6)
53 | ```
54 |
55 | - Firewall üzerine kural eklemek için şu komutları yazıyoruz:
56 |
57 | ```bash
58 | $ sudo ufw allow 20/tcp # for FTP
59 | $ sudo ufw allow 21/tcp # for FTP
60 | $ sudo ufw allow 990/tcp # for TSL
61 | $ sudo ufw allow 40000:50000/tcp # for Passive Ports
62 | $ sudo ufw status
63 |
64 | Status: active
65 |
66 | To Action From
67 | -- ------ ----
68 | OpenSSH ALLOW Anywhere
69 | 990/tcp ALLOW Anywhere
70 | 20/tcp ALLOW Anywhere
71 | 21/tcp ALLOW Anywhere
72 | 40000:50000/tcp ALLOW Anywhere
73 | OpenSSH (v6) ALLOW Anywhere (v6)
74 | 20/tcp (v6) ALLOW Anywhere (v6)
75 | 21/tcp (v6) ALLOW Anywhere (v6)
76 | 990/tcp (v6) ALLOW Anywhere (v6)
77 | 40000:50000/tcp (v6) ALLOW Anywhere (v6)
78 | ```
79 |
80 | #### ADIM 4 - Yeni FTP Kullanıcısı Ekleme
81 |
82 | - Yeni bir kullanıcı ekleyip bu kullanıcı için bir ftp dizini ayarlıyoruz.
83 |
84 | ```bash
85 | # Yeni kullanıcı ekleme
86 | $ sudo adduser
87 | ```
88 |
89 | ```bash
90 | # Kullanıcı için ftp dizini oluşturma
91 | $ sudo mkdir /home//ftp
92 | $ sudo chown nobody:nogroup /home//ftp
93 | $ sudo chmod a-w /home//ftp
94 | ```
95 |
96 | - Son durumda ftp dizininin izinleri aşağıdaki gibi olmalıdır.
97 |
98 | ```bash
99 | $ sudo ls -la /home//ftp
100 |
101 | total 8
102 | 4 dr-xr-xr-x 2 nobody nogroup 4096 Aug 24 21:29 .
103 | 4 drwxr-xr-x 3 4096 Aug 24 21:29 ..
104 | ```
105 |
106 | - Daha sonrasında ftp içinde dosyaların yükleneceği ve kullanıcının erişiminin olduğu bir dizin oluşturmamız gerekecektir.
107 |
108 | ```bash
109 | $ sudo mkdir /home//ftp/files
110 | $ sudo chown : /home//ftp/files
111 | ```
112 |
113 | - Son durumda son eklediğimiz `files` adlı dizinin izinleri şu şekilde olmalıdır.
114 |
115 | ```bash
116 | $ sudo ls -la /home//ftp
117 |
118 | total 12
119 | dr-xr-xr-x 3 nobody nogroup 4096 Aug 26 14:01 .
120 | drwxr-xr-x 3 4096 Aug 26 13:59 ..
121 | drwxr-xr-x 2 4096 Aug 26 14:01 files
122 | ```
123 |
124 | #### ADIM 5 - FTP Ayarlarının Yapılması
125 |
126 | - FTP programının ayar dosyaları `/etc/vsftpd.conf` altında bulunmaktadır.
127 | - Öncelikle bu dosyayı yeniden oluşturmak için eskisinin ismini değiştiriyoruz.
128 |
129 | ```bash
130 | $ sudo mv /etc/vsftpd.conf /etc/vsftpd.conf.orig
131 | ```
132 |
133 | - Sonrasında bu dosyayı düzenlenebilir halde yeniden açıyoruz.
134 |
135 | ```bash
136 | $ sudo nano /etc/vsftpd.conf
137 | ```
138 |
139 | - Açılan pencerede aşağıdaki satıları ekleyip kaydediyoruz.
140 |
141 | ```bash
142 | # FTP aktifleştirme
143 | listen=YES
144 |
145 | # Bilinmeyen kaynaklara erişimi kapatma
146 | anonymous_enable=NO
147 |
148 | # Lokal kaynaklara erişimi açma
149 | local_enable=YES
150 | write_enable=YES
151 | chroot_local_user=YES
152 |
153 | # FTP dosya yolu
154 | user_sub_token=$USER
155 | local_root=/home/$USER/ftp
156 |
157 | # İzin verilern kullanıcıların çekileceği dosya yolu
158 | userlist_enable=YES
159 | userlist_file=/etc/vsftpd.userlist
160 | userlist_deny=NO
161 |
162 | # Passive Port Ayarlamaları
163 | # Buraya hostun public ip adresinin girilmesi gerekmektedir.
164 | pasv_enable=YES
165 | pasv_min_port=40000
166 | pasv_max_port=50000
167 | pasv_address=
168 | ```
169 |
170 | - Bu ayarları yaptıktan sonra daha önce FTP için oluşturduğumuz kullanıcı adını, FTP erişimine izin vermek için oluşturacağımız yeni bir dosyanın içine ekliyoruz.
171 |
172 | ```
173 | $ echo "" | sudo tee -a /etc/vsftpd.userlist
174 | ```
175 |
176 | - Eklediğimiz kullanıcı adının istediğimiz şekilde eklendiğinden emin olmamız gerekmektedir.
177 |
178 | ```bash
179 | $ cat /etc/vsftpd.userlist
180 | # Çıktı
181 | serhat
182 | ```
183 |
184 | - Tüm işlemler düzgün ilerlediyse, ftp server'ı baştan başlatmamız gerekmektedir.
185 |
186 | ```
187 | $ sudo systemctl restart vsftpd
188 | ```
189 |
190 | #### ADIM 6 - Google Cloud Firewall Ayarlarının Yapılması
191 |
192 | - Lokal firewall ayarlarının yanında, Google Cloud Firewall ayarlarından da kullanacağımız portlara izin vermemiz gerekmektedir.
193 | - `VPS Ağı > Güvenlik duvarı kuralları` sekmesinden aşağıdaki ayarlamaları yapıyoruz.
194 |
195 | ```
196 | AD : FTP-Rule-1
197 | Ag : Default
198 | Öncelik : 1000
199 | Yön : Giriş
200 | Eylem : İzin Ver
201 | Hedefler : Ağdaki tüm örnekler
202 | Kaynak filt : IP Aralıkları
203 | IP Aralık : 0.0.0.0/0
204 | Protokoller : tcp:21
205 | ---
206 | AD : FTP-Rule-2
207 | Ag : Default
208 | Öncelik : 1000
209 | Yön : Giriş
210 | Eylem : İzin Ver
211 | Hedefler : Ağdaki tüm örnekler
212 | Kaynak filt : IP Aralıkları
213 | IP Aralık : 0.0.0.0/0
214 | Protokoller : tcp:20
215 | ---
216 | AD : FTP-Rule-TSL
217 | Ag : Default
218 | Öncelik : 1000
219 | Yön : Giriş
220 | Eylem : İzin Ver
221 | Hedefler : Ağdaki tüm örnekler
222 | Kaynak filt : IP Aralıkları
223 | IP Aralık : 0.0.0.0/0
224 | Protokoller : tcp:990
225 | ---
226 | AD : FTP-Rule-PassivePorts
227 | Ag : Default
228 | Öncelik : 1000
229 | Yön : Giriş
230 | Eylem : İzin Ver
231 | Hedefler : Ağdaki tüm örnekler
232 | Kaynak filt : IP Aralıkları
233 | IP Aralık : 0.0.0.0/0
234 | Protokoller : tcp:40000-50000
235 | ---
236 | ```
237 |
238 | #### ADIM 7 - FTP Giriş Kontrolü
239 |
240 | - Hızlıca ftp kontrolü yapmak için komut satırından `ftp` komutunu kullanabiliriz.
241 | - Buradaki girişte herhangi bir sorun yoksa, Filezilla vb ftp programlarından da giriş yapabilirsiniz.
242 |
243 | ```bash
244 | # Google Cloud üzerinden aldığımız public IP adres girilecek
245 | $ ftp -p
246 | Connected to .
247 | 220 (vsFTPd 3.0.3)
248 |
249 | # FTP kullanıcı adı girilecek
250 | Name (:serhat): serhat
251 | 331 Please specify the password.
252 |
253 | # Parola giriyoruz
254 | Password:
255 | 230 Login successful.
256 | Remote system type is UNIX.
257 | Using binary mode to transfer files.
258 |
259 | # ls ile dosyaları listeliyoruz.
260 | # Burada bir sorun varsa Passive Mode ayalarları kontrol edilmelidir.
261 | ftp> ls
262 | 227 Entering Passive Mode (35,227,17,158,175,27).
263 | 150 Here comes the directory listing.
264 | drwxr-xr-x 2 1002 1003 4096 May 08 14:14 files
265 | 226 Directory send OK.
266 |
267 | # Çıkış yapıyoruz.
268 | ftp> bye
269 | 221 Goodbye.
270 |
271 | ```
--------------------------------------------------------------------------------
/07 - Models ve Model Binding.md:
--------------------------------------------------------------------------------
1 | ## MODELS VE MODEL BINDING
2 |
3 | ### 01 - Controller'dan View'e Model ile Veri Taşıma
4 | - Controller üzerinden View'a veri taşınırken, `ViewBag` veya `TempData` gibi aracılarla veri gönderebileceğimiz gibi, bunları bir model üzerinde toplayıp, modeli de gönderebilir.
5 | - Modeli oluşturduktan sonra, döndüreceğimiz View metodu içinde parametre olarak veririz.
6 | - Önemli olan ve unutulmaması gereken, **model gönderildikten sonra, ilgili cshtml sayfasında da bu model tanıtılmalıdır.** Aksi halde gönderdiğimiz modele view üzerinden ulaşamayız.
7 | - Controller üzerinden gönderilen model yapısı ile view üzerinde tanıtılan model yapısı arasında fark varsa, uygulama hata verecektir.
8 | - Örnekler :
9 | - String, int gibi bilinen türleri gönderme
10 | - Tek model gönderme
11 | - Model listesi gönderme
12 |
13 | ```cs
14 | // Model yapısı
15 | public class Person
16 | {
17 | public int ID { get; set; }
18 | public string Name { get; set; }
19 | public string Surname { get; set; }
20 |
21 | public int AddressID { get; set; }
22 | public Address Address { get; set; }
23 | }
24 | ```
25 |
26 | ```cs
27 | // Controller
28 | public IActionResult Index()
29 | {
30 | List persons = new List()
31 | {
32 | new Person() { Name = "Serhat", Surname = "Sönmez" },
33 | new Person() { Name = "Ahmet", Surname = "Mete" },
34 | new Person() { Name = "Ayşe", Surname = "Demir" }
35 | };
36 | return View(persons);
37 | }
38 | ```
39 |
40 | ```html
41 |
42 | @model List
43 |
44 |
45 |
46 |
Name
47 |
Surname
48 |
49 | @foreach (var person in Model)
50 | {
51 |
52 |
@person.Name
53 |
@person.Surname
54 |
55 | }
56 |
57 |
58 |
62 | ```
63 |
64 | ### 02 - Model Binding
65 | - View'den Controller'a bilgi taşırken, taşınan bilgileri Query String, Form veya Route Data içinden almak yerine, direk parametre olarak almamıza olanak veren yapıdır.
66 | - Girilen parametre değerleri `key` kabul edilerek, sırasıyla `FormData > RouteData > QueryString` içinde aranır ve bir değere ulaşılırsa bu değer value olarak alınır.
67 | - Parametreler alınırken gelen tüm değerler `string` türündedir. Fakat alınacak key değerinin türü değiştirilerek, alındığı zaman direk tür dönüşümü yapılması sağlanabilir.
68 | - **NOT:** Burada tür dönüşümü yapılmazsa veya non-nullable bir parametreye (örn: int, datetime...) null değeri gönderilirse, ASP.NET Framework sürümünde hata alınıyordu. Fakat .NET Core sürümünde, hata alınmaz, bu değerler kendi default değerleri olarak atanır (örn: int için 0 alınır).
69 | - Parametre değeri olarak model de alınabilir.
70 | - Bu durumda, modelin içindeki property'ler yukarıda belirtilen sırayla aranır ve uygun olanlar alınır.
71 |
72 | ```html
73 |
74 |
79 |
80 |
81 |
82 | Get Product
83 |
84 |
85 | New Products
86 | ```
87 |
88 | - Model Binding olmadan alma
89 |
90 | ```cs
91 | // Form Data
92 | public IActionResult Save()
93 | {
94 | string productName = Request.Form["productName"];
95 | int count = int.Parse(Request.Form["count"].ToString());
96 | return View();
97 | }
98 |
99 | // Route Data
100 | public IActionResult Get()
101 | {
102 | int id = int.Parse(RouteData.Values["id"]?.ToString());
103 | return View();
104 | }
105 |
106 | // Query String
107 | public IActionResult Search()
108 | {
109 | string q = Request.Query["q"];
110 | return View();
111 | }
112 | ```
113 |
114 | - Model Binding ile alma
115 |
116 | ```cs
117 | // Form Data
118 | public IActionResult Save(string productName, double count)
119 | => View();
120 |
121 | // Route Data
122 | public IActionResult Get(int id)
123 | => View();
124 |
125 | // Query String
126 | public IActionResult Search(string q)
127 | => View();
128 | ```
129 |
130 | ### 03 - View'den Controller'a Model ile Veri Taşıma
131 |
132 | - Html üzerinden form üzerinden gönderilen bilgiler, model binding ile controller üzerinden alınırken, model yapısına çevrilebilir.
133 | - Bu yöntemle, istenen model yapısı içindeki property isimleri, FormData içinde araştırılır ve uygun olanlar modele bağlanır.
134 | - Modele bağlama işleminin doğru şekilde olması için, form içindeki input yapılarının name yapıları, model içindeki property isimlerinin birebir aynısı olmalıdır. Aksi durumda bağlantı sağlanmaz.
135 | - Bu html isimlendirme kısmında bize kolaylık sağlayan iki yapı bulunur :
136 | - Html helpers
137 | - Tag helpers
138 | - Bu iki yapı kullanıldığında, view kısmının üstünde tanımlanan model yapısından property isimleri çekilip input alanlarına name olarak verilir.
139 |
140 | Model yapısı :
141 | ```cs
142 | namespace Project.Models
143 | {
144 | public class Person
145 | {
146 | public string Name { get; set; }
147 | public string Surname { get; set; }
148 | }
149 | }
150 | ```
151 |
152 | Html ile veri gönderilirken kullanılabilir yöntemler:
153 |
154 | ```html
155 |
156 |
161 |
162 |
163 | @model Project.Models.Person
164 | @using (Html.BeginForm())
165 | {
166 | @Html.TextBoxFor(k => k.Name)
167 | @Html.TextBoxFor(k => k.Surname)
168 |
169 | }
170 |
171 |
172 | @model Project.Models.Person
173 |
178 |
179 | ```
180 |
181 | Gönderilen verileri controller üzerinden alma yöntemleri:
182 |
183 | ```cs
184 | // Metot 1 - Model binding ile ayrı ayrı alma
185 | [HttpPost]
186 | public IActionResult Index(string Name, string Surname)
187 | => return View();
188 |
189 | // Metot 2 - Model binding ile model yapısına çevirip alma
190 | [HttpPost]
191 | public IActionResult Index(Person person)
192 | => return View();
193 | ```
194 |
195 | > **NOT:** Birleşik modeller veya modelin foreign key ile bağlandığı başka bir model kullanıldığında, durum aynıdır.
196 | > - Eğer manuel olarak isimlendirme yapılacaksa, her seviye nokta ile ayrılır.
197 | > - Controller üzerinden model çekilirken, tüm modelin çekilmesi gereklidir. Sadece form içinde kullanılan model çekilmeye çalışılırsa, uyumsuzluktan dolayı null değer alınır.
198 |
199 | ### 04 - List Binding
200 |
201 | - Controller'a aynı key ismine sahip birden fazla eleman gönderilirse, bunlar dizi veya liste olarak bind edilip alınabilir.
202 |
203 | ```html
204 | @model List
205 |
211 | ```
212 |
213 | ```cs
214 | public IActionResult Form(string[] names)
215 | => View();
216 |
217 | // veya
218 |
219 | public IActionResult Form(List names)
220 | => View();
221 | ```
222 |
223 | - Aynı yapı birden çok model gönderimi için de kullanılabilir.
224 |
225 | ```html
226 | @model List
227 |
239 | ```
240 |
241 | ```cs
242 | public IActionResult Form(List customers)
243 | => View();
244 | ```
245 |
246 | ### 05 - ViewModel Oluşturma
247 | - Birleşik Model oluşturma
248 | - Birleşik Modelleri Controller’dan View’a gönderme
249 | - Birleşik Modelleri View’dan Controller’a gönderme
250 |
251 | ### 06 - Partial View'ı Model Yapısı Kullanarak Gönderme
252 | - Partial View yapısı ikinci parametre olarak bir model alarak dinamik bir hale bürünür.
253 | - Bu yapıyı html içinde `Html.Partial(, )` olarak kullanabileceğimiz gibi, ActionResult üzerinden `PartialView(, )` return ederek de kullanabiliriz.
254 | - Core MVC yapısında bu yapılar yerine `View Components` yapıları gelmiştir.(bkz. ilgili ders notu)
255 |
256 |
257 |
258 |
259 |
260 |
261 |
262 |
--------------------------------------------------------------------------------
/02 - ASP.NET Core MVC Giriş.md:
--------------------------------------------------------------------------------
1 | ## ASP .NET Core MVC PROGRAMLAMAYA GİRİŞ
2 |
3 | ### 01 - .NET Core ve .NET Standart Nedir?
4 |
5 | - .NET Core öncesinde kullanılan .NET Framework yapısı, aşağıdaki resimden de görüleceği üzere, birbirinden bağımsız olarak geliştirilen yapılarından oluşan bir platformdu.
6 | - Bu nedenle her bir yapının kendine ait farklı kütüphaneleri, frameworkleri ve runtime'ları vardı.
7 | - Ayrıca bu yapılar sadece belirli bir işletim sistemi üzerinde (çoğunlukla Windows) çalışıyordu.
8 | - Geliştirilen kütüphaneler de genel olarak yapıya özgü geliştiriliyordu.
9 |
10 |
11 |
12 |
13 |
14 | - Hem Cross-platform uygulama geliştirmenin hem de open-source projelerin yükselişi ile birlikte, .NET mimarisi de kökten bir dönüşüme girmiştir.
15 | - .NET Core ile birlikte .NET mimarisi;
16 | - Cross-Platform geliştirmeyi ve deployment'ı destekleyen,
17 | - [Açık kaynak kodlu](https://github.com/aspnet/home),
18 | - Web programları, web servisleri, IoT programları ve mobil program arka-planlaması yapılabilen,
19 | - Herhangi bir editör ile herhangi bir işletim sisteminde (Windows, MacOS, Linux) geliştirme yapılabilen,
20 | - Docker desteği olan,
21 | - IIS dışında herhangi bir web server kullanıma uygun bir yapı haline gelmiştir.
22 | - .NET Core ile .NET Framework arasındaki temel farklar;
23 | - .NET Core aslında tek bir uygulama modeli içermektedir : Console Application.
24 | - Bu yapıya yeni servisler eklenerek, farklı yapılar ortaya çıkarılmaktadır.
25 | - Buna karşın, .NET Framewok üzerinde, yukarıdaki şekilde de görüldüğü üzere, her yapı birbirinden farklıdır.
26 |
27 | Aşağıdaki resimden de görüleceği üzere, .NET Core yapısı ortaya çıktıktan sonra da bazı sorunlar devam etti. Bu sorunların en temelinde, .NET Core ve diğer yapılar için kullanılan kütüphane yapılarının birbirinden bağımsız olmasıydı. Bu sorundan ötürü Microsoft, `.NET Standart` adlı yapıyı çıkarıp, tüm kütüphane yapılarını bu çatı altında toplamıştır. Bu sayede herhangi bir platform için sadece bir kütüphane yapısı kullanılabilir hale gelmiştir.
28 |
29 | |
.NET Standart Öncesi
|
.NET Standart Sonrası
|
30 | | --- | --- |
31 | |
|
|
32 |
33 | #### .NET Core ile .NET Framework Arasındaki Teknik Farklar
34 |
35 | - Artık Core yapısı içinde özel dizinler bulunmamaktadır. (Örn: App_Code, App_Data vs.)
36 | - Core yapısı içinde eskiden kullanılan html helper metotlar yerine `TagHelpers`'lar ve `ViewComponents` yapıları gelmektedir.
37 | - Statik dosya yapısı `wwwroot` dizini içine taşınmıştır.
38 |
39 | ### 02 - Core Geliştirme Ortamları
40 |
41 | - Windows
42 | - Visual Studio 2017 veya 2015
43 | - 2017 kurarken dil paketinin EN seçilmesine dikkat edilmelidir.
44 | - 2015 sürümünde default Core 1.1 gelmektedir, bunun upgrade edilmesi gerekir.
45 | - VS Code
46 | - NET Core 2.0 SDK
47 | - https://www.microsoft.com/net/download/
48 | - Version görüntüleme
49 | - `dotnet --version`
50 | - VS üzerinden yeni proje açarken Core 2.0 seçeneği geliyorsa işlemler başlarıyla gerçekleşmiştir.
51 |
52 | - Mac ve Linux
53 | - VS Code
54 | - Kurulması tavsiye edilen eklentiler:
55 | - ASP.NET Core Snippets
56 | - C#
57 | - NET Core 2.0 SDK
58 | - https://www.microsoft.com/net/download/
59 |
60 |
61 |
62 |
63 |
64 | ### 03 - MVC Pattern Nedir?
65 |
66 | - Design Pattern
67 | - Kurallar düzenidir / Kalıptır.
68 | - Belirli kurallar ve düzenleri sabitleyerek projenin işleyişini ve - takım çalışmasını kolaylaştırır.
69 | - İlk olarak 1979 yılında Trygve Reenskaug ortaya atılmıştır. - (Microsoft’un kurulumu 1975)
70 | - MVC nasıl çalışır?
71 |
72 |
73 |
74 |
75 |
76 | ### 04 - İlk Projeyi Oluşturma
77 |
78 | - Visual Studio ile örnek proje açma
79 | - VS Code ile proje oluşturma (Terminal üzerinden)
80 | - `dotnet new --help`
81 | - Şablonları gösterir.
82 | - `dotnet new <şablon ismi>`
83 | - Seçili şablon dosyalarını oluşturur.
84 | - `dotnet run`
85 | - Projeyi çalıştırır.
86 | - İlk projenin ayarlamalarının yapılması ve basit bir MVC sayfası
87 | - MVC yapısına uygun olarak ilk `Hello World` uygulaması yapılması.
88 | - İlk ayarlamada `Startup.cs` dosyasının içeriği aşağıdaki gibi olmalıdır:
89 |
90 | ```cs
91 | using Microsoft.AspNetCore.Builder;
92 | using Microsoft.AspNetCore.Hosting;
93 | using Microsoft.Extensions.Configuration;
94 | using Microsoft.Extensions.DependencyInjection;
95 |
96 | namespace WebApplication1
97 | {
98 | public class Startup
99 | {
100 | public IConfiguration Configuration { get; }
101 |
102 | public Startup(IConfiguration configuration)
103 | {
104 | Configuration = configuration;
105 | }
106 |
107 | public void ConfigureServices(IServiceCollection services)
108 | {
109 | // Mvc servisini aktifleştiriyoruz.
110 | services.AddMvc();
111 | }
112 |
113 | public void Configure(IApplicationBuilder app, IHostingEnvironment env)
114 | {
115 | // Sadece development adımında aktif olmasını istediğimiz kısımları buraya yazıyoruz.
116 | if (env.IsDevelopment())
117 | {
118 | // Hata gösterimini açan metottur.
119 | app.UseDeveloperExceptionPage();
120 | }
121 |
122 | // wwwroot dizini altındaki statik dosyaları kullanmamızı sağlar.
123 | app.UseStaticFiles();
124 |
125 | // Status kod sayfalarının gösterimini sağlar.
126 | app.UseStatusCodePages();
127 |
128 | // MVC yapımız için default bir route tanımlaması yaparız.
129 | // Başka bir yöntem olarak aşağıdaki ifade de kullanılabilir.
130 | // app.UseMvcWithDefaultRoute();
131 | app.UseMvc(routes =>
132 | {
133 | routes.MapRoute(
134 | name: "default",
135 | template: "{controller=Home}/{action=Index}/{id?}");
136 | });
137 | }
138 | }
139 | }
140 | ```
141 |
142 | ### 05 - Projenin Dosya Düzeni
143 | - `Controllers`, `Views`, `Models`
144 | - MVC yapısındaki dizinlerdir.
145 | - `wwwroot`
146 | - Bu dizin, bizim statik dosyalarımızın bulunduğu alandır.
147 | - Js, css ve resim dosyaları burada bulunur.
148 | - Bu kısmı erişime açmak için `Startup.cs` dosyası içindeki `Configure` metodunun içine aşağıdaki kod satırı girilmelidir.
149 | - `app.UseStaticFiles();`
150 | - Bu dizin içine eklenen herhangi bir öğeye `~/` üzerinden ulaşabiliriz.
151 | - Örn : `wwwroot>img>01.jpg` dosyasını eklemek için;
152 | - `` olarak cshtml dosyasına ekleme yapılabilir.
153 | - `Dependencies`
154 | - Projeye eklenen paketlerin bulunduğu kısımdır.
155 | - Her paket yönetim programı için bir tane dizin oluşturulur.
156 | - Paket yönetim programlarının oluşturduğu dosya değiştirilirse veya direk olarak paketler kendi arayüzlerinden indirilirse, buraya otomatik olarak eklenir.
157 | - Buradan paketlerin üzerine tıklanarak kaldırma ve güncelleme işlemleri yapılabilir.
158 | - `Program.cs`
159 | - Programın ilk çalışmaya başladığı dosyadır.
160 | - Program çalıştırılınca, `Program.cs` içinde bulunan `Main()` metodu çalışır ve devamında ayarlar vs. yüklenerek program tam çalışır hale gelir.
161 | - `Startup.cs`
162 | - Program içindeki tüm ana ayarlamaların yapıldığı dosyadır.
163 | - İçinde temel olarak iki metot bulundurur.
164 | - `ConfigureServices()`
165 | - Program servislerinin ayarlamalarının yapıldığı alandır.
166 | - Web host, `Configure()` metodunu çağırmadan önce, ayarlamaları yapması için bu metodu çalıştırır.
167 | - Opsiyoneldir.
168 | - `Configure()`
169 | - Programa gelen isteklerin (request) işlenmesi için gerekli yol haritasının çizildiği alandır.
170 | - Zorunludur.
171 |
172 | ### 06 - Paket Yönetimi
173 |
174 | #### NuGet Package Manager
175 | - .NET Framework yapısında biz paketleri Nuget Manager ile indiriyorduk ve temel paketlerin hepsi ayrı ayrı indiriliyordu.
176 | - .NET Core ile birlikte, bu temel paketlerin hepsi tek bir paket altında toplanıp projeye dahil edilmiştir.
177 | - Bu paketi `Dependencies > NuGet > Microsoft.AspNetCore.All` altından görebiliriz.
178 | - Bu paketler internet üzerinden indirilmiş değildir, sadece referans olarak durmaktadırlar.
179 | - Proje içinde kullanılan paketler otomatik algılanıp, publish edildiğinde sadece bu paketlerin dll dosyaları projeye dahil edilecektir.
180 | - Bu paketlerin ayar dosyasını, projeye sağ tıklayıp `Edit .csproj` altında da düzenleyebiliriz.
181 |
182 | #### Bower Package Manager
183 | - Harici paketleri NuGet ile kurmanın yanında, .NET Core ile gelen başka bir paket yöneticisi olan `Bower` paket yöneticisiyle de paketleri kurabiliriz.
184 | - `Project > Manage Bower Packages...` yolunu kullanarak bower paketlerini yükleyebiliriz.
185 | - Bunun dışında manuel olarak da yükleme yapılabilir.
186 | - Bunun için yapmamız gereken, öncelikle `bower.json` dosyasını oluşturmaktır.
187 | - Bunu iki şekilde yapabiliriz:
188 | 1. Projeye sağ tıklayıp `Add > New Item > Bower Configuration File` yolunu takip ederek dosyayı dahil edebiliriz.
189 | 2. Projeye sağ tıklayıp `Add > New Item > Json File` kısmından `bower.json` isminde bir dosya ekliyoruz.
190 | - Dosyanın içine aşağıdaki örnek kodları ekliyoruz.
191 |
192 | ```json
193 | {
194 | "name": "asp.net",
195 | "private": true,
196 | "dependencies": {
197 | "bootstrap": "4.0.0"
198 | }
199 | }
200 | ```
201 |
202 | - Tüm kullanılabilir bower paketleri için : https://bower.io/search/
203 | - Dosyayı kaydettiğimiz zaman otomatik olarak paketler projeye eklenecektir.
204 | - Eklenen paketleri `Dependences > Bower` altından görebiliriz.
205 | - Projeye eklenen bower dosyaları otomatik olarak `wwwroot/lib` altında oluşturulur. Dosyaların yükleneceği yolu değiştirmek için ;
206 | - `bower.json` dosyasının altında `.bowerrc` dosyasının içinden `directory` kısmını değiştiriyoruz.
207 | - Eğer proje içinde bu dosya yoksa;
208 | - Projeye sağ tıklayıp `Add > New Item > Json File` kısmından `.bowerrc` isminde bir dosya ekliyoruz.
209 | - Dosyanın içine aşağıdaki örnek kodları ekliyoruz.
210 |
211 | ```json
212 | {
213 | "directory": "wwwroot/lib"
214 | }
215 | ```
216 |
217 | #### Node Package Manager (NPM)
218 | - Eklenecek.
219 |
--------------------------------------------------------------------------------
/21 - ASP.NET Core In-Depth - Proje Geliştime Mimarileri.md:
--------------------------------------------------------------------------------
1 | ## PROJE GELİŞTİRME MİMARİLERİ
2 |
3 | - Database modellemesi yapılırken ve bu yapılar proje içinde kullanılırken kullanacağımız bu yöntemler, hem proje yönetilebilirliğini arttırmak hem de kod tekrarını engellemek için kullanacağımız yöntemlerdir.
4 |
5 | ### 01 - Kurumsal Mimari Yapısıyla Core Projesi Oluştumak
6 |
7 | - Buradaki amaç, projemiz içindeki her katmanı ayrı bir library içinde kullanarak, projenin yönetilebilirliğini arttırmaktır.
8 | - Bunun için, bir solution içinde açamız gereken temelde 3 tane proje vardır. Bunların biri `CoreMVC`, diğer ikisi ise `Class Library`'dir.
9 | - Projeler oluşturulduktan sonra, her projenin kullanılan projeye göre birbirine referans vermesi gerekmektedir.
10 | - Migration işlemleri yapılırken, assembly yolunun WebUI içinde olduğunu ayar olarak belirtmemiz gerekmektedir.
11 |
12 | ```cs
13 | public void ConfigureServices(IServiceCollection services)
14 | {
15 | services.AddMvc();
16 | services.AddDbContext(
17 | option => option.UseSqlServer(
18 | Configuration.GetConnectionString("EfCoreDb"),
19 | b => b.MigrationsAssembly("Project.WebUI")
20 | ));
21 | }
22 | ```
23 |
24 | 1. **Project.WebUI** (Core MVC Projesi)
25 | - Bu proje, bizim çalışacak main projemizdir.
26 | - Diğer projelerden referans alır ve kullanır.
27 | - Projenin çalışması için bu projenin start-up project olarak seçilmesi gereklidir.
28 | 2. **Project.Entity** (Class Library)
29 | - İçinde db modellerimizin olduğu sınıftır.
30 | 3. **Project.Data** (Class Library)
31 | - Database bağlantılarının bulunduğu alandır.
32 | - Database bağlantısı yapılacağı için içinde Entity Framework Core kütüphanesi bulunmalıdır.
33 | - NPM içinden `Microsoft.EntityFrameworkCore.SqlServer` kütüphanesi kurulabilir.
34 | - NPM Console üzerinden `Install-Package Microsoft.EntityFrameworkCore.SqlServer` komutu çalıştırılarak kurulabilir.
35 | - `Project.Data.csproj` dosyası içindeki `` tagları içine aşağıdaki satır eklenebilir.
36 | - ``
37 | - ya da
38 | - ``
39 | - İçinde 2 tane dizin bulundurur.
40 | 1. **Abstract**
41 | - Interface'lerin tanımlandığı alandır.
42 | 2. **Concrete**
43 | - Database bağlantıları için yazılacak `Context` ve `Repository` sınıflarının olacağı klasördür.
44 | - Projeye dahil edilecek her bir db ve orm teknolojisi için ayrı bir klasör oluşturur.
45 | - Bu klasörler içinde bağlantı için gereken Context ve Repository sınıfları oluşturulur.
46 |
47 |
48 | ### 02 - Generik Class Yapılarıyla Entegrasyon
49 |
50 | - Generik sınıfların oluşturulması, kod tekrarını engellemek ve DRY prensibine uygun olarak kod yapıları oluşturmamızı sağlar.
51 | - Proje içinde `Interface`'lerin ve `Repository` sınıflarının içeriği genel olarak birbirini tekrar eder.
52 | - Bunu engellemek için iki yapı için de birer generik sınıf oluşturulur.
53 |
54 | #### Generik Intarface oluşturma
55 |
56 | - Her repository için ayrı ayrı Interface oluşturmak yerine, eğer tüm interface içerikleri aynı olacaksa, tek bir tane `Generic Interface` oluşturulabilir.
57 | - Diğer interface'ler bundan türetilir.
58 | - Eğer interface'e özel eklemeler yapılacaksa bunlar kendi içinde yapılabilir.
59 |
60 | ```cs
61 | public interface IGenericRepository where T:class
62 | {
63 | T GetByID(int ID);
64 | IQueryable GetAll();
65 | IQueryable Find(Expression> query);
66 |
67 | void Add(T entity);
68 | void Update(T entity);
69 | void Delete(int ID);
70 | void Save();
71 | }
72 |
73 | public interface IPersonRepository : IGenericRepository
74 | {
75 | IQueryable PersonsWithAddress { get; }
76 | }
77 |
78 | public interface IAddressRepository : IGenericRepository
79 | {
80 | IQueryable AddressesWithPeople { get; }
81 | }
82 | ```
83 |
84 | #### Generik Repository oluşturma
85 |
86 | - Aşağıda da görüldüğü gibi, generi interface işlemleri aynı olduğu gibi, bunların repository işlemleri de aynı. Bu nedenle bu interface içindeki ifadelerin repo işlemleri generik bir sınıf içinde yapılmıştır.
87 | - **NOT:** Bu generik sınıf içinde kullanılan database sınıfının özel olmasına gerek yok. O yüzden bizim db bağlantı sınıfımız olan `EfProjectContext` sınıfını değil, bu sınıfın kalıtım aldığı ve EF içinden gelen `DbContext` sınıfını kullanmıştır.
88 | - Bu generik sınıfı kullanan diğer özel sınıflardan bu kullanılan `DbContext` ifadesi için ctor metot içinde `EfProjectContext` yapısı gönderilmiştir.
89 | - Bu saydede özel sınıf içine inherit edilen generik sınıf metotları bu özel db class yapısını kullanır.
90 | - Özel sınıflar içinde db işlemleri kullanılırken, generik sınıftan inherit edilen `context` nesnesi kullanılabileceği gibi, bu nesne farklı bir nesneye tür dönüşümü yapılarak atanıp da kullanılabilir.
91 | - Yeni bir nesneye atanmadan;
92 | - `context.Set().Include("Address");`
93 | - Yeni bir nesneye atanırsa;
94 | - `efProjectContext.Persons.Include("Address");`
95 | - Yeni nesneye atama yapılması, bu yapıyı kullanırken daha efektif kullanmamızı sağlar.
96 |
97 | ```cs
98 | public class EfGenericRepository : IGenericRepository where T : class
99 | {
100 | protected readonly DbContext context;
101 |
102 | public EfGenericRepository(DbContext ctx)
103 | => context = ctx;
104 |
105 | public T GetByID(int ID)
106 | => context.Set().Find(ID);
107 |
108 | public IQueryable Find(Expression> query)
109 | => context.Set().Where(query);
110 |
111 | public IQueryable GetAll()
112 | => context.Set();
113 |
114 | public void Add(T entity)
115 | => context.Set().Add(entity);
116 |
117 | public void Delete(int ID)
118 | => context.Set().Remove(GetByID(ID));
119 |
120 | public void Update(T entity)
121 | => context.Set().Update(entity);
122 |
123 | public void Save()
124 | => context.SaveChanges();
125 | }
126 |
127 | public class EfPersonRepository : EfGenericRepository, IPersonRepository
128 | {
129 | public EfPersonRepository(EfProjectContext ctx)
130 | : base(ctx) { }
131 |
132 | public EfProjectContext efProjectContext
133 | { get { return context as EfProjectContext; } }
134 |
135 | public IQueryable PersonsWithAddress
136 | => efProjectContext.Persons.Include(k => k.Address);
137 | }
138 |
139 | public class EfAddressRepository : EfGenericRepository, IAddressRepository
140 | {
141 | public EfAddressRepository(EfProjectContext ctx)
142 | : base(ctx) { }
143 |
144 | public EfProjectContext _EfProjectContext
145 | { get { return context as EfProjectContext; } }
146 |
147 | public IQueryable AddressesWithPeople
148 | => _EfProjectContext.Addresses.Include(k => k.Persons);
149 | }
150 | ```
151 |
152 | ### 03 - Unit Of Work Pattern
153 |
154 | - Bu yapının temel olarak amaçları;
155 | - Controller içine repoları ayrı ayrı DI yapmak yerine, tek bir yapı altında toplayıp enjekte etmek.
156 | - Ayrı ayrı enjekte edilen yapıların ayrı ayrı DbContext öğeleri oluşturmak yerine, sadece bir tane nesne oluşturup bunun üzerinde işlemler yapmak.
157 | - Burada asıl olarak yaptığımız, tüm repository öğelerini tek bir öğe halinde birleştirmek ve bu öğenin hizmetine tek bir tane DbContext öğesi atamaktır.
158 |
159 | |
Wihtout UoW
|
With UoW
|
160 | | --- | --- |
161 | |
|
|
162 |
163 | #### Interface yapılandırılması
164 |
165 | - Oluşturacağımız interface,
166 | - Proje içinde kullandığımız sanal interface tablolarını alan,
167 | - Dispose edilebilir,
168 | - İçinde tüm işlemler bittikten sonra db kaydını gerçekleştirebileceğimiz bir SaveChanges metodu bulunduran bir yapıda olmalıdır.
169 |
170 | ```cs
171 | interface IUnitOfWork : IDisposable
172 | {
173 | IPersonRepository People { get; }
174 | IAddressRepository Addresses { get; }
175 |
176 | int SaveChanges();
177 | }
178 | ```
179 |
180 | #### Interface üzerinden sınıf türetilmesi
181 |
182 | - Öncesinde oluşturduğumuz interface'i, mevcut kullandığımız db yapısına uyarlamak için yeni bir class oluşturuyoruz.
183 | - Bu yeni class içinde;
184 | - Mevcut database yapımızı DI ile enjekte ediyoruz.
185 | - Bu yapıyı kullanacak her controller, sınıf içinde birden fazla repo oluşturmasın diye, her repodan bir private nesne oluşturuyoruz ve her nesne için bir `get` property'si oluşturup bu field nesnelerini gönderiyoruz.
186 | - Bu propery'ler oluşturulurken, ilk ulaşıldığında field alanları null olacağından dolayı, bu field'lar için yeni bir nesne oluşturup gönderiyoruz.
187 | - Daha sonrasında db kaydı için `SaveChanges` metodunu ve işimiz bittikten sonra bunu silmek için `Dispose` metodunu dolduruyoruz.
188 |
189 | ```cs
190 | public class EfUnitOfWork : IUnitOfWork
191 | {
192 | // Database
193 | private readonly EfProjectContext context;
194 |
195 | public EfUnitOfWork(EfProjectContext _context)
196 | => context = _context;
197 |
198 | // Fields
199 | private IPersonRepository _people;
200 | private IAddressRepository _addresses;
201 |
202 | // Properties
203 | public IPersonRepository People
204 | => _people ?? (_people = new EfPersonRepository(context));
205 |
206 | public IAddressRepository Addresses
207 | => _addresses ?? (_addresses = new EfAddressRepository(context));
208 |
209 | // Functions
210 | public int SaveChanges()
211 | => context.SaveChanges();
212 |
213 | public void Dispose()
214 | => context.Dispose();
215 | }
216 | ```
217 |
218 | #### Yapıların birbirine bağlanması
219 |
220 | - Repolarda olduğu gibi, `Startup.cs` dosyası içinde bu iki yapının birbirine bağlanması lazım.
221 |
222 | ```cs
223 | services.AddTransient();
224 | ```
225 |
226 | > **NOT!** Tüm repoları ve tüm interface'leri tek bir çatı altına toplayıp `Unit of Work` yapısıyla birbirine bağladığımız ve kullanacağımız alanlarda **diğer interfaceleri değil de UoW interface'ini enjekte edeceğimiz için** `Startup.cs` dosyası içindeki diğer bağlantıları kaldırabiliriz.
227 |
228 | ```cs
229 | // Öncesi
230 | services.AddTransient();
231 | services.AddTransient();
232 | ```
233 |
234 | ```cs
235 | // Sonrası
236 | services.AddTransient();
237 | ```
238 |
239 | #### Son durum
240 |
241 | UoW öncesi Controller:
242 |
243 | ```cs
244 | public class Home2Controller : Controller
245 | {
246 | private IPersonRepository person;
247 | private IAddressRepository address;
248 |
249 | public Home2Controller(IPersonRepository _person, IAddressRepository _address)
250 | {
251 | person = _person;
252 | address = _address;
253 | }
254 |
255 | public IActionResult People()
256 | => View(person.PersonsWithAddressWithCountry);
257 |
258 | public IActionResult Addresses()
259 | => View(address.GetAll());
260 |
261 | public IActionResult AddPerson(Person model)
262 | {
263 | person.Add(model);
264 | person.Save();
265 | return RedirectToAction("People");
266 | }
267 | }
268 | ```
269 |
270 | UoW sonrası Controller :
271 |
272 | ```cs
273 | public class Home2Controller : Controller
274 | {
275 | private IUnitOfWork uow;
276 |
277 | public Home2Controller(IUnitOfWork _uow)
278 | => uow = _uow;
279 |
280 | public IActionResult People()
281 | => View(uow.People.PersonsWithAddressWithCountry);
282 |
283 | public IActionResult Addresses()
284 | => View(uow.Addresses.GetAll());
285 |
286 | public IActionResult AddPerson(Person model)
287 | {
288 | uow.People.Add(model);
289 | uow.SaveChanges();
290 | return RedirectToAction("People");
291 | }
292 | }
293 | ```
294 |
295 |
296 | ### 04 - Onion Architecture
297 |
298 | - Katmanların iç içe olduğu ve her katmanın sadece kendi üstündeki katmanı gördüğü tasarım mimarisidir.
299 |
300 |
301 |
302 |
--------------------------------------------------------------------------------
/12 - Durum Yönetimi.md:
--------------------------------------------------------------------------------
1 | ## DURUM YÖNETİMİ
2 |
3 | - HTTP, stateless bir protokoldür.
4 | - Kullanılan web server, her bir request'i ayrı ve birbirinden bağımsız olarak oluşturur ve önceki request üzerindeki veriler ve user bilgileri kullanılamaz.
5 | - Bu yüzden, belirli bir kullanıcıya ait bilgileri o kullanıcıya özel saklamak veya tüm isteklerin kullanabileceği bir havuz yapısı oluşturmak için, Session ve Cookie bağımlı yapılar kullanarak durum yönetimi yapabiliriz.
6 |
7 | ### 01 - Session
8 |
9 | - Sunucunun REMinde tutulur.
10 | - Her kullanıcı için bir ID oluşturur ve tutulacak verileri bu ID üzerinden saklar.
11 | - Bu oluşturulan ID’ler şifrelenerek kullanıcının bilgisayarında cookie olarak tutulur.
12 | - Farklı tarayıcılarda farklı kullanıcı gibi davranır.
13 | - Belirli bir süresi vardır, ayarlanabilir. Default : 20dk
14 | - Bu süre son request'ten sonrası için geçerlidir. (Sliding time)
15 | - Session nesnesi içine eskiden obje atayabilirken, Core 2.0 üzerinde sadece primitive değişkenler saklayabiliyoruz(int, string vb)
16 | - Bu durumu aşmak için model yapıları json nesnesine çevrilip saklanabilir.
17 | - Bu durumlar için bir kütüphane oluşturulup kullanılabilir.
18 | - .NET Core içinde session kullanılacaksa bunu aktifleştirmek gerekir. Bunu Startup.cs dosyası içinden;
19 | - Aktifleştirme yapılırken `UseSession` yapısının `UseMvc` yapısından önce kullanılmasına dikkat edilmelidir.
20 | - Aksi halde hata alınır.
21 |
22 | ```cs
23 | public void ConfigureServices(IServiceCollection services)
24 | {
25 | services.AddMemoryCache();
26 | services.AddSession();
27 | }
28 |
29 | public void Configure(IApplicationBuilder app, IHostingEnvironment env)
30 | {
31 | app.UseSession();
32 | app.UseMvcWithDefaultRoute();
33 | }
34 | ```
35 |
36 | - Aktifleştime yapılırken, Session ayarlamaları da eklenebilir:
37 | - Buradaki `IdleTimeout` ile, Session süresi ayarlanabilir.
38 | - `TimeSpan` olarak belirtilir.
39 | - Session middleware'i üzerinden geçen her request bu zamanı sıfırlar.
40 | - `options.Cookie` üzerinden, session'ın oluşturduğu cookie ile ilgili ayarlamalar yapılabilir.
41 |
42 | ```cs
43 | services.AddSession(options => {
44 | options.Cookie.HttpOnly = true;
45 | options.IdleTimeout = new TimeSpan(0, 0, 10);
46 | options.Cookie.Name = ".AdventureWorks.Session";
47 | });
48 | ```
49 |
50 | #### Session kullanımı
51 |
52 | - Session oluşturma:
53 | - `HttpContext.Session.Set (string key, byte[] value)`
54 | - `HttpContext.Session.SetString(string key, string value)`
55 | - `HttpContext.Session.SetInt32 (string key, int value)`
56 | - Session çekme:
57 | - `byte[] HttpContext.Session.Get(string key)`
58 | - `bool HttpContext.Session.TryGetValue(string key, out byte[] value)`
59 | - `string HttpContext.Session.GetString(string key)`
60 | - `int? HttpContext.Session.GetInt32(string key)`
61 | - Sessionların silinmesi
62 | - `void HttpContext.Session.Remove(string key)`
63 | - `void HttpContext.Session.Clear()`
64 |
65 | #### Modellerin Json yapısına çevrilip session yönetiminde kullanılması
66 |
67 | ```cs
68 | // Library/Extensions/SessionExtensions.cs
69 | using Microsoft.AspNetCore.Http;
70 | using Newtonsoft.Json;
71 |
72 | public static class SessionExtensions
73 | {
74 | public static void SetJson(this ISession session, string key, T value)
75 | {
76 | session.SetString(key, JsonConvert.SerializeObject(value));
77 | }
78 |
79 | public static T GetJson(this ISession session, string key)
80 | {
81 | var value = session.GetString(key);
82 | return value == null ? default(T) :
83 | JsonConvert.DeserializeObject(value);
84 | }
85 | }
86 | ```
87 |
88 | ```cs
89 | // Controller
90 | using System.Collections.Generic;
91 | using Microsoft.AspNetCore.Http;
92 | using Microsoft.AspNetCore.Mvc;
93 | using Project.Library.Extensions;
94 | using Project.Models;
95 | public class SessionController : Controller
96 | {
97 | public IActionResult Index()
98 | {
99 | // FakeData ile örnek bir model listesi oluşturma
100 | List list = new List();
101 |
102 | for (int i = 0; i < 20; i++)
103 | list.Add(new Person {
104 | Name = FakeData.NameData.GetFirstName(),
105 | Surname = FakeData.NameData.GetSurname()
106 | });
107 |
108 | // Listeyi session üzerine kaydetme
109 | HttpContext.Session.SetJson("people", list);
110 |
111 | return RedirectToAction("Index2");
112 | }
113 |
114 | public IActionResult Index2()
115 | {
116 | // Session'ı çekme ve modele dönüştürme
117 | // Eğer gelen değer null ile boş bir liste gönderme
118 | List list = HttpContext.Session.GetJson>("people")
119 | ?? new List();
120 | return View(list);
121 | }
122 | }
123 | ```
124 |
125 | ### 02 - Cookie
126 |
127 | - String türünde değişkenler saklar.
128 | - Cookie'ler kullanıcının bilgisayarında saklanır.
129 | - Kullanıcının bilgisayarında saklandığı ve görülebildiği için hassas verilerin saklanmaması gereklidir.
130 | - Eğer önemli veriler saklanacaksa ve bu verilerin görünmesi istenmiyorsa, şifrelenerek gönderilebilir.
131 | - Ömürleri dolana kadar browser tarafından saklanmaya devam eder
132 | - Her request ve response üzerinde taşınır.
133 | - Bu taşınmanın performansının düşmemesi için cookie boyutunu minimum tutmak önemlidir.
134 | - Genellikle kullanıcıya özel bilgiler server üzerinde barındırılıp, bunu tanımlayan unique bir değer cookie ile kullanıcıya gönderilir.
135 | - Çoğu browser, cookie boyutunu 4096 byte olarak sınırlandırmıştır.
136 | - Eklenme için `Response` okuma için ise `Request` objeleri kullanılır.
137 |
138 | #### Cookie Kullanımı
139 |
140 | - Cookie oluşturma:
141 | - `void Response.Cookies.Append(string key, string value);`
142 | - `void Response.Cookies.Append(string key, string value, CookieOptions options);`
143 |
144 | ```cs
145 | Response.Cookies.Append("name", "serhat", new CookieOptions
146 | {
147 | HttpOnly = true,
148 | Expires = DateTime.Now.AddDays(1)
149 | });
150 | ```
151 |
152 | - CookieOptions :
153 | - **Domain** - Cookie ile birleştirilmek istenen domain ismi
154 | - **Path** - Cookie yolu
155 | - **Expires** - Cookie silinme zamanı
156 | - **HttpOnly** - Client-Side scriptlerin cookie kullanıp kullanmayacağının ayarlanması
157 | - **Secure** - Sadece HTTPS üzerinden çalışmayı desteklemek için true yapılabilir.
158 |
159 | - Cookie Okuma:
160 | - Controller üzerinden :
161 | - `Request.Cookies[]`
162 | - View üzerinden :
163 | - `Context.Request.Cookies[]`
164 | - Herhangi bir dış class veya middleware üzerinden :
165 | - https://stackoverflow.com/questions/38571032/how-to-get-httpcontext-current-in-asp-net-core
166 |
167 | ```cs
168 | // External Class :
169 | public class MyCookieExp
170 | {
171 | public static string ReadCookie(HttpContext context, string key)
172 | {
173 | return context.Request.Cookies[key];
174 | }
175 | }
176 |
177 | // Controller :
178 | var cookie = MyCookieExp.ReadCookie(HttpContext, "name");
179 | ```
180 |
181 | - Cookie Silme:
182 | - `Response.Cookies.Delete()`
183 |
184 | #### Önemli bir not!
185 |
186 | > İnternet standartlarına göre request headers içerisinde non-ascii değerlerin taşınması uygun görülmemektedir. Bu tür bir zorunluluk taşıyorsanız ilgili değerleri encode-decode yöntemleri ile işleyebilirsiniz.
187 |
188 | - Bu sebepten ötürü, .NET Core Cookies içinde ASCII değerlerine uygun olmayan karakterler taşındığında, request Controller yapısına uğramadan direk 400 hatası döndürür.
189 |
190 | ### 03 - Cache
191 |
192 | - Caching mekanizması, herhangi bir veriyi cevap olarak daha hızlı döndürmek için, bu veriyi RAM üzerinde saklama ve gerektiğinde geri döndürme işlemidir.
193 | - Ayrıntılı bilgi için şu kaynağa bakılabilir:
194 | - https://www.devtrends.co.uk/blog/a-guide-to-caching-in-asp.net-core
195 | - Globaldir. Her kullanıcı aynı bilgileri görür.
196 | - Statik değişkenlerden farkı, burada süre belirtebiliyoruz.
197 | - İsteğimiz dışımızda, server alan açmaya zorlandığında yine bu cache’ler silinebilir.
198 | - Bu silinmeler önceliğe göre yapılır.
199 | - ASP.NET Core içinde caching mekanizaması bir servis olarak bulunmaktadır.
200 | - Bu nedenle bu servisi kullanmadan önce `Startup.cs` içine eklemek gerekmektedir.
201 |
202 | ```cs
203 | public void ConfigureServices(IServiceCollection services)
204 | {
205 | services.AddMvc();
206 | services.AddMemoryCache();
207 | }
208 | ```
209 |
210 | - Caching mekanizmasını kullanacağımız herhangi bir yerde, bu servis DI ile enjekte edilip kullanılır.
211 |
212 | ```cs
213 | private IMemoryCache cache;
214 | public IDGCacheController(IMemoryCache cache)
215 | {
216 | this.cache = cache;
217 | }
218 | ```
219 |
220 | #### Cache oluşturma
221 |
222 | ```cs
223 | public static TItem Set(this IMemoryCache cache, object key, TItem value);
224 | public static TItem Set(this IMemoryCache cache, object key, TItem value, DateTimeOffset absoluteExpiration);
225 | public static TItem Set(this IMemoryCache cache, object key, TItem value, TimeSpan absoluteExpirationRelativeToNow);
226 | public static TItem Set(this IMemoryCache cache, object key, TItem value, IChangeToken expirationToken);
227 | public static TItem Set(this IMemoryCache cache, object key, TItem value, MemoryCacheEntryOptions options);
228 | ```
229 |
230 | #### Cache Options
231 |
232 | ```cs
233 | public DateTimeOffset? AbsoluteExpiration { get; set; }
234 | //
235 | // Summary:
236 | // Gets or sets an absolute expiration time, relative to now.
237 | public TimeSpan? AbsoluteExpirationRelativeToNow { get; set; }
238 | //
239 | // Summary:
240 | // Gets or sets how long a cache entry can be inactive (e.g. not accessed) before
241 | // it will be removed. This will not extend the entry lifetime beyond the absolute
242 | // expiration (if set).
243 | public TimeSpan? SlidingExpiration { get; set; }
244 | //
245 | // Summary:
246 | // Gets the Microsoft.Extensions.Primitives.IChangeToken instances which cause the
247 | // cache entry to expire.
248 | public IList ExpirationTokens { get; }
249 | //
250 | // Summary:
251 | // Gets or sets the callbacks will be fired after the cache entry is evicted from
252 | // the cache.
253 | public IList PostEvictionCallbacks { get; }
254 | //
255 | // Summary:
256 | // Gets or sets the priority for keeping the cache entry in the cache during a memory
257 | // pressure triggered cleanup. The default is Microsoft.Extensions.Caching.Memory.CacheItemPriority.Normal.
258 | public CacheItemPriority Priority { get; set; }
259 | //
260 | // Summary:
261 | // Gets or sets the size of the cache entry value.
262 | public long? Size { get; set; }
263 | ```
264 |
265 | #### Cache okuma
266 |
267 | ```cs
268 | public static object Get(this IMemoryCache cache, object key);
269 | public static TItem Get(this IMemoryCache cache, object key);
270 |
271 | public static bool TryGetValue(this IMemoryCache cache, object key, out TItem value);
272 |
273 | public static TItem GetOrCreate(this IMemoryCache cache, object key, Func factory);
274 | [AsyncStateMachine(typeof(CacheExtensions.d__9<>))]
275 | public static Task GetOrCreateAsync(this IMemoryCache cache, object key, Func> factory);
276 | ```
277 |
278 | - Basit olarak bir cache kontrolü, yoksa oluşturulması, varsa geri döndürülmesi şu şekilde oluşturulur:
279 |
280 | ```cs
281 | public string GetCacheValue(){
282 | string key ="IDGKey";
283 | string obj;
284 |
285 | if (!cache.TryGetValue(key, out obj))
286 | {
287 | obj = DateTime.Now.ToString();
288 | cache.Set(key, obj);
289 | }
290 |
291 | return obj;
292 | }
293 | ```
294 |
295 | - Aynı işlemler daha basit olarak `GetOrCreate()` metoduyla da yapılabilir.
296 | - Bu metot varsa çeker, yoksa oluşturup geri döndürür.
297 |
298 | ```cs
299 | public string Get()
300 | {
301 | return cache.GetOrCreate(“IDGKey”,
302 | cacheEntry => {
303 | return DateTime.Now.ToString();
304 | });
305 | }
306 | ```
307 |
308 | #### External class içinde cache kullanımı
309 |
310 | - Diğerlerinden olduğu gibi, `IMemoryCache` interface'i parametre olarak verilir ve kullanılıldığı controller üzerinde DI ile enjekte edilip, metoda parametre olarak verilir.
311 |
312 | ```cs
313 | // External class :
314 | public class MyCacheExp
315 | {
316 | public static T GetCache(IMemoryCache cache, string key)
317 | => cache.Get(key);
318 | }
319 |
320 | // Controller :
321 | ViewBag.date = MyCacheExp.GetCache(cache, "date");
322 | ```
323 |
324 | #### Cache Silme
325 |
326 | ```cs
327 | public IActionResult Delete()
328 | {
329 | cache.Remove("date");
330 | return RedirectToAction("Index");
331 | }
332 | ```
333 |
334 | ### Diğer Durum Yönetim Yöntemleri
335 |
336 | - Yukarıdaki yöntemler dışında, bilgileri sayfalar arasında aktarma işlemleri yaparken kullanabileceğimiz yöntemler şunlardır :
337 | - TempData
338 | - QueryString
339 | - PostData içinde Hidden alanlar
340 | - Statik değişkenler kullanma
341 | - Dosyalara ve Database'e kayıt
--------------------------------------------------------------------------------
/20 - ASP.NET Core In-Depth - Middleware.md:
--------------------------------------------------------------------------------
1 | ## MIDDLEWARE
2 |
3 | - Oluşturulan Middleware'ler, `Request`'ten `Response`'a kadar olan döngü arasına yazılan ve bu ikisi arasında manipülasyon işlemleri yapan elemanlardır.
4 | - Her bir component;
5 | - Pipeline üzerinde, sonraki elemana geçiş yapılmasını sağlayabilir veya geçişi kesebilir.
6 | - Sonra gelen component öncesinde ve sonrasında işlem yapılmasını sağlayabilir. (Encapsulation)
7 | - `RequestDelegate`, Request pipeline kurulmasında görevlidir.
8 | - Her gelen HTTP isteğiyle `RequestDelegate` ilgilenir.
9 | - `RequestDelegate` için tanımlanan `Run`, `Map` ve `Use` extension metotlarıyla, `In-Line` veya `Reusable Class` kullanılarak middleware eklemesi yapılabilir.
10 | - Sonraki pipeline'a geçiş işlemleri bu sınıf üzerinden yapılır.
11 | - Her middleware, pipeline üzerinde kendisinden sonra gelen componeti çalıştırmak veya `short-circuiting` ile direk response cevabı vermek ile görevlidir.
12 |
13 | ### 01 - IApplicationBuilder ile Middleware Pipeline oluşturma
14 |
15 |
16 |
17 |
18 |
19 | - Core üzerinde middleware'ler, `IApplicationBuilder` ile düzenlenir.
20 | - Bu yapının düzenlenmesi, `Starup.cs` içindeki `Configure` metodunda olur.
21 | - Yukarıdaki şekilden de görüleceği üzere, her middleware, kendisinden sonra gelen yapıdan önce ve sonra çalışır (kapsar).
22 | - Ayrıca her middleware, kendisinden sonra gelecek yapının çalışıp çalışmayacağına karar verir.
23 | - Bu karar yapısı, `next()` fonksiyonunun çalışıp çalışmamasıyla alakalıdır.
24 | - Eğer yapıda short-circuit isteniyorsa, next metodu çalıştırılmaz ve başka bir yönlendirme yapılır.
25 |
26 | #### Middleware Ekleme : IApplicationBuilder.Run()
27 |
28 | - `Run()` metoduyla eklenen middleware, pipeline'ı sonlandırır.
29 | - Bundan dolayı içinde `next()` metodu bulundurmaz.
30 |
31 | ```cs
32 | using Microsoft.AspNetCore.Builder;
33 | using Microsoft.AspNetCore.Hosting;
34 | using Microsoft.AspNetCore.Http;
35 |
36 | public class Startup
37 | {
38 | public void Configure(IApplicationBuilder app)
39 | {
40 | app.Run(async context =>
41 | {
42 | await context.Response.WriteAsync("Hello, World!");
43 | });
44 | }
45 | }
46 |
47 | ```
48 |
49 | #### Middleware Ekleme : IApplicationBuilder.Use()
50 |
51 | - Birden fazla middleware'i birbirine bağlamak için kullanılır.
52 | - `next()` metoduyla kendinden sonra gelen componenti çalıştırabilir.
53 |
54 | ```cs
55 | public class Startup
56 | {
57 | public void Configure(IApplicationBuilder app)
58 | {
59 | app.Use(async (context, next) =>
60 | {
61 | // Do work that doesn't write to the Response.
62 | await next.Invoke();
63 | // Do logging or other work that doesn't write to the Response.
64 | });
65 |
66 | app.Run(async context =>
67 | {
68 | await context.Response.WriteAsync("Hello from 2nd delegate.");
69 | });
70 | }
71 | }
72 | ```
73 |
74 | - Buradaki `next` parametresi öncesinde yazılan kodlar, sonra gelecek olan component çalıştırılmadan önce; `next` parametresi çalıştrıldıktan sonra yazılan kodlar, sonraki component çalıştırıldıktan sonra çalışır.
75 | - Başka bir deyişle **next parametresi öncesindeki kodlar `request` oluştuktan sonra, next parametresi sonrasındakiler ise `response` oluştuktan sonra** çalışır.
76 | - Bu yapıyla, önceki middleware'in, sonrakini **kapsadığını** anlayabiliriz.
77 | - Örnek vermek gerekirse;
78 |
79 | ```cs
80 | public class Startup
81 | {
82 | public void Configure(IApplicationBuilder app)
83 | {
84 | app.Use(async (context, next) =>
85 | {
86 | Fn.LogYaz("Before Middleware 1");
87 | await next.Invoke();
88 | Fn.LogYaz("After Middleware 1");
89 | });
90 |
91 | app.Use(async (context, next) =>
92 | {
93 | Fn.LogYaz("Before Middleware 2");
94 | await next.Invoke();
95 | Fn.LogYaz("After Middleware 2");
96 | });
97 | }
98 | }
99 | ```
100 |
101 | ```
102 | # ÇIKTI
103 | Startup sınıfı oluşturuldu.
104 | ConfigureServices metodu çalıştı.
105 | Configure metodu çalıştı.
106 | Main metodu çalıştı.
107 |
108 | Before Middleware 1
109 | Before Middleware 2
110 | After Middleware 2
111 | After Middleware 1
112 | ```
113 |
114 | > **NOT** : Middleware'ler next parametreleriyle en son middleware'e ulaşınca `Response` oluşur ve middleware üzerinden geriye doğru tekrar geçer. Bu geriye geçişte Response üzerinde değişiklik yapılamaz. Yapılırsa değişiklik değişmez veya hata alınır. Bunu kontrol etmenin en iyi yolu `context.Response.HasStarted` ile kontrol sağlamaktır.
115 |
116 | ```cs
117 | public class Startup
118 | {
119 | public void Configure(IApplicationBuilder app)
120 | {
121 | app.Use(async (context, next) =>
122 | {
123 | Fn.LogYaz("Before Middleware");
124 |
125 | if (context.Response.HasStarted)
126 | Fn.LogYaz("Response oluştu!");
127 | else
128 | Fn.LogYaz("Response bekliyor.");
129 |
130 | context.Response.Headers.Add("exp1", "value1");
131 |
132 | await next.Invoke();
133 |
134 | Fn.LogYaz("After Middleware");
135 |
136 | if (context.Response.HasStarted)
137 | Fn.LogYaz("Response oluştu!");
138 | else
139 | Fn.LogYaz("Response bekliyor!");
140 |
141 | context.Response.Headers.Add("exp2", "value2");
142 | });
143 | }
144 | }
145 | ```
146 |
147 | ```
148 | # ÇIKTI
149 | Before Middleware
150 | Response bekliyor.
151 | After Middleware
152 | Response oluştu!
153 |
154 | # HEADERS
155 | exp1 : value1
156 | ```
157 |
158 | #### Middleware Ekleme : IApplicationBuilder.Map() ve IApplicationBuilder.MapWhen()
159 |
160 | - `Map*`, verilen request url path ile, birbirinden farklı middleware çalıştırmamıza olanak sağlayan bir yapıdır.
161 | - Verilen url path kısmı ile **başlayan** tüm url isteklerinde, belli bir fonksiyonun middleware olarak kullanılması sağlanabilir.
162 |
163 | ```cs
164 | public class Startup
165 | {
166 | private static void HandleMapTest1(IApplicationBuilder app)
167 | {
168 | app.Run(async context =>
169 | {
170 | await context.Response.WriteAsync("Map Test 1");
171 | });
172 | }
173 |
174 | private static void HandleMapTest2(IApplicationBuilder app)
175 | {
176 | app.Run(async context =>
177 | {
178 | await context.Response.WriteAsync("Map Test 2");
179 | });
180 | }
181 |
182 | public void Configure(IApplicationBuilder app)
183 | {
184 | app.Map("/map1", HandleMapTest1);
185 |
186 | app.Map("/map2", HandleMapTest2);
187 |
188 | app.Run(async context =>
189 | {
190 | await context.Response.WriteAsync("Hello from non-Map delegate.
");
243 | });
244 | }
245 | }
246 |
247 | // RESULT
248 | // Request Response
249 | // localhost:1234 Hello from non-Map delegate.
250 | // localhost:1234/?branch=master Branch used = master
251 | ```
252 |
253 | - `Map` fonksiyonu nesting yapısını destekler:
254 |
255 | ```cs
256 | app.Map("/level1", level1App => {
257 | level1App.Map("/level2a", level2AApp => {
258 | // "/level1/level2a"
259 | //...
260 | });
261 | level1App.Map("/level2b", level2BApp => {
262 | // "/level1/level2b"
263 | //...
264 | });
265 | });
266 | ```
267 |
268 | ### 02 - Hazır Middleware Yapıları
269 |
270 | - Dotnet Core içinde gelen hazır middleware yapılarına [burdan](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/middleware/?view=aspnetcore-2.0&tabs=aspnetcore2x#built-in-middleware) ulaşılabilir.
271 | - Burda bilinmesi gereken önemli nokta, bu yapılar eklenirken sırasına dikkat edilmesidir.
272 | - Listede verilen sıralamaya göre eklenmesi gerekir.
273 |
274 | ### 03 - External Class Yapısında Middleware Oluşturma ve Kullanma
275 |
276 | - Middleware olarak kullanılabilecek iki çeşit class yapısı aşağıdaki gibidir:
277 |
278 | ```cs
279 | using Microsoft.AspNetCore.Http;
280 | using System.Globalization;
281 | using System.Threading.Tasks;
282 |
283 | namespace Culture
284 | {
285 | // Create Middleware (Request only)
286 | public class RequestCultureMiddleware
287 | {
288 | private readonly RequestDelegate _next;
289 |
290 | public RequestCultureMiddleware(RequestDelegate next)
291 | {
292 | _next = next;
293 | }
294 |
295 | public Task InvokeAsync(HttpContext context)
296 | {
297 | // Request Codes...
298 |
299 | // Call the next delegate/middleware in the pipeline
300 | return this._next(context);
301 | }
302 | }
303 |
304 | // Create Middleware (Request and Response)
305 | public class RequestCultureMiddleware
306 | {
307 | private readonly RequestDelegate _next;
308 |
309 | public RequestCultureMiddleware(RequestDelegate next)
310 | {
311 | _next = next;
312 | }
313 |
314 | public async Task InvokeAsync(HttpContext context)
315 | {
316 | // Request Codes...
317 |
318 | // Call the next delegate/middleware in the pipeline
319 | await this._next(context);
320 |
321 | // Response Codes...
322 | }
323 | }
324 |
325 | // Add the middleware to IApplicationBuilder as extension method (optional)
326 | public static class RequestCultureMiddlewareExtensions
327 | {
328 | public static IApplicationBuilder UseRequestCulture(
329 | this IApplicationBuilder builder)
330 | {
331 | return builder.UseMiddleware();
332 | }
333 | }
334 | }
335 | ```
336 |
337 | - Middleware oluşturulduktan sonra direk `Configure` içinde kullanılabileceği gibi, `IApplicationBuilder` içine extension metot olarak eklenip de kullanılabilir.
338 | - Bu yapılar :
339 |
340 | ```cs
341 | public class Startup
342 | {
343 | public void Configure(IApplicationBuilder app)
344 | {
345 | // Use directly
346 | app.UseMiddleware();
347 |
348 | // Use as extension method
349 | app.UseRequestCulture();
350 |
351 | app.Run(async (context) =>
352 | {
353 | await context.Response.WriteAsync(
354 | $"Hello {CultureInfo.CurrentCulture.DisplayName}");
355 | });
356 |
357 | }
358 | }
359 | ```
360 |
361 | ### 04 - Middleware Yapılarında Dependency Injection
362 |
363 | #### Conventional Middleware Activation
364 |
365 | - Middleware classları, **program başlangıcında**, configuration ayarları bittikten sonra oluşturulur.
366 | - Oluşum esnasında constructor metotlar çalıştırılıp nesne türetilir, fakat `invoke()` metodu, requestler geldiğinde çalıştırılır.
367 | - Her request için değil, program başlangıcında nesneler türetildiği için, `scoped` ömrüne sahip servisler constructor metotlar ile enjekte edilemez. Bunun yerine `Invoke()` metoda `parametre` olarak verilip alınabilir.
368 |
369 | ```cs
370 | public class RequestCultureMiddleware
371 | {
372 | private readonly RequestDelegate _next;
373 |
374 | public RequestCultureMiddleware(RequestDelegate next)
375 | {
376 | _next = next;
377 | }
378 |
379 | public async Task InvokeAsync(HttpContext context, IUnitOfWork db)
380 | {
381 | db.Logs.Add(new Models.Log{ LogName = "Example Log" });
382 | db.SaveChanges();
383 |
384 | await _next(context);
385 | }
386 | }
387 | ```
388 |
389 | #### Factory-Based Middleware Activation
390 |
391 | - `IMiddleware` interface'i ile oluşturulmuş middleware yapılarıdır.
392 | - Avantajları:
393 | - Her request ile birlikte tekrar oluşturulurlar.
394 | - Her request oluşturulduğunda constructor metodu tekrar çalıştırılır.
395 | - `scoped` ömrüne sahip servisler, constructor metot içinde enjekte edilebilirler.
396 | - Factory-Based middleware kullanımı için, kullanmadan önce bu sınıfın servis olarak eklenmesi gerekmektedir.
397 |
398 | ```cs
399 | // Create Factory-Based Middleware
400 | public class IMiddlewareMiddleware : IMiddleware
401 | {
402 | private readonly AppDbContext _db;
403 |
404 | public IMiddlewareMiddleware(AppDbContext db)
405 | => _db = db;
406 |
407 | public async Task InvokeAsync(HttpContext context, RequestDelegate next)
408 | {
409 | _db.Logs.Add(new Models.Log { Area4 = "Deneme" });
410 | await _db.SaveChangesAsync();
411 |
412 | await next(context);
413 | }
414 | }
415 |
416 | // Use as extension method (optional)
417 | public static class MiddlewareExtensions
418 | {
419 | public static IApplicationBuilder UseIMiddlewareMiddleware(
420 | this IApplicationBuilder builder)
421 | {
422 | return builder.UseMiddleware();
423 | }
424 | }
425 | ```
426 |
427 | ```cs
428 | // Startup.cs
429 |
430 | public void ConfigureServices(IServiceCollection services)
431 | {
432 | services.AddDbContext(options =>
433 | options.UseInMemoryDatabase("InMemoryDb"));
434 |
435 | services.AddTransient();
436 | }
437 |
438 | public void Configure(IApplicationBuilder app, IHostingEnvironment env)
439 | {
440 | app.UseIMiddlewareMiddleware();
441 | }
442 | ```
--------------------------------------------------------------------------------
/13 - Validation İşlemleri.md:
--------------------------------------------------------------------------------
1 | ## VALIDATION İŞLEMLERİ
2 |
3 | ### 01 - Giriş
4 | - Validation kontrolü yapmanın önemi
5 | - Web zaafiyetlerinin en büyük nedeni input validation eksikliğinden kaynaklanır.
6 | - Ayrıca kullanıcıdan alınan bilgilerin bazı kurallara uyması gerekir. Örn: Parolanın minimum uzunluğu gibi.
7 | - .NET kütüphanesi ile, alınan her verinin validation kontrolünün ayrı ayrı yapılma zorluğu ortadan kalkmış ve bu validationların model üzerinden attribute olarak tanımlanması sağlanmıştır.
8 | - Bu şekilde validation yapıldığıda, kontrol etmek için sadece `ModelState` üzerinde `IsValid` kontrolü yapmak yeterlidir.
9 |
10 | > **NOT:** .NET Framework sürümünde, inputa gidilen html kodları otomatik olarak elimine edilip zararlı kod olarak algılanıyordu. .NET Core sürümünde ise bu kontrol yapılmamaktadır. Buna dikkat edilmesi gerekir.
11 |
12 | - Validation işlemleri `Model Binding` tarafından yönetilir.
13 | - Model Binding, `ModelStateDictionary` ile çalışır.
14 | - Bu dictionary içinde temel olarak kullanacağımız 3 yapı vardır:
15 | 1. **AddModelError:** Model içindeki herhangi bir property üzerine error eklemeyi sağlar
16 | 2. **GetValidationStat:** Validasyon durumunu gösterir. Enum değer döndürür.
17 | 3. **IsValid:** Validasyon işlemi sonucunda `true` veya `false` döndürür.
18 | - ModelState ifadesi, programı debug modda çalıştırınca, `QuickWatch` penceresinden izlenebilir.
19 |
20 |
21 |
22 |
23 |
24 | İleriki kısımlarda kullanılacak model ve html yapısı yapısı:
25 | ```cs
26 | public class Register
27 | {
28 | public string UserName { get; set; }
29 | public string EMail { get; set; }
30 | public string Password { get; set; }
31 | public DateTime Birthday { get; set; }
32 | public bool TermsAccepted { get; set; }
33 | }
34 | ```
35 |
36 | ```html
37 | @model Register
38 |
39 |
63 | ```
64 |
65 | ### 02 - Modele Error Mesaj Ekleme
66 |
67 | - Modele error mesaj eklemek için, `ModelState`'in `AddModelError` metodu kullanılır.
68 | - İlk parametre olarak eklenmesini istediğimiz property ismini, ikinci olarak da eklemek istediğimiz mesajı gireriz.
69 | - Property ismini `string` veya `nameof()` ifadeleriyle verebiliriz.
70 | - **NOT:** Model içindeki herhangi bir property'ye bu metot ile hata mesajı eklendiğinde, `IsValid` işlemi false döner.
71 |
72 | ```cs
73 | [HttpPost]
74 | public IActionResult Index(Register model)
75 | {
76 | if (model.Password != null && model.Password.Length < 6)
77 | ModelState.AddModelError("Password", "Parola en az 6 karakterli olmalıdır.");
78 |
79 | if (!model.EMail.Contains("@"))
80 | ModelState.AddModelError(nameof(Register.EMail), "Girilen değer email olmalıdır!");
81 |
82 | return View(model);
83 | }
84 | ```
85 |
86 | ### 03 - Form Üzerinde Validation Hata Mesajlarını Gösterme
87 |
88 | - Bir model üzerinde Validation kontrolü yapıldığında, model üzerine hata mesajları da eklenir.
89 | - Bu nedenle hata mesajlarının gösterilmesi için, *validation kontrolünden geçmiş modelin* tekrar sayfaya gönderilmesi gereklidir.
90 | - Bu model daha sonra tekrar View üzerine gönderildiğinde, inputlar `asp-for` ile tanımlanmışsa, input'un class yapısına `.input-validation-error` isminde bir class daha eklenir.
91 | - Bu class ismi kullanılarak, hatalı validation hatası yakalanmış inputlar üzerinde css düzenlemesi yapılabilir. Örn, input kırmızı border ile işaretlenebilir.
92 | - Validation işlemlerini her input için ayrı ayrı göstermek istiyorsak, `asp-validation-for` yapısı kullanılmalıdır.
93 |
94 | ```html
95 |
96 |
97 |
98 |
99 |
100 | ```
101 |
102 | - Validation mesajlarının tamamının gösterilmesi için, `asp-validation-summary` yapısı kullanılmalıdır. Bu yapının 3 tane enum değeri vardır:
103 | - **All:** Property ve model errorların hepsi gösterilir.
104 | - **ModelOnly:** Property errorları dahil edilmez, sadece model'e manuel eklenen hata mesajları gösterilir.
105 | - **None:** Herhangi bir hata mesajı gösterilmez.
106 |
107 | ```html
108 |
109 | ```
110 |
111 | ### 04 - MetaData Etiketleri
112 |
113 | - MetaData etiketleri .NET içinde aşağıdaki `ComponentModel` ve `DataAnnotations` kütüphaneleri içinde bulunur.
114 |
115 | ```cs
116 | using System.ComponentModel;
117 | using System.ComponentModel.DataAnnotations;
118 | using Microsoft.AspNetCore.Mvc;
119 | ```
120 |
121 | - **DiplayName()** : View içinde label tagı `asp-for` ile kullanıldığında otomatik olarak burdaki ismi çeker.
122 | - Bu sayede, tüm formlar üzerinde aynı ismi kullanmış oluruz ve düzenlemesi daha kolay olur.
123 | - **Required()** : Bir alanın girilmesini zorunlu kılmak için kullanılır.
124 | - **Range()** : Sayısal ifadelerin belirli bir double aralıkta girilmesini sağlamak için kullanılır.
125 | - ilk parametresine farklı bir type verilerek, double ifadeler dışında da bir aralık girilmesi sağlanabilir.
126 | - **NOT:** Bu, client-side bazen validation sorunları çıkarabiliyor.
127 |
128 | ```cs
129 | [Range(0, 100)]
130 | // veya
131 | [Range(typeof(bool), "true", "true")]
132 | ```
133 |
134 | - **MinLength()** : String ifadenin min uzunluğunu belirlemek için kullanılır.
135 | - **MaxLength()** : String ifadenin max uzunluğunu belirlemek için kullanılır.
136 | - **DataType()** : Parametre olarak enum değerlerden biri seçilerek, client side validation hata gösterimleri için bir type yaratır.
137 | - **Compare()** : Bir property'i başka biriyle karşılaştırmak için kullanılır. Eğer uyumsuzluk varsa hata verir.
138 | - Genellikle parola kontrolü için kullanılır.
139 | - Parametre olarak, karşılaştırılacak property'nin string olarak ismi verilebileceği gibi, `nameof` ile otomatik olarak de çekilebilir.
140 |
141 | ```cs
142 | [Required]
143 | public string Password { get; set; }
144 | [Required, Compare(nameof(Password))]
145 | public string RePassword { get; set; }
146 | ```
147 |
148 | - **RegularExpression()** : Bir alana regex kısıtlaması getirmemizi sağlar.
149 | - **HiddenInput()** : Belirtilen alanın, kullanıldığı yerde `type="hidden"` olarak işaretlenmesini sağlar.
150 | - Yukarıdakilerden farklı bir kütüphaneden gelir.
151 | - `using Microsoft.AspNetCore.Mvc;`
152 | - **DisplayFormat()** : Sayfaya bastırılacak fotmat bilgisi belirlenir. Genellikle Tarih, Saat, Sayı formatlama gibi durumlarda kullanılır.
153 | - `ApplyFormatInEditMode` özelliği varsayılan olarak false durumundadır. Eğer bu formatlı görüntünün input içerisinde de görünmesini istiyorsak true yaparız.
154 | - `NullDisplayText` özelliğini kullanarak NULL dönen sonuçlar yerine bir not gösterilmesini sağlayabilirsiniz.
155 |
156 | ```cs
157 | [DisplayFormat(ApplyFormatInEditMode=true, DataFormatString="{0:c}")]
158 | public decimal Ucret { get; set; }
159 | ```
160 |
161 | - **Remote()** : Client side validation için ilgili js dosyaları projeye eklenmişse, belirtilen endpoint'e istek yollayarak (ajax yapısı ile) kontrol işlemi yapar.
162 | - Daha çok kullanıcı adı veya mail gibi alanların db üzerinde bulunup bulunmadığını kontrol etmek için kullanılır.
163 | - Verilecek parametreler :
164 | - **action** : Gideceği action ismi
165 | - **controller** : Gideceği controller ismi
166 | - **area** : Belirtilmesi gerekiyorsa area ismi
167 | - **HttpMethod** : Gideceği endpoint'e hangi türde bir istek yapılacağını string olarak belirtiriz.
168 | - **ErrorMessage** : Eğer gittiği endpointten herhangi bir ifade dönmezse, bu hata mesajı yazdırılır.
169 | - İstek yaptığı endpointten json olarak `true` veya hata mesajı bekler. True ifadesi gidildiğinde validation yapar, diğer durumlarda validation yapmaz ve hata mesajını yazdırır.
170 | - Endpoint üzerinden property ismi parametre olarak alınıp kullanılabilir.
171 | - Doc : https://docs.microsoft.com/en-us/aspnet/core/mvc/models/validation#remote-validation
172 | > - **NOT 1 :** Bu metodun çalışması için client-side validation kütüphanelerinin yüklenmesi zorunludur.
173 |
174 | > - **NOT 2 :** Eğer database tabloları MVC projesinin dışında başka bir proje içinde oluşturulmuşsa, ilgili > proje içine `Microsoft.AspNetCore.Mvc` kütüphanesinin eklenmesi gerekmektedir.
175 | >
176 | > ```
177 | >
178 | >
179 | >
180 | > ```
181 |
182 | ```cs
183 | // Model
184 | [Remote(action:"UserNameCheck", controller:"Validation", HttpMethod = "Post", ErrorMessage = "Remote control not checked!")]
185 | public string UserName { get; set; }
186 |
187 | // Controller
188 | [HttpPost]
189 | public IActionResult UserNameCheck(string UserName)
190 | {
191 | if (usernameCheck(UserName))
192 | return Json(true);
193 | else
194 | return Json($"{UserName} does exists.");
195 | }
196 | ```
197 |
198 | *Modelin son yapısı:*
199 | ```cs
200 | using Microsoft.AspNetCore.Mvc;
201 | using System.ComponentModel;
202 | using System.ComponentModel.DataAnnotations;
203 |
204 | namespace WebApplication1.Models
205 | {
206 | public class Register
207 | {
208 | [DisplayName("User Name"), Required, MinLength(4), MaxLength(10)]
209 | [Remote(action:"Check", controller:"Validation", HttpMethod = "Post", ErrorMessage = "Remote control not checked!")]
210 | public string UserName { get; set; }
211 |
212 | [DisplayName("E-Mail"), DataType(DataType.EmailAddress)]
213 | [RegularExpression("^[a-z0-9]+@mail.com$")]
214 | public string EMail { get; set; }
215 |
216 | [DisplayName("Password"), Required, DataType(DataType.Password)]
217 | public string Password { get; set; }
218 |
219 | [DisplayName("RePassword"), Required, DataType(DataType.Password), Compare(nameof(Password))]
220 | public string RePassword { get; set; }
221 |
222 | [DisplayName("Accepting Terms"), Required]
223 | public bool TermsAccepted { get; set; }
224 |
225 | [DisplayFormat(DataFormatString = "{0:MMM dd, yyyy}")]
226 | public DateTime BithDate { get; set; }
227 | }
228 | }
229 | ```
230 |
231 | > - **NOT 3 :** Endpoint üzerinde kontrol edilecek parametre, doldurulan formun input alanındaki `name` ismiyle gönderilecek parametredir. Bu yüzden kullanılan formun name alanına dikkat edilmelidir.
232 | > - Örnek olarak aşağıdaki gibi ViewModel ile kullanılan bir form yapısı varsa, kontrol eden endpoint yapısı şu şekilde olmalıdır:
233 | >
234 | > ```cs
235 | >
236 | >
237 | >
238 | >
239 | >
240 | > ```
241 | >
242 | > ```cs
243 | > [HttpPost]
244 | > public IActionResult Check_Kullanici_KullaniciAdi()
245 | > {
246 | > string kullaniciAdi = Request.Form["Kullanici.KullaniciAdi"];
247 | > if (check(kullaniciAdi))
248 | > return Json(true);
249 | > else
250 | > return Json($"{kullaniciAdi} daha önceden kullanılmış!");
251 | > }
252 | > ```
253 |
254 | ### 05 - Custom Error Message
255 |
256 | - Validasyon elemanlarının hepsinde default bir hata mesajı vardır.
257 | - Bu default değerler dışında kendimiz hata mesajı yazmak istediğimizde, MetaData'ların `ErrorMessage` parametresinden yararlanırız.
258 | - Bu parametre tüm MetaData'larda bulunur.
259 | - **NOT:** Hatalar gösterilirken, yazılma sırası dikkate alınır. İlk uyumsuzluk hatası gösterilir, düzeltilirse diğerleri gösterilir.
260 | - Hata mesajı içinde dinamik öğeler kullanabiliriz. Bunu, hata mesajının ilgili yerine `{}` ile yaparız. Bu yapıda;
261 | - `{0}` : Display name veya bu yoksa property ismini belirtir.
262 | - `{1}, {2}...` : Attribute içindeki değerlerin sırayla numaralandırmasıdır.
263 |
264 | ```cs
265 | [DisplayName("Age"), Range(18, 100, ErrorMessage = "{0} must be between {1} and {2}...")]
266 | public int Age { get; set; }
267 |
268 | // Result:
269 | // Age must be between 18 and 100...
270 | ```
271 |
272 | ### 06 - Custom Property Validation Method
273 |
274 | - Default validation MetaData'ların yetmediği yerde kendi metotlarımızı yazabiliriz.
275 | - Bunun için `Attribute` ve `IModelValidator` sınıf ve interface'lerinden türeyen bir sınıfa ihtiyacımız var.
276 | - Bu sınıfın isminin `-Attribute` ile bitmesi gerekmektedir.
277 | - Interface'i implament ettiğimizda gelen `Validate` metodunu, aşağıdaki örneğe uygun yazdığımızda, artık bunu attribute olarak kullanabiliriz.
278 | - Model içinde kullandığımız attribute değerine `context.Model` üzerinden ulaşıp kontrol edebiliriz.
279 | - **NOT:** Bu yapı sadece server-side validation için kullanılabilir, client-side validation işlemleri bunu görmeyecektir.
280 |
281 | ```cs
282 | public class DateValidationAttribute : Attribute, IModelValidator
283 | {
284 | public string ErrorMessage { get; set; }
285 | = "Date cannot be bigger than today!";
286 |
287 | public IEnumerable Validate(ModelValidationContext context)
288 | {
289 | var date = context.Model as DateTime?;
290 |
291 | // Block
292 | if (!date.HasValue || date > DateTime.Now)
293 | return new List
294 | { new ModelValidationResult("", ErrorMessage) };
295 |
296 | // Allow
297 | else
298 | return Enumerable.Empty();
299 | }
300 | }
301 | ```
302 |
303 | Attribute olarak kullanımı :
304 | ```cs
305 | [DataType(DataType.Date), DateValidation]
306 | public DateTime BithDate { get; set; }
307 | ```
308 |
309 | ### 07 - Validation Kontrolü
310 |
311 | - Formdan gelen verilerin, kaydedilmeden önce, validation işlemlerinden geçip geçmediğini kontrol etme yönetimidir.
312 | - Bunun için `ModelState` sınıfının `IsValid` özelliği kullanılır.
313 | - Formlarla yapılan her işlemde bu kontrol mutlaka yapılmalıdır.
314 | - Eğer validasyon işlemleri yeterli değilse, IsValid kontrolünden sonra if yapıları kullanılarak gerekli validasyonlar tekrar kontrol edilir.
315 |
316 | ```cs
317 | [ValidateAntiForgeryToken]
318 | [HttpPost]
319 | public ActionResult Index(Register model)
320 | {
321 | if (ModelState.IsValid)
322 | {
323 | // Save process
324 | }
325 | return View(model);
326 | }
327 | ```
328 |
329 | ### 08 - Client Side Validation
330 |
331 | - Şu ana kadar yapılan işlemler, programın server-side validation işlemleri yapmaları için gerekli adımlardı.
332 | - Server-side validation yapıldığında, sayfanın formu server üzerine göndermesi ve doğal olarak sayfanın yenilenmesi gerekmekteydi.
333 | - Client-side validation yapıldığında ise, server üzerine bilgiler gönderilmeden önce validation işlemleri kontrol edilir ve bilgiler valid değilse form gönderilmez.
334 | - View içinde `asp-for` ile form verileri çekildiğinde, element içine aynı zamanda `data-val-` özellikleri otomatik gelir.
335 | - Bu elementler client-side validation için kullanılan attribute'lerdir.
336 | - Bu attribute'lerin kullanılması ve validation yapması için projemiz içine bazı js kütüphanelerini dahil etmemiz gerekmektedir. Bunlar;
337 | 1. JQuery
338 | 2. JQuery-Validation
339 | 3. JQuery-Validation-Unobtrusive
340 | - Bu kütüphanler projeye dahil edilip, html içine yolları verildikten sonra herhangi bir işlem yapmamıza gerek yoktur. Server-side validation kontrolü yapılacaktır.
341 | - **NOT:** Server-side validation güvensizdir. Tarayının js ayarları kapatıldığında veya tarayıcı dışında başka bir mekanizma ile bilgiler gönderildiğinde, bu validation çalışmayacaktır. Buradaki amaç sadece client tarafında daha efektif ve hızlı validation yapmaktır. **Server-side validation ve IsValid kontrolünün kesinlikle unutulmaması gereklidir!**
--------------------------------------------------------------------------------
/17 - Deployment.md:
--------------------------------------------------------------------------------
1 | ## DEPLOYMENT
2 |
3 | - Core projesinin deployment süreci basit olarak 3 temel adımda gerçekleşir:
4 | 1. Projenin dizine publish edilmesi ve barındıracak host üzerine gönderilmesi.
5 | - Publish işlemi, projenin derlenip, Release sürümünün statik dosyalarla birlikte bir dizine veya ftp üzerinden belirlenen bir alana çıkarılması işlemidir.
6 | 2. Host üzerinde, servis oluşturulması
7 | - Dotnet Core projesi, temelinde bir konsol uygulaması olduğundan, server reboot veya programın çökmesi durumda tekrar başlatılması gerekmektedir.
8 | - Host üzerinde bu işlemler için bir servis yazıp, yukarıdaki durumlarda uygulamamızın tekrar başlatılmasını sağlamamız gerekmektedir.
9 | 3. Reverse Proxy kurulumu
10 | - Core uygulaması, kendi içinde `Kestel` adlı bir web server bulundurmaktadır.
11 | - Kestel, tüm web server işlemlerini ve statik dosya yönlendirmesi gibi işlemleri kendi içinde oluşturmaktadır.
12 | - Bununla birlikte, 5000 portundan dinleme yapan Kestel'i, herhangi bir proxy aracılığıyla istediğimiz bir portu (web için genellikle 80) dinlemeye ayarlamamız gerekmektedir.
13 | - Bunun için genellikle kullandığımız Reverse Proxy Server'lar:
14 | - IIS
15 | - Nginx
16 | - Apache
17 |
18 | ### 01 - Projeyi Yayınlama Yöntemleri
19 |
20 | Dotnet Core projeleri temel olarak iki şekilde yayınlanabilir:
21 |
22 | - **Framework Bağımlı Yayınlama (Framework-dependent deployments - FDD)**
23 | - Sadece uygulama ve 3. parti bağımlılıkların yayınlanmasına dayanır.
24 | - .NET Core runtime bağımlılığı vardır, bu yüzden deploy ettiğimiz hedef makinede .NET Core kütüphanesinin ilgili sürümü kurulu olması gerekmektedir.
25 | - İşletim sistemi belirtmeye gerek yoktur. Deploy ettikten sonra, her işletim kendi işletim sistemine özgü Core runtime üzerinden çalıştırılabilir haldedir.
26 | - Deployment paketi, sadece gerekli olan frameworkleri barındırdığı için boyutu küçüktür.
27 |
28 | - **Framework Bağımsız Yayınlama (Self-contained deployments - SCD)**
29 | - Uygulama, 3. parti bağımlılıklar ve dotnet core kütüphanelerinin birlikte deploy edilmesi yöntemidir.
30 | - Hedef kaynakta dotnet core kurulu olmasına gerek yoktur.
31 | - Dotnet core kütüphaneleri için işletim sistemi altyapıları farklı olduğundan, deploy ederken hangi işletim sistemi için deploy etmemiz gerektiğini belirtmemiz gerekmektedir.
32 | - [Runtime IDentifier Catalog](https://docs.microsoft.com/en-us/dotnet/core/rid-catalog)
33 | - Deployment paketi, FDD'ye göre daha büyük boyutludur.
34 |
35 | ### 02 - Projenin Yayınlanması
36 |
37 | - Proje, 4 farklı şekilde yayınlanabilir:
38 | - Framework-dependent deployment
39 | - Framework-dependent deployment with third-party dependencies
40 | - Self-contained deployment
41 | - Self-contained deployment with third-party dependencies
42 |
43 | #### i. CLI Araçları (Command-Line Interface Tools) ile yayınlama
44 |
45 | - Projeyi CLI ile yayınlarken `dotnet publish` komutunu kullanırız.
46 | - Bu komutun ayrıntılı opsiyonları aşağıdaki gibidir:
47 |
48 | ```
49 | Usage: dotnet publish [options]
50 |
51 | Options:
52 | -h, --help Show help information.
53 | -o, --output Output directory in which to place the published artifacts.
54 | -f, --framework Target framework to publish for. The target framework has to be specified in the project file.
55 | -r, --runtime Publish the project for a given runtime. This is used when creating self-contained deployment. Default is to publish a framework-dependent app.
56 | -c, --configuration Configuration to use for building the project. Default for most projects is "Debug".
57 | --version-suffix Defines the value for the $(VersionSuffix) property in the project.
58 | --manifest The path to a target manifest file that contains the list of packages to be excluded from the publish step.
59 | --self-contained Publish the .NET Core runtime with your application so the runtime doesn't need to be installed on the target machine. Defaults to 'true' if a runtime identifier is specified.
60 | --no-restore Does not do an implicit restore when executing the command.
61 | -v, --verbosity Set the verbosity level of the command. Allowed values are q[uiet], m[inimal], n[ormal], d[etailed], and diag[nostic].
62 | --no-dependencies Set this flag to ignore project to project references and only restore the root project.
63 | --force Set this flag to force all dependencies to be resolved even if the last restore was successful. This is equivalent to deleting project.assets.json.
64 |
65 | ```
66 |
67 | **Framework-dependent deployment**
68 |
69 | - Proje bağımlılıklarının güncellenmesi
70 | - `[proje-adi].csproj` dosyası içinde yazdığımız bağımlılıkların indirilmesi ve güncellenmesi işleminin yapılmasıdır.
71 | - `dotnet restore` komutu çalıştırılır.
72 | - Projenin derlenmesi
73 | - `dotnet build` komutu ile projenin son halinin derlenmesi işlemidir.
74 | - Projenin yayınlanması
75 | - `dotnet publish -f netcoreapp2.0 -c Release -o CorePublish` komutu projenin yayınlanacak dosyalarının oluşturulmasını sağlar.
76 | - `-o` parametresi ile publish edilecek dosyaların çıkarılacağı konumu belirtiyoruz.
77 | - `-c` parametresi ile derlenme ayarının belirtiyoruz.
78 | - `-f` parametresi ile projemizin framework versiyonunu belirtiyoruz.
79 | - Projenin çalıştırılması
80 | - `dotnet [app_name].dll` komutuyla, ilgili projenin dll dosyası dotnet aracılığıyla çalıştırılır.
81 | - Proje çalıştırıldığında, `localhost:5000` üzerinden yayın yapmaya başlar.
82 |
83 | **Self-contained deployment**
84 |
85 | - Projenin hedef platformlarının belirtilmesi
86 | - `[app_name].csproj` içine hedef platformların belirtilmesi gereklidir.
87 | - [Tüm platformlar için bakınız...](https://docs.microsoft.com/en-us/dotnet/core/rid-catalog)
88 |
89 | ```xml
90 |
91 | win10-x64;osx.10.11-x64
92 |
93 | ```
94 |
95 | - Proje bağımlılıklarının güncellenmesi
96 | - `[proje-adi].csproj` dosyası içinde yazdığımız bağımlılıkların indirilmesi ve güncellenmesi işleminin yapılmasıdır.
97 | - `dotnet restore` komutu çalıştırılır.
98 | - Projenin derlenmesi
99 | - `dotnet build` komutu ile projenin son halinin derlenmesi işlemidir.
100 | - Projenin yayınlanması
101 | - `dotnet publish -r win10-x64 -f netcoreapp2.0 -c Release -o CorePublish`
102 | - `dotnet publish -r osx.10.11-x64 -f netcoreapp2.0 -c Release -o CorePublish`
103 | - `-r` parametresi ile hedef platformu belirtiyoruz.
104 | - `-o` parametresi ile publish edilecek dosyaların çıkarılacağı konumu belirtiyoruz.
105 | - `-c` parametresi ile derlenme ayarının belirtiyoruz.
106 | - `-f` parametresi ile projemizin framework versiyonunu belirtiyoruz.
107 | - Projenin çalıştırılması
108 | - Hedef platforma özgün olarak çıkarılmış `[app_name]` veya `[app_name].exe` dosyalarından biri çalıştrırılarak programa ulaşım sağlanır.
109 | - Proje çalıştırıldığında, `localhost:5000` üzerinden yayın yapmaya başlar.
110 |
111 | #### ii. Visual Studio ile Yayınlama
112 |
113 | **Framework-dependent deployment**
114 |
115 | - Projenin derlenmesi
116 | - Solution üzerine sağ tıklayıp `Build Solution` tıklanır.
117 | - Projenin yayınlanması
118 | - Araç çubukları üzerinden config kısmı `Debug > Release` olarak değiştirilir.
119 | - Proje üzerine sağ tıklanıp `Publish` tıklanır.
120 | - Buradan `Folder Publish` seçilip, dosyaların çıkarılacağı konum belirlenir.
121 | - Projenin çalıştırılması
122 | - `dotnet [app_name].dll` komutuyla, ilgili projenin dll dosyası dotnet aracılığıyla çalıştırılır.
123 | - Proje çalıştırıldığında, `localhost:5000` üzerinden yayın yapmaya başlar.
124 |
125 | **Self-contained deployment**
126 |
127 | - Projenin hedef platformlarının belirtilmesi
128 | - `[app_name].csproj` içine hedef platformların belirtilmesi gereklidir.
129 | - Projeye sağ tıklanıp `Edit [app_name].csproj` tıklanır.
130 | - [Tüm platformlar için bakınız...](https://docs.microsoft.com/en-us/dotnet/core/rid-catalog)
131 |
132 | ```xml
133 |
134 | win10-x64;osx.10.11-x64
135 |
136 | ```
137 |
138 | - Projenin derlenmesi
139 | - Solution üzerine sağ tıklayıp `Build Solution` tıklanır.
140 | - Projenin yayınlanması
141 | - Araç çubukları üzerinden config kısmı `Debug > Release` olarak değiştirilir.
142 | - Proje üzerine sağ tıklanıp `Publish` tıklanır.
143 | - Buradan `Folder Publish` seçilip, dosyaların çıkarılacağı konum belirlenir.
144 | - Alt kısımdaki `Publish` kısmının yanındaki ayarlara tıklayarak, `Create Profile` olarak değiştirilir ve tıklanır.
145 | - Yeni gelen pencereden `Target Location > Settings > Target Runtime` kısmından ilgili platform seçilir.
146 | - Pencere kapatılıp `Start` butonuna tıklanır.
147 | - Projenin çalıştırılması
148 | - Hedef platforma özgün olarak çıkarılmış `[app_name]` veya `[app_name].exe` dosyalarından biri çalıştrırılarak programa ulaşım sağlanır.
149 | - Proje çalıştırıldığında, `localhost:5000` üzerinden yayın yapmaya başlar.
150 |
151 | #### iii. Publish Dosyaları
152 |
153 | - `[app-name].deps.json` :
154 | - Projenin çalışma zamanındaki bağımlılıklarını bulunduran dosyadır.
155 | - Kompoentleri ve kütüphaneleri bulundurur.
156 | - Projenin çalışması için zorunlu dosyadır.
157 | - [Ayrıntılı Bilgi](https://github.com/dotnet/cli/blob/85ca206d84633d658d7363894c4ea9d59e515c1a/Documentation/specs/runtime-configuration-file.md)
158 | - `[app-name].dll` :
159 | - Application'ı bulunduran dosyadır.
160 | - Yayın yapılırken bu dosya çalıştırılır.
161 | - `[app-name].pdb` :
162 | - Debug sembollerinin bulundugu dosyadır.
163 | - Projenin çalışması için zorunlu değildir.
164 | - `[app-name].runtimeconfig.json` :
165 | - Uygulamanın çalışma zamanı ayarlarının bulunduğu dosyadır.
166 | - Projenin yazıldığı dotnet versiyonunu da barındırır.
167 | - [Ayrıntılı Bilgi](https://github.com/dotnet/cli/blob/85ca206d84633d658d7363894c4ea9d59e515c1a/Documentation/specs/runtime-configuration-file.md)
168 | - `bundleconfig.json`:
169 | - Bundle configuration ayarlarını bulundurur.
170 | - `appsettings.json`:
171 | - Bağlantı stringi, log ayaları vb gibi ayaların bulunduğu json dosyasıdır.
172 | - `wwwroot`
173 | - Statik dosyaların bulundugu dizindir.
174 |
175 | ### 03 - Linux Server Ayarlamaları (Ubuntu 16.04)
176 |
177 | #### Self-Contained Deployment
178 |
179 | 1. Proje dosyası yukarıda anlatıldığı şekilde, `Self-Contained` olarak dosyaya yayınlanır.
180 | 2. Yayınlanan dosyalar server üzerine gönderilir.
181 | - Bunun için sunucumuza [FTP Server kurabileceğimiz](BONUS%20-%20FTP%20Server%20Kurulumu.md) gibi, SSH üzerinden de FTP bağlantısı yapıp dosyalarınızı gönderebilirsiniz.
182 | 3. Proje kütüphanelerini ekleme ve çalıştırma
183 | - Proje `dotnet` dahil olmak üzere tüm kütüphane yapılarını içinde bulundurduğundan, server üzerine herhangi bir kütüphane kurmaya gerek yoktur.
184 | - Proje içinde gelen `libxxx.so` kitaplıklarının `ldd` komutu ile sisteme eklenmesi gerekmektedir.
185 | - Daha sonra uygulama dosyası direk çalıştırılarak dotnet projesinin çalıştığını görebiliriz.
186 |
187 | ```bash
188 | # Kütüphaneleri sisteme ekleme
189 | $ ldd lib*.so
190 |
191 | # İstenilirse çalıştırılacak dosya yolu path olarak eklenebilir.
192 | # "proje" > dotnet ana dosyasıdır.
193 | $ export PATH=$PATH:/var/www/CoreProje/
194 |
195 | # Dotnet dosyasının çalıştırılması
196 | $ proje
197 |
198 | Hosting environment: Production
199 | Content root path: /var/www/CoreProje/proje
200 | Now listening on: http://localhost:5000
201 | Application started. Press Ctrl+C to shut down.
202 | ...
203 | ```
204 |
205 | > **NOT:** Self-contained olarak server üzerine kopyalanmış dosyalar çalıştırıldığında hata ile karşılaşılırsa, aşağıdaki iki kütüphanenin kurulması önerilir.
206 |
207 | ```
208 | $ ./proje
209 | Failed to load ***, error: libunwind.so.8: cannot open shared object file: No such file or directory
210 | Failed to bind to CoreCLR at '/var/www/libcoreclr.so'
211 |
212 | $ apt install libicu-dev
213 | $ apt install libunwind-dev
214 |
215 | $ ./proje
216 | warn: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[35]
217 | No XML encryptor configured. Key {61d5203f-7b87-4911-bb68-126a09425473} may be persisted to storage in unencrypted form.
218 | Hosting environment: Production
219 | Content root path: /var/www
220 | Now listening on: http://localhost:5000
221 | Application started. Press Ctrl+C to shut down.
222 | ```
223 |
224 | 4. Reverse Proxy kurulur ve ayarlamaları yapılır.
225 | 5. Herhangi bir nedenden ötürü server veya uygulama kapanırsa tekrardan başlaması için gerekli olan servis dosyası oluşturulur ve çalıştırılır.
226 |
227 | #### Framework-Dependent Deployment
228 |
229 | 1. Proje dosyası yukarıda anlatıldığı şekilde, `Framework-Dependent` olarak dosyaya yayınlanır.
230 | 2. Yayınlanan dosyalar server üzerine gönderilir.
231 | - Bunun için sunucumuza [FTP Server kurabileceğimiz](BONUS%20-%20FTP%20Server%20Kurulumu.md) gibi, SSH üzerinden de FTP bağlantısı yapıp dosyalarınızı gönderebilirsiniz.
232 | 3. Dotnet kütüphane kurulumları yapılır.
233 | - Kütüphaneler iki farklı yöntemle kurulabilir:
234 | 1. `Dotnet Runtime` + `Dotnet Hosting` kurulabilir.
235 | 2. Geliştirici araçları da dahil olmak üzere her şeyi bulunduran `Dotnet SDK` kurulabilir.
236 | - Son sürüm kütüphaneleri Linux üzerinde görüntülemek için basitçe `apt search dotnet` komutu çalıştırılabilir.
237 |
238 | ```bash
239 | $ apt search dotnet
240 |
241 | dotnet-hosting-2.0.8/xenial,now 2.0.8-1 amd64 [installed]
242 | Microsoft .NET Core 2.0.8 Linux Server Hosting
243 |
244 | dotnet-runtime-2.1.0-rc1/xenial,now 2.1.0-rc1-1 amd64 [installed]
245 | Microsoft .NET Core Runtime - 2.1.0 Release Candidate 1 Microsoft.NETCore.App 2.1.0-rc1
246 |
247 | dotnet-sdk-2.1.4/xenial 2.1.4-1 amd64
248 | Microsoft .NET Core SDK - 2.1.4
249 |
250 | ...
251 |
252 | # 1. Yöntem
253 | $ sudo apt install dotnet-runtime-2.1.0-rc1
254 | $ sudo apt install dotnet-hosting-2.0.8
255 |
256 | # 2. Yöntem
257 | $ sudo apt install dotnet-sdk-2.1.4
258 | ```
259 |
260 | 4. Reverse Proxy kurulur ve ayarlamaları yapılır.
261 | 5. Herhangi bir nedenden ötürü server veya uygulama kapanırsa tekrardan başlaması için gerekli olan servis dosyası oluşturulur ve çalıştırılır.
262 |
263 | #### Reserve Proxy Kurulumu
264 |
265 | - Reserver proxy için **Nginx, ISS, Apache** vb. kurabilirsiniz.
266 | - Nginx kullanımı için öncelikle Nginx kurulumunu yapıyoruz.
267 |
268 | ```
269 | sudo apt-get install nginx
270 | ```
271 |
272 | - Nginx'i kurduktan sonra servisini başlatmamız gerekmektedir.
273 |
274 | ```
275 | sudo service nginx start
276 | ```
277 |
278 | - Servisi başlattıktan sonra, server ip adresine herhangi bir yerden bağlandığımızda, aşağıdaki gibi Nginx default sayfasını görmemiz gerekmetedir. Eğer bu sayfayı görüyorsak herhangi bir hata olmadan servisin kurulduğunu anlayabiliriz.
279 |
280 |
281 |
282 |
283 |
284 | - Nginx kurulumunu yaptıktan sonra, default olarak 5000 portundan dinleme yapan Core projemizi, Nginx ile 80 portuna proxy yönlendirmesi yapmamız gerekmektedir.
285 | - Öncelikle default Nginx ayarlarının dosya ismini değiştiriyoruz
286 | - `mv /etc/nginx/sites-available/default /etc/nginx/sites-available/defaultOriginal`
287 | - Daha sonra yeniden bir ayar dosyası oluşturup aşağıdaki kodları içine yapıştırıyoruz.
288 | - `nano /etc/nginx/sites-available/default`
289 |
290 | ```
291 | server {
292 | listen 80;
293 | location / {
294 | proxy_pass http://localhost:5000;
295 | proxy_http_version 1.1;
296 | proxy_set_header Upgrade $http_upgrade;
297 | proxy_set_header Connection keep-alive;
298 | proxy_set_header Host $http_host;
299 | proxy_cache_bypass $http_upgrade;
300 | }
301 | }
302 | ```
303 |
304 | - Ayarları yaptıktan sonra Nginx'i yeni ayarlarıyla test edip baştan başlatıyoruz.
305 |
306 | ```
307 | $ sudo nginx -t
308 | nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
309 | nginx: configuration file /etc/nginx/nginx.conf test is successful
310 |
311 | $ sudo nginx -s reload
312 | ```
313 |
314 | - Nginx'i tekrar başlattıktan sonra herhangi bir hata yoksa, dotnet projemizi başlatıp, server ip adresine direk bağlanıp projeye ulaşıp ulaşamadığımızı kontrol edebiliriz.
315 |
316 |
317 | #### Servis Yazılması
318 |
319 | - Öncelikle servise dosyasını oluşturuyoruz.
320 |
321 | ```
322 | $ sudo nano /etc/systemd/system/core.service
323 | ```
324 |
325 | - Daha sonrasında dosyanın içine aşağıdaki satırları ekliyoruz.
326 | - **Description:** Servis açıklaması
327 | - **WorkingDirectory:** Çalışma dizini. App dosyalarımızın olduğu dizini vermemiz gerekmektedir.
328 | - **ExecStart:** Konsolda uygulamamızı çalıştırmak için kullandığımız `dotnet helloworld.dll` komutunun tam yol olarak belirtildiği yerdir.
329 | -
330 | ```
331 | [Unit]
332 | Description=Example .NET Web API App running on Ubuntu
333 |
334 | [Service]
335 | WorkingDirectory=/var/aspnetcore/hellomvc
336 | ExecStart=/usr/bin/dotnet /var/aspnetcore/hellomvc/hellomvc.dll
337 | Restart=always
338 | RestartSec=10
339 | SyslogIdentifier=dotnet-example
340 | User=www-data
341 | Environment=ASPNETCORE_ENVIRONMENT=Production
342 | Environment=DOTNET_PRINT_TELEMETRY_MESSAGE=false
343 |
344 | [Install]
345 | WantedBy=multi-user.target
346 | ```
347 |
348 | - Dosyayı kaydettikten sonra, servisi aktifleştirmemiz ve başlatmamız gerekmektedir.
349 |
350 | ```
351 | $ systemctl enable kestrel-hellomvc.service
352 | $ systemctl start kestrel-hellomvc.service
353 | $ systemctl status kestrel-hellomvc.service
354 |
355 | ● kestrel-hellomvc.service - Example .NET Web API App running on Ubuntu
356 | Loaded: loaded (/etc/systemd/system/kestrel-hellomvc.service; enabled)
357 | Active: active (running) since Thu 2016-10-18 04:09:35 NZDT; 35s ago
358 | Main PID: 9021 (dotnet)
359 | CGroup: /system.slice/kestrel-hellomvc.service
360 | └─9021 /usr/local/bin/dotnet /var/aspnetcore/hellomvc/hellomvc.dll
361 | ```
362 |
363 | - Servisi aktifleştirdikten sonra, web uygulamamız otomatik olarak başlayacaktır.
364 | - Servisin log dosyasına bakmak için `sudo journalctl -fu ` komutu kullanılabilir.
--------------------------------------------------------------------------------
/14 - Routing.md:
--------------------------------------------------------------------------------
1 | ## ROUTING
2 |
3 | ### 01) Routing nedir?
4 | - `Routing Middleware`, gelen url isteklerinin yönledirilmesini düzenleyen kurallardır.
5 | - `Startup.cs` sınıfı içindeki `Configure` metotu içinde tanımlanır.
6 | - Neden routing ayarlarına ihtiyaç var?
7 | - SEO uyumluluğunu arttırır. Böylece default yapı dışında, SEO uyumlu url yapılarını oluşturmamıza izin verir.
8 | - Uzun ve anlaşılmaz url yapısı yerine, daha anlaşılır bir yapı kurmamıza olanak tanır. Örn:
9 | - `site.com/Haberler/Index?haberID=123` gibi bir url yerine,
10 | - `site.com/Haberler/123` veya `site.com/Haberler/istanbulda-son-durum` gibi daha anlaşılır url yapılarını oluşturabiliriz.
11 | - Microsoft Docs yolu :
12 | - https://docs.microsoft.com/en-us/aspnet/core/mvc/controllers/routing
13 |
14 | ### 02) Default Routing Ayarları
15 | - Default routing ayarları `site/{controller}/{action}` yapısında url yapıları oluşturmak için kullanılır.
16 | - İki şekilde default routing ayarlaması yapılabilir.
17 |
18 | ```cs
19 | app.UseMvcWithDefaultRoute();
20 | ```
21 |
22 | ```cs
23 | app.UseMvc(routes =>
24 | {
25 | routes.MapRoute(
26 | name: "default",
27 | template: "{controller=Home}/{action=Index}/{id?}");
28 | });
29 | ```
30 |
31 | ### 03) Custom Routing
32 | ```cs
33 | // Örnek bir custom routing
34 | routes.MapRoute(
35 | name: "CustomRoute",
36 | template: "Haberler/Kategori/{catID}",
37 | defaults: new { controller = "Haberler", action = "Index"}
38 | );
39 | ```
40 |
41 | - Parametreler :
42 | - **name** : Route yönlendirmemize bir isim vermemizi sağlar. Bu ismin tekrarlanmaması gereklidir.
43 | - **template** : Kullanıcı tarafından gelecek isteğin url tanımlanması burada yapılır.
44 | - **defaults** : Gelen isteğin hangi Controller ve Action tarafından karşılanacağını belirttiğimiz kısımdır.
45 | - **constraints** : Rota kısıtlamalarını tanımladığımız alandır.
46 | - **dataTokens** : (Açıklaması ayrı başlık olarak aşagıda yazılmıştır.)
47 |
48 | - Template kısmında tanımladığımız dinamik değerler, karşılayan action tarafından parametre olarak alınır.
49 | - Yine bu değerlerin opsiyonel olarak girilmesini istiyorsak yanına `?` işareti bıkakıp opsiyonel yapabiliriz.
50 |
51 | #### DataTokens
52 | - Burada tanımlanan değerlere action içinde `RouteData` üzerinden ulaşılarak, actiona hangi route düzeninden ulaşıldığı bilgisi alınabilir.
53 | - Genellikle, birden fazla route yolunun aynı actiona ulaşması ve bu action içinde bu ayrımın yapılmasına olanak vermek için kullanılır.
54 | - Örnek olarak;
55 |
56 | ```cs
57 | // Route Ayarlaması
58 | app.UseMvc(routes =>
59 | {
60 | routes.MapRoute(
61 | name: "default",
62 | template: "{controller=Home}/{action=Index}/{id?}",
63 | defaults: null,
64 | constraints: null,
65 | dataTokens: new { Name = "default_route" });
66 | });
67 |
68 | // Bu değere action içinden ulaşım
69 | public class ProductController : Controller
70 | {
71 | public string Index()
72 | {
73 | var nameTokenValue = (string)RouteData.DataTokens["Name"];
74 | return nameTokenValue;
75 | }
76 | }
77 | ```
78 |
79 | #### Area Routing
80 | - Routing tanımlamasında default parametre değerine area tanımlaması da yapılabilir.
81 |
82 | ```cs
83 | defaults: new { area = Blog, controller = Users, action = AddUser }
84 | ```
85 |
86 | #### RouteData
87 | - `RouteData` request içindeki route parametrelerinin tutulduğu alandır.
88 | - Bunlar action, controller ve diğer taşınan parametrelerdir.
89 | - RouteData içinde post veya get ile taşınan veriler taşınmaz!
90 | - Route içinde taşınan parametrelerin, action metodu içine parametre olarak alınmasına gerek yoktur. Direk `RouteData.Values` içinden çekilebilir.
91 | - İstenilirse, RouteData yerine, metot içine parametre olarak da alınabilir.
92 |
93 | ```cs
94 | [Route("Haberler/Spor/{haberID}")]
95 | public string Index3()
96 | {
97 | object hID = RouteData.Values["haberID"]; // 1
98 | object action = RouteData.Values["action"]; // Index3
99 | object controller = RouteData.Values["controller"]; // Home
100 | return "Haber Sayfası";
101 | }
102 | ```
103 |
104 | #### A catch-all parameters
105 | - Route değerlerindeki her bir parametre sadece bir segment alır. Eğer url olarak gönderilen segment sayısı, onu karşılayan route ayarlarındaki segment sayısından fazla olursa, url uyumsuzluğu olduğundan 404 hatası ile karşılaşırız.
106 | - Bazı durumlarda, route üzerinden kaç tane segment geleceğini sınırlandırmak istemeyiz.
107 | - Bu durumda route temasının en sonuna, gelecek tüm segmentleri alabilecek `Catch-All` parametresi ekleriz.
108 | - Bu yapı, metotlardaki `params` ifadesine benzerdir.
109 | - Bunun için parametre isminin önüne `*` işareti getirilir.
110 | - Parametre ismi olarak herhangi bir değer alınabilir.
111 | - Örnek vermek gerekirse;
112 |
113 | ```cs
114 | routes.MapRoute(
115 | name: "default",
116 | template: "{controller=Home}/{action=Index}/{*extra}");
117 | ```
118 |
119 | - Burada yapılan ayarlamada, ilk iki segmentten sonra gelen tüm segmentler, extra parametresi ile alınır.
120 | - Daha sonra bu parametre `/` işareti üzerinden parse edilebilir.
121 | - Örnek olarak aşağıdaki gibi bir controller yazıp değerleri çekersek;
122 |
123 | ```cs
124 | public IActionResult Index()
125 | {
126 | var model = new Dictionary();
127 |
128 | model["controller"] = RouteData.Values["controller"];
129 | model["action"] = RouteData.Values["action"];
130 | model["extra"] = RouteData.Values["extra"];
131 |
132 | return View(model);
133 | }
134 | ```
135 |
136 | - Request Url yapısına göre aşağıdaki değerler elde edilecektir.
137 | - **NOT:** Catch-All parametresi her zaman opsiyoneldir.
138 |
139 | ```
140 | >> URL : localhost/Route/Index/3/4/5
141 | Controller : Route
142 | Action : Index
143 | Extra : 3/4/5
144 |
145 | >> URL : localhost/Route/Index/3
146 | Controller : Route
147 | Action : Index
148 | Extra : 3
149 |
150 | >> URL : localhost/Route/Index
151 | Controller : Route
152 | Action : Index
153 | Extra :
154 | ```
155 |
156 | ### 04) Route Constraints ( Rota Kısıtlamaları )
157 | - Route yollarındaki parametrelerin belirli bir kuralda girilmesi ve bu kurallarda girilmediği takdirde yönlendirme yapılmamasını istediğimiz durumlarda kullanırız.
158 | - Rota kısıtlaması yapılan parametreler opsiyonel olarak kullanılmaz.
159 | - Rota kısıtlamaları iki yolla yapılabilir :
160 | 1. Inline Constraints
161 | 2. Maproute içinde Contraints argümanı ile
162 |
163 | ```cs
164 | // Inline Constraint
165 | routes.MapRoute(
166 | name: "default",
167 | template: "{controller=Home}/{action=Index}/{id:int:range(1,100)?}");
168 |
169 | // Maproute parametresi - Single
170 | routes.MapRoute(
171 | name: "default",
172 | template: "{controller}/{action}/{id}",
173 | defaults: new { controller = "Home", action = "Index"},
174 | constraints: new { id = new IntRouteConstraint()});
175 |
176 | // Maproute parametresi - Multiple
177 | routes.MapRoute(
178 | name: "default",
179 | template: "{controller}/{action}/{id}",
180 | defaults: new { controller = "Home", action = "Index"},
181 | constraints: new { id = new CompositeRouteConstraint(
182 | new IRouteConstraint[]
183 | {
184 | new IntRouteConstraint(),
185 | new RangeRouteConstraint(1,100)
186 | }
187 | )});
188 | ```
189 |
190 | #### Regex (Regular Expressions - Düzenli İfadeler) ile Rota Kısıtlaması
191 | - (.) -> Bir karakterin gelecebileceği ve karakteri tanımlamadığımız yerlerde kullanılır.
192 | - (+) -> Kendinden önce gelen karakterin en az bir kere tekrarlanması gerektiğini belirtir.
193 | - (*) -> Kendinden önce gelen karakterin sıfır veya daha fazla tekrarlanması gerektiğini belirtir.
194 | - (?) -> Kendinden önce gelen karakterin en fazla bir kere gelmesi gerektiğini belirtir.
195 | - ([ ]) -> İçindeki karakterlerden birinin geleceğini belirtir.
196 | - [ab] [0-9] [a-z]
197 | - ({ }) -> Kendinden önce gelen karakterin içinde yazılan sayı kadar tekrar edeceğini belirtir.
198 | - (^) -> Metnin başını ifade eder. Kendinden sonra gelen ifadeyi metnin başında arar.
199 | - ($) -> Metnin sonunu ifade eder. Kendinden önce gelen ifadeyi metnin sonunda arar.
200 | - (\s) -> Belirtilen yerde boşluk olması gerektiğini belirtir.
201 | - (\S) -> Belirtilen yerde boşluk olmaması gerektiğini belirtir.
202 | - (\d) -> Sayısal ifade geleceğini belirtir.
203 | - Örnekler:
204 | - Cep telefonu düzeni
205 | - (05)55-444-22-11
206 | - ^(05)\d{2}-\d{3}-\d{2}-\d{2}$
207 |
208 | #### HTTPverb ile Rota Kısıtlaması
209 | - Route düzenine hangi HTTP methodlar ile ulaşılabileceğini belirtmek için kullanırız.
210 | - Birden fazla HTTP method yazılabilir.
211 | - `HttpMethodRouteConstraint` metodu kullanılır.
212 |
213 | #### Örnek:
214 | ```cs
215 | routes.MapRoute(
216 | name: "Haber Kategori",
217 | template: "Haber/Kategori/{catName}/{catID}",
218 | defaults: new { controller = "Home", action = "Kategori" },
219 | constraints: new {
220 | catID = @"^\d{2}$", // Sadece iki basamaklı sayı
221 | catName = @"^[a-z]+$", // Sadece harflerden oluşacak
222 | metod = new HttpMethodRouteConstraint("GET","POST") });
223 | ```
224 |
225 | #### Custom Route Constraints
226 | - Kendi kuralımızı yazmak için öncellikle `IRouteConstraint` interface'inden türeyen bir sınıfa ihtiyacımız vardır.
227 | - Interface'i implement ettiğimizde içinde bir tane `Match` adlı metot gelir ve bu metodun geri dönüş tipi `bool` olmak zorundadır.
228 | - Bu metot içine kuralımızı yazıp, sonucunda, gelen değerin uyup uymadığını belirtmek için true veya false döndürürüz.
229 | - Match metodu içinde toplam 5 tane parametre bulunur :
230 | - **HttpContext:** Request, response, session vs gibi, http özel metotlarını bulunduran parametredir.
231 | - **IRouter:** Constraint'lere ait router metotlarını taşır.
232 | - **routeKey:** Kontrol edilecek route değerinin alındığı yerdir. Custom Constraint metodumuzu üzerinde çalıştırdığımız route elemanının kendisine bunla ulaşırız.
233 | - **RouteValueDictionary:** Url route parametrelerinin bulunduğu sözlük yapısıdır.
234 | - **RouteDirection:** Routing işleminin Http isteğinin URL'inden mi, yoksa custom oluşturulan URL'den mi geldiğini söyler. Enum yapısındadır.
235 | - IncomingRequest : Client tarafından gelen url isteği.
236 | - UrlGeneration : Route ayarları sonucu oluşturulan URL isteği.
237 |
238 | ```cs
239 | public class WeekDaysConstraint : IRouteConstraint
240 | {
241 | List WeekDays = new List() { "pzt", "sali", "car", "per", "cuma" };
242 |
243 | public bool Match(HttpContext httpContext, IRouter route, string routeKey, RouteValueDictionary values, RouteDirection routeDirection)
244 | {
245 | return WeekDays.Contains(values[routeKey]);
246 | }
247 | }
248 | ```
249 |
250 | - MapRoute parametresi olarak kullanımı
251 |
252 | ```cs
253 | routes.MapRoute(
254 | name: "default",
255 | template: "{controller}/{action}/{day}",
256 | defaults: new { controller = "Home", action = "Index"},
257 | constraints: new { day = new WeekDaysConstraint()});
258 | ```
259 |
260 | - Eğer metot inline constraint olarak kullanılacaksa, bunun servis olarak eklenmesi gerekmektedir.
261 |
262 | ```cs
263 | public void ConfigureServices(IServiceCollection services)
264 | {
265 | services.AddMvc();
266 |
267 | services.Configure(option =>
268 | option.ConstraintMap.Add("weekday", typeof(WeekDaysConstraint))
269 | );
270 | }
271 |
272 | public void Configure(IApplicationBuilder app, IHostingEnvironment env)
273 | {
274 | app.UseMvc(routes =>
275 | {
276 | routes.MapRoute(
277 | name: "default",
278 | template: "{controller}/{action}/{day:weekday}",
279 | defaults: new { controller = "Home", action = "Index" });
280 | });
281 | }
282 | ```
283 |
284 |
285 | ### 05) Attribute Routing
286 | - Attribute Route ayarlaması kullanılan actionlara, `{controller}/{action}` üzerinden artık bağlanılınamaz.
287 | - Attribute Routing kullanılacak action üzerinde aşağıdaki gibi tanımlama yapılır:
288 | - `[Route(, Name=, Order=)]`
289 | - **template** : Kullanıcı tarafından gelecek isteğin url tanımlanması burada yapılır.
290 | - **name** : Route yönlendirmemize bir isim vermemizi sağlar. Bu ismin tekrarlanmaması gereklidir.
291 | - **order** : Route ayarlarını sıralamamızı sağlar. Birbirine benzeyen yapılardan öncelikle hangisinin sorgulanacağını buradaki sıraya göre yaparız.
292 | - Bir action üzerinde birden fazla attribute routing tanımlaması yapılabilir.
293 |
294 | ```cs
295 | public class HomeController : Controller
296 | {
297 | [Route("")]
298 | [Route("Home")]
299 | [Route("Home/Index")]
300 | public IActionResult Index()
301 | {
302 | return View();
303 | }
304 | [Route("Home/About")]
305 | public IActionResult About()
306 | {
307 | return View();
308 | }
309 | [Route("Home/Contact")]
310 | public IActionResult Contact()
311 | {
312 | return View();
313 | }
314 | }
315 | ```
316 |
317 | #### Http[Verb] ile Tanımlama
318 | - Özellikle Rest API yapılarında, isimleri aynı fakat metotları farklı end-pointler oluşturmak için kullanılan yöntemdir.
319 | - İki şekilde tanımlaması yapılabilir :
320 |
321 | ```cs
322 | // Yöntem 1
323 | [HttpGet]
324 | [Route("Kullanici")]
325 | public IActionResult Goster() => View();
326 |
327 | [HttpPost]
328 | [Route("Kullanici")]
329 | public IActionResult Ekle() => View();
330 |
331 | // Yöntem 2
332 | [HttpGet("/Kullanici")]
333 | public IActionResult Goster() => View();
334 |
335 | [HttpPost("/Kullanici")]
336 | public IActionResult Ekle() => View();
337 | ```
338 |
339 | #### Kök Yol Tanımlama
340 | ```cs
341 | [Route("Haber")]
342 | public class HaberlerController : Controller
343 | {
344 | // site/haber
345 | public IActionResult Anasayfa() => View();
346 |
347 | // site/haber/icerik
348 | [Route("Icerik")]
349 | public IActionResult Icerik() => View();
350 |
351 | // site/bagimsiz
352 | [Route("~/bagimsiz")]
353 | public IActionResult Bagimsiz() => View();
354 | }
355 | ```
356 | - Controller üzerinde route tanımlaması yapılırsa, altındaki tüm action metotlar için bu tanımlama kök route tanımlaması olur.
357 | - Bu controller içinde herhangi bir action üzerinde route tanımlaması yapılmazsa, bu action default olarak ulaşılabilir hale gelir.
358 | - **NOT** : Kök route tanımlaması bulunan bir controller içinde en fazla bir tane default action boş bırakılabilir. Birden fazla bırakıldığı durumda hata verir.
359 | - Kök route tanımlaması yapılan bir controller içinde `~/` ifadesi kullanılarak bu tanımlama ezilebilir. Böylece ana site yolu üzerinden belirlenen yola erişim sağlanabilir.
360 |
361 | #### Route Template Özel isimleri
362 | - O an içinde bulunulan default yolu tanımlamak için kullanılan özel isimlerdir.
363 | - Bu isimlendirmeler 3 tanedir:
364 | - `[controller]`
365 | - `[action]`
366 | - `[area]`
367 |
368 | ```cs
369 | [Route("[controller]/[action]")]
370 | public class ProductsController : Controller
371 | {
372 | [HttpGet] // Matches '/Products/List'
373 | public IActionResult List() {
374 | // ...
375 | }
376 |
377 | [HttpGet("{id}")] // Matches '/Products/Edit/{id}'
378 | public IActionResult Edit(int id) {
379 | // ...
380 | }
381 | }
382 | ```
383 |
384 | #### AreaAttribute Kullanımı
385 | - Bir controller veya areanın başka bir areaya ait davranmasını istiyorsak bu yöntemi kullanabiliriz.
386 |
387 | ```cs
388 | [Area("Blog")]
389 | public class UsersController : Controller
390 | {
391 | public IActionResult AddUser()
392 | {
393 | return View();
394 | }
395 | }
396 | ```
397 |
398 | #### Multiple Routes
399 | ```cs
400 | [Route("Store")]
401 | [Route("[controller]")]
402 | public class ProductsController : Controller
403 | {
404 | [HttpPost("Buy")] // Matches 'Products/Buy' and 'Store/Buy'
405 | [HttpPost("Checkout")] // Matches 'Products/Checkout' and 'Store/Checkout'
406 | public IActionResult Buy()
407 | }
408 | ```
409 |
410 | #### Opsiyonel Parametre
411 | - Opsiyonel parametre, yanına soru işareti (?) getirilerek tanımlanır.
412 | - Böylece bu parametre gelmediği durumlarda hata gösterilmez.
413 | ```cs
414 | [Route("Icerik/{gelenDeger?}")]
415 | public IActionResult Icerik(string gelenDeger)
416 | {
417 | ViewBag.gelenDeger = gelenDeger;
418 | return View();
419 | }
420 | ```
421 |
422 | #### Parametreye Default Değer Atama
423 | ```cs
424 | [Route("Icerik/{gelenDeger=deneme}")]
425 | public IActionResult Icerik(string gelenDeger)
426 | {
427 | ViewBag.gelenDeger = gelenDeger;
428 | return View();
429 | }
430 | ```
431 |
432 | #### Rota Kısıtlamaları
433 | - Girilen route parametrelerine kısıtlama getirmek için kullanılır.
434 | - Referans : https://docs.microsoft.com/en-us/aspnet/core/fundamentals/routing#route-template-reference
435 |
436 | ```cs
437 | [Route("Icerik/{icerikID:int:min(1)?}")]
438 | public IActionResult Icerik(int icerikID)
439 | {
440 | ViewBag.gelenDeger = icerikID;
441 | return View();
442 | }
443 | ```
444 |
445 | ### 06 - Roting Constraints Listesi
446 |
447 | #### Data Tipi Kontrolü
448 |
449 | CONSTRAINT | INLINE | CLASS | NOTES
450 | --- | --- | --- | ---
451 | int |{id:int} | IntRouteConstraint | Constrains a route parameter to represent only 32-bit integer values
452 | alpha | {id:alpha} | AlphaRouteConstraint | Constrains a route parameter to contain only lowercase or uppercase letters A through Z in the English alphabet.
453 | bool | {id:bool} | BoolRouteConstraint | Constrains a route parameter to represent only Boolean values.
454 | datetime | {id:datetime} | DateTimeRouteConstraint | Constrains a route parameter to represent only DateTime values.
455 | decimal | {id:decimal} | DecimalRouteConstraint | Constrains a route parameter to represent only decimal values.
456 | double | {id:double} | DoubleRouteConstraint | Constrains a route parameter to represent only 64-bit floating-point values
457 | float | {id:float} | FloatRouteConstraint | Matches a valid float value (in the invariant culture - see warning)
458 | guid | {id:guid} | GuidRouteConstraint | Matches a valid Guid value
459 |
460 | #### Uzunluk ve İçerik Kontrolü
461 |
462 | CONSTRAINT | INLINE | CLASS | NOTES
463 | --- | --- | --- | ---
464 | length(length) | {id:length(12)} | LengthRouteConstraint | Constrains a route parameter to be a string of a given length or within a given range of lengths.
465 | maxlength(value) | {id:maxlength(8)} | MaxLengthRouteConstraint | Constrains a route parameter to be a string with a maximum length.
466 | minlength(value) | {id:minlength(4)} | MinLengthRouteConstraint | Constrains a route parameter to be a string with a maximum length.
467 | range(min,max) | {id:range(18,120)} | RangeRouteConstraint | Constraints a route parameter to be an integer within a given range of values.
468 | min(value) | {id:min(18)} | MinRouteConstraint | Constrains a route parameter to be a long with a minimum value.
469 | max(value) | {id:max(120)} | MaxRouteConstraint | Constrains a route parameter to be an integer with a maximum value.
470 |
471 | #### Regex Kontrolü
472 |
473 | CONSTRAINT | INLINE | CLASS | NOTES
474 | --- | --- | --- | ---
475 | regex(expression) | {ssn:regex(^\\d{{3}}-\\d{{2}}-\\d{{4}}$)} | RegexRouteConstraint | Constrains a route parameter to match a regular expression.
--------------------------------------------------------------------------------