ASP.NET vNext WebApplication ile Yeni Özellikler
Selamlar;
Çok yakın zamanda Microsoft çoklu platformda çalışabilecek, açık kaynak kodlu, mobil ve cloud çalışma tabanlı yeni ürünü Asp.Net vNext ürününü duyurdu. Doğruyu söylemek gerekir ise 2014’ün son ayına girdiğimiz bu günlerde Microsoft için gerçekten de heycan verici bir yıl sonu oluyor. Artık açık kaynak kodu ile, birçok developer’ın ilgisini üstüne çekeceği aşikar. Bu makalemde ASP.NET vNext ve Visual Studio 14 CTP3 ile yeni gelen daha önce değinmediğim ilginizi çekebilecek farklı özellikleri tanıtacağım.
Öncelikle ASP.NET vNext Web Application projesini aşağıda görüldüğü gibi yaratıyoruz.
vNextFutures adında yarattığım projede dikkatimi çeken ilk şey Solution Explorer ve proje yapısı oluyor. Aşağıda görüldüğü gibi web.config veya Global.asax dosyaları yok. Bunu yerine config.json, project.json ve startup.cs dosyaları var. Hepsini tek tek inceleyeceğiz.
Config.json:
1 2 3 4 5 6 7 |
{ "Data": { "DefaultConnection": { "ConnectionString": "Server=(localdb)\\mssqllocaldb;Database=aspnetvnext-vNextFutures-512d3eb7-b7fc-4dcf-b6e3-6d098a186a30;Trusted_Connection=True;MultipleActiveResultSets=true" } } } |
Startup.cs : Configure() function içermektedir.Http pipeline ayarları burdan yapılır. Routing işlemleri gene burdan ayarlanır. Konfigurasyon ayarları yukarıda ki gibi Config.json ile tanımlanır. Mesela yukarıda default connection string belirlenmiştir. Ayrıca kullanılacak servisler gene startup.cs’de tanımlanır. İstenirse aşağıda görüldüğü gibi SqlServer bırakılıp memory storage’a geçilebilir.
// Set up application services
app.UseServices(services =>
{
// Add EF services to the services container
services.AddEntityFramework()
.AddInMemoryStore();
//.AddSqlServer();
MVC Service ve Identity Service’de Startup.cs’den ayarlanmaktadır.
Startup.cs:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 |
using System; using Microsoft.AspNet.Builder; using Microsoft.AspNet.Http; using Microsoft.AspNet.Identity; using Microsoft.AspNet.Routing; using Microsoft.AspNet.Security.Cookies; using Microsoft.Data.Entity; using Microsoft.Framework.ConfigurationModel; using Microsoft.Framework.DependencyInjection; using vNextFutures.Models; namespace vNextFutures { public class Startup { public void Configure(IApplicationBuilder app) { // Setup configuration sources var configuration = new Configuration(); configuration.AddJsonFile("config.json"); configuration.AddEnvironmentVariables(); // Set up application services app.UseServices(services => { // Add EF services to the services container services.AddEntityFramework() .AddSqlServer(); // Configure DbContext services.SetupOptions<DbContextOptions>(options => { options.UseSqlServer(configuration.Get("Data:DefaultConnection:ConnectionString")); }); // Add Identity services to the services container services.AddIdentitySqlServer<ApplicationDbContext, ApplicationUser>() .AddAuthentication(); // Add MVC services to the services container services.AddMvc(); }); // Enable Browser Link support app.UseBrowserLink(); // Add static files to the request pipeline app.UseStaticFiles(); // Add cookie-based authentication to the request pipeline app.UseCookieAuthentication(new CookieAuthenticationOptions { AuthenticationType = ClaimsIdentityOptions.DefaultAuthenticationType, LoginPath = new PathString("/Account/Login"), }); // Add MVC to the request pipeline app.UseMvc(routes => { routes.MapRoute( name: "default", template: "{controller}/{action}/{id?}", defaults: new { controller = "Home", action = "Index" }); routes.MapRoute( name: "api", template: "{controller}/{id?}"); }); } } } |
Project.json: project.json’da görülen aşağıdaki liste aynı Nuget’de olduğu gibi uygulamamıza dependency olan kütüpahanelerin versiyon numaraları ile gözüken listesidir.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
{ "webroot": "wwwroot", "exclude": "wwwroot/**/*.*", "dependencies": { "Microsoft.AspNet.Mvc": "6.0.0-alpha4", "Microsoft.AspNet.Identity.SqlServer": "3.0.0-alpha4", "Microsoft.AspNet.Identity.Authentication": "3.0.0-alpha4", "Microsoft.AspNet.Security.Cookies": "1.0.0-alpha4", "Microsoft.AspNet.Server.IIS": "1.0.0-alpha4", "Microsoft.AspNet.Server.WebListener": "1.0.0-alpha4", "Microsoft.AspNet.StaticFiles": "1.0.0-alpha4", "Microsoft.Framework.ConfigurationModel.Json": "1.0.0-alpha4", "Microsoft.VisualStudio.Web.BrowserLink.Loader": "14.0.0-alpha4" }, "commands": { /* Change the port number when you are self hosting this application */ "web": "Microsoft.AspNet.Hosting --server Microsoft.AspNet.Server.WebListener --server.urls http://localhost:5000" }, "frameworks": { "aspnet50" : {}, "aspnetcore50" : { } } } |
- Bu sadece sıradan bir text dosya değildir. Yukarıda görüldüğü gibi listede görülen tüm dependency dosyalar projemizin referance dosyasında aynen gözükmektedir.
- Artık referance’ı sağ tıklayıp istenen dll’i ekleme devri bitmiştir. İlgili ekran görüntüsü aşağıdadır.
- Istenen paket uygulamamıza referance edilmesi işlemi project.json dosyası üzerinden dependencies altına eklenerek yapılmaktadır. Tanımlanan dosyalar Referance altına da otomatik olarak eklenmektedir. Gene project.json dosyası üzerinden kaldırılan dependencyler Referance dosyası üzerinden’de otomatik kalkmaktadır. Aşağıda projemize EntityFramework.SqlServer paketini ekleyip çıkararak solutiondaki referance altında olan değişiklikleri gözlemlemekteyiz.
.kproj:Visual Studio’da yeni bir proje yarattığımızda, yukarıda görüldüğü gibi .kproj uzantlı bir dosya, root project’de oluşur. Bu alışık olmadığımız file oluşturduğumuz application’ın Visual Studio tarafından yüklenip çalıştırılması için gerekli ayarları barındırır.
vNextFutures.kproj:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
<?xml version="1.0" encoding="utf-8"?> <Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <PropertyGroup> <VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion> <VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath> </PropertyGroup> <Import Project="$(VSToolsPath)\AspNet\Microsoft.Web.AspNet.Props" Condition="'$(VSToolsPath)' != ''" /> <PropertyGroup Label="Globals"> <ProjectGuid>c12439f1-02b1-45ef-80f0-dfd522ee3a0f</ProjectGuid> <OutputType>Web</OutputType> <RootNamespace>vNextFutures</RootNamespace> </PropertyGroup> <PropertyGroup> <SchemaVersion>2.0</SchemaVersion> <DevelopmentServerPort>7076</DevelopmentServerPort> </PropertyGroup> <Import Project="$(VSToolsPath)\AspNet\Microsoft.Web.AspNet.targets" Condition="'$(VSToolsPath)' != ''" /> </Project> |
Dynamic Code Compilation [Roslyn Compiler]:
ASP.NET’in kullandığı 2 tip derleme yöntemi vardır. Biri projenin derlenmesi(.csproj/.vbproj) ve biri de çalışma zamanı yani Runtime derleme (uygulama çalışırken). vNext ile başlarsak, şimdi elimizde Roslyn compiler var. Roslyn’in en önemli özelliği dinamic derleme yapabilmesidir. Kısaca uygulamada değişiklik yapılınca tekrardan derlemeye gerek yoktur. Bu zamana kadar Visual Studio’da code tarafında değişiklik yapınca , Ctrl+Shift+B’ye basarak ve sayfayı refresh ederek yapılan değişiklikler görülebiliyordu. Artık Roslyn ile code tarafında değişklik yapılınca uygulama yeniden derlenmeden sadece F5 ile browser yenilerek dinamic derleme ile ilgili değişiklikler görülebilir.
Aşağıda bunla ilgi örneği görebilirsiniz. HomeController’da About sayfasındaki mesaj çalışma zamanında değiştirilince tekrardan derlemeden ilgili değişiklik yansıtılır. Uygulama Ctrl + F5 ile çalıştırılınca Aşağıdaki ekran görüntüsü alınır.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
ing System; using System.Collections.Generic; using System.Linq; using Microsoft.AspNet.Mvc; namespace vNextFutures.Controllers { public class HomeController : Controller { public IActionResult Index() { return View(); } public IActionResult About() { ViewBag.Message = "Sayfanın ilk açılış hali..."; return View(); } public IActionResult Contact() { ViewBag.Message = "Your contact page."; return View(); } } } |
About action alttaki gibi değiştirilip derlenmeden sadece save edilip sayfanın F5 ile yenilenmesi ile alttaki sonuç elde edilir.
1 2 3 4 5 6 |
public IActionResult About() { ViewBag.Message = "Sayfanın değişen hali..."; return View(); } |
ASP.NET vNext ile Self-hosting : Şimdi sıra geldi benim en favori yeniliklerden biri olarak gördüğüm kendi kendine host etme özelliğine. Bu zamana kadar Web API 2 ve SignalR 2 kendi kendine host edebiliyordu. Şimdi bu örneğimizde vNext ile command prompt dan uygulamamızı host edicez. Öncelikle K Version Manager (KVM)’nin kullanılması için alttaki komutun command prompt’dan yazılarak çalıştırılıp kvm local makinaya install edilmelidir.
@powershell -NoProfile -ExecutionPolicy unrestricted -Command “iex ((new-object net.webclient).DownloadString(‘https://raw.githubusercontent.com/aspnet/Home/master/kvminstall.ps1’))”
Command Prompt’da yazacağımız ilk komut “kvm list” dir. Bize local makinamızdaki .NET Frameworkleri listeler. Mesela bende aşağıda gördüğünüz gibi 4 tane vardır.
Ben default’ı kullanacağım. Bu nedenle “kvm use default” komutunu yazdım.
Şimdi en güzel kısma geldik. IIS olmadan yayın yapmaya. “k web” komutu HTTP listener’ı projeyi derlemeye gerek olmadan yayımlar. Ayrıca “k web” komutu main için Microsoft.AspNet.Hosting API’ye bakmasını sağlar ve uygulamayı host url’de yayınlar . “k web” komutu vNextFutures uygulamamızda project.json dosyasında aşağıda görüldüğü gibi zaten default tanımlıdır. Kodda da görüldüğü gibi port olarak :5000‘i kullanmaktadır.
1 2 3 4 |
"commands": { /* Change the port number when you are self hosting this application */ "web": "Microsoft.AspNet.Hosting --server Microsoft.AspNet.Server.WebListener --server.urls http://localhost:5000" }, |
Komut aşağıda görüldüğü gibi çalıştırılır.
Sonuç yukarıda görüldüğü gibidir. Ve en muhteşemi status bar’a bakıldığında IIS Express running yazısı görülmemektedir. Bu sizce de mükemmel değil mi:)
Yayını durdurmak için “k stop” komutu yazılır.
ASP.NET MVC 6.0 Yenilik ve Değişiklikleri:
ASP.NET vNext; MVC, Web API, ve Web Pages frameworkleri birleşti ve MVC 6 dediğimiz tek bir framework oldu.
- Aşağıda görüldüğü gibi startup.cs’de tanımlanan servislerde AddMvc() methodu ile servislere Mvc’de eklenmektedir. Bu satır kaldırıldığı takdirde 500 internal server error hatası alınır.
app.UseServices(services =>
{
// Add MVC services to the services container
services.AddMvc();
});
- Startup.cs’i inceler isek aşağıda görüldüğü gibi MVC 6’da template yapısının geldiği görülebilir.
- Gene aşağıda görüldüğü gibi UrlParameter.Optional kaldırılmış ve bunu yerine ? gelmiştir. Ve id? ile birlikte id parametresinin optional olduğu belirtilmiştir.
template: “{controller}/{action}/{id?}”
1 2 3 4 5 6 7 8 9 10 11 |
app.UseMvc(routes => { routes.MapRoute( name: "default", template: "{controller}/{action}/{id?}", defaults: new { controller = "Home", action = "Index" }); routes.MapRoute( name: "api", template: "{controller}/{id?}"); }); |
- View’da aşağıdaki modeli tanıtalım
1234@{var codeLanguage = new[] { new { LanguageID = "C#", CoderName = "Bora Kaşmer", City = "İstanbul" },new {LanguageID = "Swift", CoderName = "Gökhan Gözütok", City = "London"} };}
Daha sonra da view içinde ilgili codeLanguage modelini aşağıdaki gibi çağıralım.
12345678910111213141516171819202122<table border="1"><tr><th>Language ID</th><th>Coder Name</th><th>City</th></tr><tr>@foreach (var item in codeLanguage){<tr><td>@item.LanguageID</td><td>@item.CoderName</td><td>@item.City</td></tr>}</tr></table>
Sonuç Ekranı aşağıdaki gibidir.
- Aynı işlemi controller tarafından’da yapabiliriz. Öncelikle aşağıdaki class yaratılır.
1 2 3 4 5 6 |
public class Customers { public string LanguageID { get; set; } public string CoderName { get; set; } public string City { get; set; } } |
Daha sonra ilgili view’a model controller’ın action’ında aşağıdaki gibi gönderilir.
1 2 3 4 5 6 7 8 |
public class HomeController : Controller { public IActionResult Index() { Customers[] codeLanguage = new[] { new Customers{ LanguageID = "C#", CoderName = "Bora Kaşmer", City = "İstanbul" }, new Customers{LanguageID = "Swift", CoderName = "Gökhan Gözütok", City = "London"} }; return View(codeLanguage); } |
View tarafında model aşağıdaki gibi çağrılır. Sonuç önceki ile aynıdır.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
<table border="1"> <tr> <th> Language ID </th> <th> Coder Name </th> <th> City </th> </tr> <tr> @foreach (var item in Model) { <tr> <td>@item.LanguageID</td> <td>@item.CoderName</td> <td>@item.City</td> </tr> } </tr> </table> |
- ASP.NET MVC 6’da Controller’ı yaratmak için illaki Microsoft.AspNet.Mvc.Controller base classından türetilmesine gerek yoktur. Aşağıda görüldüğü gibi de Clr class veya Poco yazılabilir. Aşağıda [Activate] çok önemli bir attribute’dür. ASP.Net runtime da controller’a çeşitli servisleri inject etmek için kullanılır. HomeController’ı flat olarak yani hiçbir class’dan inherit almadan yaratılmıştır. ViewDataDictionary ile de ilgili model view’a göderilmiştir.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
public class HomeController { // Use the ActivateAttribute to inject services into your controller Customers[] codeLanguage = new[] { new Customers{ LanguageID = "C#", CoderName = "Bora Kaşmer", City = "İstanbul" }, new Customers{LanguageID = "Swift", CoderName = "Gökhan Gözütok", City = "London"} }; [Activate] ViewDataDictionary CustomList { get; set; } public ActionResult Index() { CustomList.Model = codeLanguage; return new ViewResult() { ViewData = CustomList }; } } |
- ASP.NET MVC 6 ve vNext üzerinde çalışılan çok dallı ve büyük projelerde herbiri için folder açıp, kendisine özel yerler belirleme işlemini Areas denen yeni attribute ile yapabilmekteyiz. Burda amaç dağıtık mimariyi en iyi şekilde uygulayabilmektir.Area özlliği, Mvc 6’da controller’ın başına [Area] attribute konarak ve ayrıca route template de {area} parametresi tanımlanarak kullanılırlar. Aşağıda öncelikle custom bir rout yazılmıştır. Bu CustomRoute, CustomArea altındaki Index.cshtml dosyasına yönlendirir. Doğal olarak dosyanın yolu Areas/CustomArea/Views/Home/ Index.cshtml dosyasıdır. Route tanımlama kısmına dikkat edilirse, area tanımlamasını ve optional id parametresinin kullanım şekli yandaki gibi görülmektedir. {area}/{controller}/{action}/{id?}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
public class CustomRoute : TemplateRoute { public CustomRoute(IRouteBuilder routeCollectionBuilder) : base(routeCollectionBuilder.DefaultHandler, "default", "{area}/{controller}/{action}/{id?}", new RouteValueDictionary(new { area = "CustomArea", controller = "Home", action = "Index" }), new RouteValueDictionary(new { }), routeCollectionBuilder.ServiceProvider.GetService<IInlineConstraintResolver>()) { } public override Task RouteAsync(RouteContext context) { return base.RouteAsync(context); } } |
Startup.cs’deki route tanımlarına yukarıda tanımlanan CustomRoute’u aşağıda görüldüğü gibi eklenir.
1 2 3 4 5 |
routes.Routes.Add(new CustomRoute(routes)); ; routes.MapRoute( name: "api", template: "{controller}/{id?}"); |
Aşağıda görüldüğü gibi HomeController’ın başına [Area(“CustomArea”)] attribute konarak Areas/CustomArea/Views/Home/ altındaki dosyalara erişileceği belirtilmiştir.
1 2 3 4 5 6 7 |
[Area("CustomArea")] public class HomeController : Controller { public IActionResult Index() { return View(new GameModel()); } |
Geldik bir makalenin daha sonuna. Bu makalede ASP.NET vNext ile gelen yeniliklerin bir kısmına deyinebildik. Anlatılacak daha birçok yenilik var. İlerleyen makalelerde onlardan da bahsedeceğim.
Yeni bir makalede görüşmek üzere hoşçakalın.
Hocam elinize saglik.
Cok detayli ve anlasilir bir makale olmus.
vNext de bu denli yenilik oldugunu bu makaleye kadar bilmiyordum. Devamini merakla bekliyorum hocam. Hoscakalin.
Selam Veli;
Öncelikle teşekkürler. Bu makalenin devamı olucak..
Hoşçakal.
Selamlar Hocam;
Gene harika bir makale olmuş. Bu konuda türkçe kaynak olarak daha iyisi yok.
Özellikle self hosting muhteşem. Üstüne üstük yayınlanacak port’u projet.json’dan ayarlayabilmek muhteşem. No More IIS. Ayrıca Dynamic Code Compilation muhteşem hocam.
Elinize sağlık.
Teşekkürler Ömer. Self hosting benim de favorim:)
Selamlar Hocam;
Öncelikle çok faydalı bir makale olmuş. Gerçekten çok başarılı.Teşekkürler..
Video ile anlatılan konuları desteklemeniz bence çok güzel. Hem içerik hakkında fikrimiz oluyor. Hem de konunun anlaşılmasında büyük yarar sağlıyor. Ellerinize sağlık. Tüm makalelerinizi canı gönülden takip ediyorum. Sağlıcakla kalın.
Teşekkürler Murat. Anlatılan konuların görsel örnekler ile güzel desteklendiğini düşünüyorum. İlerde belli konular üzerinde tartışma videolarıda çekmeyi planlıyorum.
İyi akşamlar hocam;
Bencede bu konu ile ilgili okuduğum engüzel makale bu. Sadece türkçe kaynaklarda değil yabancı kaynaklar içinde de en iyisi bu. Detaylı ve titiz bir çalışma. Elinize sağlık. Sizin bloğunuzu keşfetmem büyük bir şans. Çok teşekkürler hocam.
Teşekkürler duygu. Bu makalenin devamı da olucak daha deyinmediğim birçok konu var…
Selamlar Hocam;
Asp.Net vNext Yeni Özellikler şeklinde bir arama yapınca çıkan yazılar ile sizin yazınızı karşılaştırınca ne kadar komik kalıyorlar anlatamam. Öncelikle bu konuya bu denli detaylı girdiğiniz için teşekkür ederim. Gerçekten çok güzel bir yazı olmuş. Genelde pek yorum yazmam ama bloğunuz gerçekten bir hazine.
İyi çalışmalar.
Teşekkürler burak. Elimden geleni yapıyorum.
Bilgilendirici bir makale olmuş, emeğinize sağlık.
Teşekkür ederim..
Hocam selamlar,
Her zamanki gibi çok faydalı bir makale olmuş.Anlatımınız yine harika gerçekten çok ama çok Teşekkürler..
Birde Hocam Design Pattern konularına mı değinseniz acaba bu şekilde anlatarak yine bizlere faydalı olacağı kanaatindeyim :)
Saygılarımla
Selam Hakan;
Öncelikle teşekkürler;
Aslında arada design patternla ilgili yazılar yazıyorum.Örnek birkaç makale alttadır. Ama sıklığını arttırırım. Önümüzdeki bir kaç hafta mobile konulara deyineceğim.
http://www.borakasmer.com/interpreter-design-pattern/
http://www.borakasmer.com/the-chain-of-responsibility-pattern/
İyi çalışmalar.
Şuanda .NET Core, vNext’in yerini aldı diyebilir miyiz?
Kesinlikle deriz!