Asp.Net Core’da Memory Cache ve AngularJs Kullanma
Selamlar,
Bugün bir önceki makalede işlenen, Osx işletim sisteminde webapi den çekilen datanın, Mvc bir ara yüzde gösterilmesi konusuna derinlemesine devam edeceğiz. Bu makalede ilgili datanın değişmesi durumunda, bunun client’lara nasıl yansıtılacağını hep beraber inceleyeceğiz. Performans amaçlı Memory Cache kullanıp, view katmanını AngularJS ile kodlayacağız.
Update Asp.Net Core 1.0.1:
İlk makalenin üstünden bu yana(1 hafta :) ) geçmesine rağmen Asp.Net Core 1.0.1 çıktı. Osx ortamında güncelleme için yandaki adresten https://www.microsoft.com/net/download macOS3 işletim sistemine ait paket indirilir. Daha sonra var olan projede, versiyon güncellenmesi için project.json dosyasında, aşağıdaki satırlar değiştirilir. Son olarak “dotnet restore” komutu çalıştırılarak ilgili paketler indirilir.
1 2 |
"Microsoft.AspNetCore.Mvc": "1.0.*", "Microsoft.AspNetCore.Server.Kestrel": "1.0.1" |
MameCache:
İlk yapılması gereken iş, datalar çekilmeden önce yüksek trafiğin kaldırabilmesi için bir Mame Cache’in projeye dahil edilmesidir.
Project.json: dosyasına aşağıdaki paketler eklenir.
1 2 |
"Microsoft.Extensions.Caching.Abstractions": "1.0.*", "Microsoft.Extensions.Caching.Memory": "1.0.*" |
Startup.cs’in “ConfigureServices()” methoduna aşağıda görüldüğü gibi “Caching” servisi eklenir.
1 2 3 4 5 6 7 8 |
public void ConfigureServices(IServiceCollection services) { // Add Caching Support services.AddMemoryCache(); // Add framework services. services.AddMvc(); } |
Startup.cs’in “Startup()” : Aşağıda görüldüğü gibi singleton olarak yeni bir “MemoryCache” oluşturulur. Bunun HomeController sınıfının constructor’ı içinde injection ile oluşturulması malesef “Asp.Net Core 1.0.1” versiyonunda başarılı olmadı.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
public static IMemoryCache _memoryCache; public Startup(IHostingEnvironment env) { if (_memoryCache == null) _memoryCache = new MemoryCache(new MemoryCacheOptions()); var builder = new ConfigurationBuilder() .SetBasePath(env.ContentRootPath) .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true) .AddEnvironmentVariables(); Configuration = builder.Build(); } |
Home Controller.cs: “ShowExchange()” ve “ShowExchange(string title)” aşağıdaki gibi değiştirilmiştir.
- “Startup._memoryCache.TryGetValue()” methodu ile ilgili “Cache_Data” veya “title“‘a göre value olup olmadığına bakılır.
- Eğer kayıt yok ise sayfanın başında tanımlanan “cached” değişkenine, “MemoryCacheEntryOptions()” sınıfı ile tanımlanan 1 dakkalık timeout süreli option ve webapi servisinden çekilen data, belirlenen Cache ismi ile atanı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 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 |
[Route("Show")] public async Task<ActionResult> ShowExchange() { var CACHEKEY = "Cache_Data"; List<DataModel> cached; if (!Startup._memoryCache.TryGetValue(CACHEKEY, out cached)) { using (HttpClient client = new HttpClient()) { var response = await client.GetAsync("http://localhost:1455/api/data"); var model = JsonConvert.DeserializeObject<List<DataModel>>( response.Content.ReadAsStringAsync().Result); // Memory Cache İşlemleri cached = model; var opts = new MemoryCacheEntryOptions() { SlidingExpiration = TimeSpan.FromMinutes(1) }; Startup._memoryCache.Set(CACHEKEY, cached, opts); } } return View(cached); } [Route("show/{title}")] public async Task<ActionResult> ShowExchange(string title) { List<DataModel> cached; if (!Startup._memoryCache.TryGetValue(title, out cached)) { using (HttpClient client = new HttpClient()) { var response = await client.GetAsync("http://localhost:1455/api/data/" + title); var data = JsonConvert.DeserializeObject<DataModel>( response.Content.ReadAsStringAsync().Result); List<DataModel> model = new List<DataModel>(); model.Add(data); // Memory Cache İşlemleri cached = model; var opts = new MemoryCacheEntryOptions() { SlidingExpiration = TimeSpan.FromMinutes(1) }; Startup._memoryCache.Set(title, cached, opts); } } return View(cached); } |
AngularJS : Projeye angularJs’i “Bower” kullanarak aşağıdaki komut ile yüklenir. “bower install angular“. Daha sonra da “dotnet restore” komutu ile ilgili paketler indirilir.
Eğer makinanızda Bower yok ise aşağıdaki komut ile indirebilirsiniz.
1 |
npm install -g bower |
Bütün bu işlemlerden sonra proje açıldığında “wwwroot/lib/angular” altında aşağıdaki fileların gözükmesi gerekmektedir.
Şimdi gelin kodları angularJS’e göre tekrardan yeni bir view ile kodlayalım.
Views/Home/AngularExchnage.cshtml : “AngularExchange” adında yeni bir view aşağıdaki komut yeoman kullanılarak eklenir.
1 |
yo aspnet:MvcView AngularExchange |
HomeController.cs (AngularExchange()):
1 2 3 4 |
public ActionResult AngularExchange() { return View(); } |
exchange.js(1): Sayfa içerisinde ilgili “app” module aşağıda görüldüğü gibi oluşturulduktan sonra, tanımlanan “Controller” altında, sayfanın yüklenmesi sırasında ilgili webapi servisine bağlanılır. Ve “$scope.Exchange” dizisi “List<DataModel>” ile doldurulur.
Custom Filter olan “spaceless”: Servisden gelen kağıt isimlerindeki ” ” boşluk karakterini “-” karakter ile değiştiren custom bir functiondir. Amaç url bilgisinde boşluk karakteri koymamaktır.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
var app = angular.module('app', []); app.controller('Controller', function ($scope, $http) { $scope.Exchanges = []; $http({ method: 'GET', url: '/api/data' }).success(function (result) { $scope.Exchanges = result; console.log("data: "+JSON.stringify($scope.Exchanges)); }); }); app.filter('spaceless', function () { return function (input) { if (input) { return input.replace(/\s+/g, '-'); } } }); |
AngularExchange.cshtml: Aşağıda görüldüğü gibi “exchange.js” sayfaya eklenmiştir. “ng-repeat” ile çekilen “Exchange” datası, bootstarp css kullanılarak ekrana yukarıda görüldüğü gibi basılmıştır.
“ng-href” ie tanımlanan url bilgisi [“data.name | spaceless“] filter’i ile ekrana basılmıştı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 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
<script src="~/lib/jquery/src/jquery.js"></script> <script src="~/lib/angular/angular.min.js"></script> <link href="~/lib/bootstrap/dist/css/bootstrap.min.css" rel="stylesheet" /> <script src="~/lib/bootstrap/dist/js/bootstrap.min.js"></script> <script src="~/js/exchange.js"></script> <body ng-app="app"> <div ng-controller="Controller"> <div class="container"> <div class="jumbotron"> <h1>WellComeTo Angular Live Exchange</h1> <p>Result Change Real Time With AngularJS!</p> </div> <table class="table"> <thead> <tr> <th class="hBig">Hisse</th> <th class="hNarrow">Son</th> <th class="hNarrow">Dün</th> <th class="hNarrow">%</th> <th class="hNarrow">Yüksek</th> <th class="hNarrow">Düşük</th> <th class="hNarrow">Ağ.Ortalama</th> <th class="hWide">Hacim(LOT)</th> <th class="hWide">Hacim(TL)</th> </tr> </thead> <tbody> <tr ng-repeat="data in Exchanges" class="success"> <td><a ng-href="/show/{{data.name | spaceless}}" target="_blank">{{ data.name }}</a> <span class="s up"></span></td> <td>{{ data.son }}</td> <td>{{ data.dun}}</td> <td>{{ data.yuzde }}</td> <td>{{ data.yuksek }}</td> <td>{{ data.dusuk }}</td> <td>{{ data.ort }}</td> <td>{{ data.hacimLot }}</td> <td>{{ data.hacimTl }}</td> </tr </tbody> </table> </div> </div> </body> |
Şimdi sıra geldi datanı sürekli değiştiği durumda bunu client’a bildirmeye. Bu iş için “Socket” teknoloji kullanmak gayet mantıklı bir çözümdür. Ben OsX işletim sisteminde signalR kullanmaya çalıştım. Asp.Net Core 1.0.1, windows 10 işletim sisteminde signalR in RC versiyonları ile çalışsa da, ben OsX işletim sisteminde henüz çalıştırmayı başaramadım. 4 gözle signalR 3.0’ı beklemekteyim :) Bu durumda angularJS modeli timer ile belli bir zaman aralığında doldurmak 2. bir alternatif çözüm.
Önemli Not: Bu projede timer’ın çalışacağı ekran, client sayısı 5’i geçmeyecek bir admin monitör sayfasıdır. Bu neden ile performansı etkiliyecek pek bir durum yoktur. Ancak bu admin sayfası 1000’lerce kişinin göreceği bir sayfa olsa idi, bu yöntem çok yanlış ve kesin sunucuları tavan yapan bir çözüm olurdu. O zaman tek çareniz node.js, Go veya signalR gibi socket teknolojileri kullanmak olacaktı.
exchange.js(2): Aşağıda görüldüğü gibi “$interval” directive’i kullanılmıştır. Ve her “60000” yani dakikada 1 kere, webapi servisinden yeni bir data çekilmektedir.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
var app = angular.module('app', []); app.controller('Controller', function ($scope, $http, $interval) { $scope.Exchanges = []; $interval(function () { $http({ method: 'GET', url: '/api/data' }).success(function (result) { $scope.Exchanges = result; console.log("data: " + JSON.stringify($scope.Exchanges)); }); }, 60000); }); app.filter('spaceless', function () { return function (input) { if (input) { return input.replace(/\s+/g, '-'); } } }); |
Böylece geldik bir makalenin daha sonuna. Bazen ihtiyaçlara göre daha basit çözümler üretmek, hem zaman hem de emek anlamında büyük kazançlar sağlıyabilir. Örneğin bu projede olduğu gibi, çekilen datanın real time olmasının hayati bir önem taşımaması ve kullanıcı sayısının bir elin parmaklarını geçmemesi, websocket teknolojisi yerine basit bir “timer interval” kullanımının çok daha başarılı bir sonuç vermesini sağlıyabilir.
Yen bir makalede görüşmek üzere hoşçakalın.
Source: https://docs.microsoft.com/tr-tr/aspnet/core/performance/caching/memory?view=aspnetcore-1.0
Hocam gayet stabil çalışıyor .net core mac osx de https://github.com/aspnet/SignalR-Server
Selamlar Kaan,
Kendin 0 dan bir proje yaratıp ilgili paketleri indirmeyi denedin mi?
Eğer herşeyi kendin yapıp çalıştırdı isen bir github adresini alırım :)
İyi çalışmalar.
Merhabalar, yazınız çok güzel öncelikle
Bir sorum olucak “memorycache” controller seviyesinde çalışıyor ama .net core web projesine referans ettiğim library project içinde çalışmıyor ben mi birşeyi yanlış yapıyorum yoksa normal mi ?
.net core 2.0
Selamlar,
Öncelikle teşekkürler.
WebApi içerisinde cache yapabilirsiniz. Versiyon ile ilgili bir sorun olabilir.
Hoşçakalın.