MS Sql Database ile SignalR için Angularjs Optimizasyonu
Selamlar;
Bir önceki makalede, Sql database üzerindeki tabloda değişiklik yapıldığında, WebApi sayesinde bir hub classına bağalanarak, ilgili client’lara signalR sayesinde oyunların listelendiği tüm partial view string’e çevirilip push edilmekte idi. Tüm sayfanın clientlara gönderilmesi büyük bir performans kaybına neden olmakta idi. Bunun için sayfada partial view yerine bir View Model kullanılıp sadece değişen data clientlara gönderilerek, var olan model den sadece bu değişen data replace edilerek clientlara gösterilmelidir. Bunun için Mvc View tarafında Partial View yerine AngularJs kullanıcaz ve sadece değişen datayı signalR ile clientlara push edicez. İsterseniz önce en başta ürünleri partial view yerine AngularJs ve signalR ile nasıl listeleyeceğimize bakalım.
Öncelikle NuGet’den alttaki dosyaların bir önceki versiyonlarını indiriyoruz. En son versiyonlarında angularJS ve signalR’ın birlikte çalışmısında sıkıntılar var arkadaşlar. Eminim bir süre sonra düzeltilecektir.
Aşağıda görüldüğü gibi HomeController Index view’daki kodlar kaldırılmıştır. Bunun yerine GamesHub classında, client Connect olduğu zaman ilgili game data model’i çekilip connect olan client’a push edilir. Yalnız burada isWebUser şeklinde bir kontrol vardır. Amaç client web’den mi yoksa webapi servisinden mi bağlanmaya çalıştığına bakılır. Eğer connection webapi servisin’den yapılıyor ise ilgili game data çekilip push edilmez. Denenir ise client bulunamıyacağı için timeout’a düşülü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 |
using DAL; using Microsoft.AspNet.SignalR; using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; namespace SignalRSqlAngular.Controllers { public class HomeController : Controller { // GET: Home public ActionResult Index() { return View(); } } public class GamesHub : Hub { public static GameShopContext db = new GameShopContext(); public override async System.Threading.Tasks.Task OnConnected() { if(Context.QueryString["isWebUser"]=="true") { var data = from prod in db.tblGames where prod.CategoryID == 2 select new { prod.Name, prod.Price, prod.ID, prod.ImageUrl }; await Clients.Caller.getAllProduct(data); } } } } |
Aşağıda görüldüğü gibi view tarafında ilgili data signalR getAllProduct() client function’ı ile alınıp angularJS’de $scope.Games view modeline atanır. Bu işlem client’ın hub class’ına connect durumunda yapılır. Connection olunmadan önce dikkat ederseniz querystring’e yandaki değer atanmıştır. $.connection.hub.qs = { isWebUser: true }; . Amaç web sayfasından geldiğimizi belirtip server side’daki koşulu geçip ilgili datayı çekebilmektir. Daha sonra ng-repeat ile Games view model’i içinde dönülerek ilgili oyun tablosu doldurulur. Tüm operational işlemler {{ }} parentezler içinde yapılır. Ayrıca price alanında currency filter :| currency:’€’ şeklinde kullanılmaktadı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 |
@{ Layout = null; } <!DOCTYPE html> <html> <head> <link href="~/Content/Site.css" rel="stylesheet" /> <script src="~/Scripts/angular.min.js"></script> <script src="~/Scripts/jquery-2.1.1.min.js"></script> <script src="@Url.Content("~/Scripts/jquery.signalR-2.1.1.min.js")" type="text/javascript"></script> <script src="@Url.Content("~/signalr/hubs")"></script> <script> function Controller($scope) { var messagehub = $.connection.gamesHub; messagehub.client.getAllProduct = function (data) { console.log("All Datas:" + JSON.stringify(data)); $scope.Games = data; $scope.$apply(); } $.connection.hub.qs = { isWebUser: true }; $.connection.hub.start(); } </script> <meta name="viewport" content="width=device-width" /> <title>Index</title> </head> <body> <div id="gamesContent" ng-app="" ng-controller="Controller"> <table> <tr> <td style="padding: 10px" ng-repeat="item in Games"> <div><b>[{{item.ID}}] {{item.Name}}</b></div> <img src="/Content/GamesImage/{{item.ImageUrl}}" /> <div><h4><b><font color="red">{{item.Price | currency:'€'}}</font></b></h4></div> <div><input type='button' value='Buy' class='btnClass' /></div> </td> </tr> </table> </div> </body> </html> |
Aşşağıda görüldüğü gibi connect durumunda tüm data çekilmiş , console’a json formatında log olarak yazılmış ve herbiri ng-repeat ile dönülerek ilgili tabloya basılmıştır.Şimdi artık önceki makaleden de hatırlayacağınız gibi WebApi servisinden değişen oyunun ID’si ile birlikte tetiklenen RefreshData() methodu üzerinde konuşalım. Aşağıda görüldüğü gibi gelen değişen data ID’si ile ilgili data çekilir ve tüm clientlara signalR sayesinde push edilir.
1 2 3 4 5 |
public void RefreshData(int ID) { var changeData = from prod in db.tblGames where prod.ID == ID select new { prod.Name, prod.Price, prod.ID, prod.ImageUrl }; Clients.All.refreshData(changeData); } |
View tarafında ilgili kod aşağıdaki gibidir. Tetiklenen refreshData() function’ı updateDataFromID() functionın’ı değişen oyun ID’si, ViewModel’deki Games dizisisi ve değişe data ile birlikte call edilir. Değişen data’nın ID’si varolan viewModel’de gezilerek aranır. Bu şekilde bulunan data yeni gelen data ile değiştirilir. Ve $scope.$apply() ile bind edilir. Bu şekilde oyun datası ile dolan tablolar yenilenmiş olur.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
<script> function Controller($scope) { messagehub.client.refreshData = function (data) { console.log("Changed Data:"+JSON.stringify(data)); updateDataFromID(data[0].ID, $scope.Games, data[0]); $scope.$apply(); } } function updateDataFromID(ID, Array, Game) { console.log("ID="+ID); for (var index = 0; index < Array.length; index++) { if (Array[index].ID == ID) { Array[index] = Game; console.log("Game Found Index:" + index); } } } </script> |
Güncelenen datalara göre console’a basılan örnek loglar aşağıdadır.
Index.cshtml:
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 |
@{ Layout = null; } <!DOCTYPE html> <html> <head> <link href="~/Content/Site.css" rel="stylesheet" /> <script src="~/Scripts/angular.min.js"></script> <script src="~/Scripts/jquery-2.1.1.min.js"></script> <script src="@Url.Content("~/Scripts/jquery.signalR-2.1.1.min.js")" type="text/javascript"></script> <script src="@Url.Content("~/signalr/hubs")"></script> <script> function Controller($scope) { var messagehub = $.connection.gamesHub; messagehub.client.getAllProduct = function (data) { $scope.Games = data; $scope.$apply(); } messagehub.client.refreshData = function (data) { console.log(JSON.stringify(data)); updateDataFromID(data[0].ID, $scope.Games, data[0]); $scope.$apply(); } $.connection.hub.qs = { isWebUser: true }; $.connection.hub.start(); } function updateDataFromID(ID, Array, Game) { console.log("ID="+ID); for (var index = 0; index < Array.length; index++) { if (Array[index].ID == ID) { Array[index] = Game; console.log("Game Found " + index); } } } </script> <meta name="viewport" content="width=device-width" /> <title>Index</title> </head> <body> <div id="gamesContent" ng-app="" ng-controller="Controller"> <table> <tr> <td style="padding: 10px" ng-repeat="item in Games"> <div><b>[{{item.ID}}] {{item.Name}}</b></div> <img src="/Content/GamesImage/{{item.ImageUrl}}" /> <div><h4><b><font color="red">{{item.Price | currency:'€'}}</font></b></h4></div> <div><input type='button' value='Buy' class='btnClass' /></div> </td> </tr> </table> </div> </body> </html> |
HomeController.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 |
using System.Threading.Tasks; using DAL; using Microsoft.AspNet.SignalR; using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; namespace SignalRSqlAngular.Controllers { public class HomeController : Controller { // GET: Home public ActionResult Index() { return View(); } } public class GamesHub : Hub { public static GameShopContext db = new GameShopContext(); public override async System.Threading.Tasks.Task OnConnected() { if(Context.QueryString["isWebUser"]=="true") { var data = from prod in db.tblGames where prod.CategoryID == 2 select new { prod.Name, prod.Price, prod.ID, prod.ImageUrl }; await Clients.Caller.getAllProduct(data); } } public override async Task OnReconnected() { await OnConnected(); } public void RefreshData(int ID) { var changeData = from prod in db.tblGames where prod.ID == ID select new { prod.Name, prod.Price, prod.ID, prod.ImageUrl }; Clients.All.refreshData(changeData); } } } |
Yukarıda ki örnekte de görüldüğü gibi ilgili değişen datanın ID’si sql den ilgili WebApi servisine ordanda signalR methoduna gönerilmiştir. RefreshData() methodunda ilgili, yani sadece değişen data çekilerek json formatında view’a gönderilmiştir. AngularJS sayesinde var olan viewModel’de değişen data bulunup yenisi ile replace edilmiş ve clientlara gösterilmiştir. Böylece önceki makalede yaptığımız bütün modeli clientlara push etmek yerine sadece değişen data gönderilerek büyük bir performance kaybından kurtulunmuştur.
Bir sonraki makalede görüşmek üzere hoşçakalın.
Source Code: http://www.borakasmer.com/projects/SignalRSqlAngular.rar
Teşekkürler hocam. Ağzınıza sağlık. Çok açıklayıcı olmuş. Angular ile nasıl yapılacağı konusu baya kafama takılmış. Açıkçası harika bir teknoloji. Keşke herkez sizin gibi özgün olsa. Tabii bir de bu kadar bilgili ..
Teşekkürler Cem. Bloğun title’ına layik yazılar yazmaya çalışıyorum!
Cem;
Bora hoca çok sağlamdır. Sonuçta doğrudan Sql ve SignalR le ilgili böyle yabancı kaynak bile nerde ise pek yok.
İnan bu konuda çok şanslıyız. Gelsinler de adamlar türkçe öğrensinler şimdi :)
Teşekkürler Murat. Sen de ne yazmışın ama :)
Selamlar;
Genelde hiç yorum yazmam. Ama bu makaleden sonra dayanamadım. Hocam siz bunca zaman nerede idiniz. Bundan önceki makalenizi de okudum cidden muhteşem. Ne Mvp’ler var.Adı lazım değil tam da bu konuda seminerine katıldım. Yapılan örnek ile bunu kıyaslayınca tofaşla mercedes yok yok bugatti gibi kalıyor :)
Teşekkürler Burakcım. Yalnız bu konuda Türkiye’de tek bir Mvp var. Yani kimden bahsettiğin malesef belli. Hem arkadaşımızı rencide etmeme adına hemde kıyaslamanın hem bu işte hem de başka bir işte hiç doğru olmadığı kanaatindeyim. Sonuçta farklı konularda herkez birbirinden üstündür. O yüzden lütfen Microsoft’un MVP olarak kabul ettiği birine çamur atma. Hatta bilgi konusunda kimseye atma..
:) Hocam nezaman ve nerde seminer vereceksiniz? Ben kesin katılıcam…
Merhabalar
Yazılarınızı ve videolarınızı zevk ile takip ediyorum. Benim size danışmak istediğim bir kaç senaryo var.
Antrenman için bir üniversite otomasyonu hazırlıyorum. Bunu içinde öğrenci sistemi, yemekhane kart sistemi, kütüphane otomasyonu gibi bir üniversitenin tüm işlerini a’dan z’ye görecek şekilde bir otomasyon sistemi olarak düşünebilirsiniz.
SignalR ve AngulaJS uyumu gerçekten muhteşem olmuş buda Mvc framework çatısı altına cuk diye oturmuş buraya kadar sorun yok. Benim danışmak istediğim konu;
– Web üzerinden öğrencilerin ders seçtiğini düşünün ve 1000 öğrenci var her ders için kontenjan 50 kişi olarak belirlendi.
-SignalR ile bir öğrenci A dersini seçtiğine anında (Diğer öğrencinin sayfayı yenilemeden görecek şekilde) kontenjan 49 a düşüyor. Buraya kadar sorun yok.
-Sistem saat 00.00 da açılıyor ve 1000 kişi hucum ediyor anlık 1000 kişi A dersini seçmeye çalışıyor ve 50 kontenjanı olan derse tıklıyor. (Hocanın Bedavadan AA verdiğini farz ediyoruz :) ) Bu senaryoda sistem 51. kişiye izin verir mi ? Hata payı nedir. Bunun için ne kullanmalıyız?
Teşekkürler.
Teşekkürler Bertan.
Hayır gelen 51. kişi istersen sokmaz isin. Olay lock mekanizması. Önceki makalem olan mssql database ile signalr etkilesimleri‘de dikkat edersen orda bu konu üzerinde durdum. Aşşağıda görüldüğü gibi gerekli update islemlerini bu lock içinde yaparsan yapacağın kişi kontrolü filiter’ından 51. kişi kapı duvar olarak geri döner :)
public static object _lock=new object();
lock (_lock)
{
Database İşlemlerini burda yap.
}
İyi çalışmalar…
Hocam, güncel teknolojileri sıklıkla takip ediyorum.
Signalr 2.1 + Angularjs 1.3 + Asp.Net MVC 6 + Azure SQL DB = E-ticaret Web Site + Admin Portal
bu denklem mantıklı mıdır? Bazıları beta sürümünde bile ama böyle bir çılgınlığa siz kalkışırmıydınız? en modern ve en performancelı yapıyı kullanmak istiyorum. Tavsiyeleriniz nelerdir?
Selam Sertaç;
Bunun neresi çılgınlık:) Bu konular zaten bir web developer’ın bilmesinde büyük fayda olan konular. Bunlardan hiçbiri beta değil:) Ben Mvc6, Angularjs, Azure DB ve TypeScript’i zaten iş hayatında bolca kullanıyorum. Yani hepsi kullanılıyor. Bu teknolojler gayet modern ama en performanslı yapı değiller. Eğer web teknolojileri ile uğraşacak isen aynen devam sertaç:)
Hocam Merhaba öncellikle emeğinize sağlık elleriniz dert görmesin :) böyle güzel ve sade anlatım için ayrıca teşekkürler. bir projede yukarıda çok güzel bir biçimde anlattığınız yöntemi kullamak istiyorum. sizin projenizi download ettim ve onun üzerinde öğrenmek için çalışıyorum . herşey çalışıyor fakat databasede bir değişiklik yaptığımda değişiklik clientlar’a push olmuyor sqlden calwebservices proceduresini execute ediyorum bir id ile response null dönüyor nerde yanlış yapıyorum 2 gündür bulamadım yardımcı olabilirseniz sevinirim. Saygılarımla. Çalışmalarınızda başarılar.
Teşekkürler Mahmut;
Öncelikle web sayfası http://localhost:18852 sayfası olarak mı görünüyor ona bak. Eğer farklı bir url’den çalışıyor ise windows services signalR sınıfına erişemiyordur. id’nin null dönmesi CodeFirst poco nesnesi yani ilgili tablonun class’ı ile database’deki tablonun örtüşmemesinden olabilir. Şimdilik aklıma gelenler bunlar..
İyi çalışmalar.
Hocam merhaba, bir önceki sorumda olan problemleri düzelttim ve sizin projenizi çalıştırm. sizin projeniz üzerinden kendi projeme uyarlama yaptım. benim projemde updat değilde tabloya insert edildiğinde insert edilen kayıtı göstermek istiyorum.tüm işlemleri sizin projenizi baz olarak yaptım fakat şöyle bir kaç problem yaşıyorum bana yardımcı ollabilirseniz Çok sevinirim,
1- projeme kullanıcı ve şifre ile giriliyor ve controllerde actionlarda authentication doğrulamısı yaptırıyorum kullanıcı giriş yapmamışsa giriş sayfasına yönlendiriyorum. apide trigger sınıfında
” stockTickerHubProxy.Invoke(“RefreshData”, ID);” satırında
“tatusCode: 404, ReasonPhrase: ‘Not Found’, Version: 1.1, Content: System.Net.Http.StreamContent, Headers:
{
X-SourceFiles: =?UTF-8?B?”
bu hata ile karşılaşıyorum bu authentication kontrolündenmi kaynaklanıyor. bu sorunu nasıl aşarım.
2-callweservices store prosedürünü çalıştırdığımda authentication olmayan bir sayfada
“Zaman uyumsuz bir modül veya işleyici, zaman uyumsuz işlem halen beklemedeyken tamamlandı.” Bu hatayı alıyorum ve şuan kilitli kalmış durumdayım :( ban buralarla ilgili yol göstermenizi istiyorum Saygılarımla. Verdiğim rahatsızlık için çok özür diler çalışmalarınızda başarılar dilerim
Selamlar Mahmut;
Projeni Github’a koy. Bana da link’ini ver. Müsait olunca inceleyim. Bu şekilde sana daha çok yardımcı olabilirim.
İyi çalışmalar…
https://github.com/pisscom/DenemeSignalr.git
Hocam yukarıdaki linkte benim kullanmam gereken sayfası aynen kodladım database bak dosyasınıda ekledim birde ufak açıklama dosyası koydum sizin makalenizi nasıl çalıştırmak istediğile ilgil şimdiden Çok teşekkür ederim değerli vaktinizden bana zaman ayırdığınız için .
Saygılarımla.
İyi çalışmalar..
Teşekkürler Mahmut;
Gerçekten öğrenmişin. Çok mutlu oldum.. Birinİ daha SignalR’cı yaptık:)
Asıl ben teşekkür ederim Hocam sizin sayenizde, inşallah iyi bir SignalR’cı olurum : ), ellerinize Yüreğinize Ağzınıza sağlık öyle güzel anlatıyorsunuz ki öğrenmemek ele değil ve bana kattığınız bu değer için tekrar çok teşekkür ederim . Hocam proje çalışıyormu bende yeni kayıt eklemede Güncelleme Gerçekleşmiyodu ama yorumunuzdan anladığım kadarıyla çalışıyor galiba. bana ayırdığınız vakit ve emek için minnettarım . Saygılarımla
Hocam kendi projemdeki tüm hataları düzelttim Şuan sorunsuz Çalışıyor Bilgi vermek istedim. Saygılarımal
Hocam Merhaba, bu makale içiçn size nekadar teşekkür etsem azdır.Çok faydalı oldu ve bana çok iyi bi iş çıkartırdı. Size uFak bir Sorum olacak. Projemi kendi serverımız üzerinde yayınlayacağım webapi proje,birde html helper yazmıştım classlibrary olarak bu üçü aynı proje içinde , bu üç ünü tek bir seferde publish edebiliyormuyum yoksa web apiyi projeyi ayrı ayrı mı publish etmem ve çalıştırmam gerekiyor. Saygılarımla
Selamlar Mahmut;
Öncelikle bu 2 makale benim favorimdir:) Üçünü birden publish edebilirsin.
Kaynak MSDN: https://msdn.microsoft.com/library/ms404233(v=vs.100).aspx
Merhabalar hocam, burdaki örneği kendi projemde kullandım database üzerinde direk ekleme yapınca sorunsuz olarak çalışıyor fakat bir uygulama dışarıdan kayıt attığında “belirtilen atama geçerli değil” diye hata veriyor triggeri iptal ettiğimde direk store producure çağırdığımda yine web servis tetikliniyor triggerin çaışmasını nasıl sağlayabilirim bu konuda yardımcı olurmusunuz.
Selamlar Ferdi,
Öncelikle en sevdiğim makalemi bulmuşun. Tebrikler:) Senin sorunun WebApi de güvenlik. 2 yolun var. Kolay yol: Sql ile WebApi servisini aynı sunucuya koy. Ya da Webapi servisini Cross Domain’e aç. Onu da makalelerimden bulabilirsin.
Türkçe olmasına ragmen bu makalem yurt dışından en çok ragbet gören, okunan ve soru sorulan makalemdir:)
İyi çalışmalar.
Merhaba Bora hocam yapmış olduğunuz makaleyi aynen aldım çalıştırdım. Gayet sağlıklı çalışıyor. Fakat ben baştan kendime göre uyarladığımda değişen kaydın bir önceki halini getiriyor. yani seyfullah yazısına seyfullah-1 yazıyorum “seyfullah” geliyor “seyfullah-2” yazdığımda “seyfullah-1” geliyor. kafam karıştı. Bunun sebebi nedir sizce
Selamlar,
Büyük ihtimalle kaydın değişmemiş halini gönderiyorsunuz. Kayıdın değiştiğine emim olduktan sonra “signalR :Hub” methodunun tetiklenmesi gerekmektedir.
İyi çalışmalar.
Merhaba Bora hocam
public void RefreshData(int Id)
{
Thread.Sleep(1000); komutu ekleyince çalışıyor. Sizce durumun kaynağın ne olabilir. Komutlarınıza ek olarak sadece eklenen komut thread komutu.
Selamlar,
Çekilmek istenen datanın sonucu belli bir zaman aldığı için bu şekilde bekleniyordur. Bundan dolayı çalışıyordur. Ama bu malesef doğru bir çözüm değil.
Bu methodu asenkron çağırsan bu iş çözülür :)
“var changeData = from prod in db.tblGames where prod.ID == ID select new { prod.Name, prod.Price, prod.ID, prod.ImageUrl };”
İyi çalışmalar.
Merhaba Bora hocam,
“var changeData = from prod in db.tblGames where prod.ID == ID select new { prod.Name, prod.Price, prod.ID, prod.ImageUrl };” bu şekilde çağırıyorum. Fakat değişimden önceki kaydı gösteriyor. Ne yapacağımı çıkartamadım. yoğunluktan cevabınıza ancak cevap yazabilidim.
Merhaba Bora hocam,
“var changeData = from prod in db.tblGames where prod.ID == ID select new { prod.Name, prod.Price, prod.ID, prod.ImageUrl };” bu şekilde çağırıyorum. Fakat değişimden önceki kaydı gösteriyor. Ne yapacağımı çıkartamadım. yoğunluktan cevabınıza ancak cevap yazabilidim.
Hocam,kolay gelsin bilgiler için çok teşekkürler. Angularjsi ben de neredeyse aynı amaç için kullanacağım ama hiçbir bilgim yok kendisi hakkında. Kaynak olarak nereden çalışmamı önerirsiniz ? Kendi sitesinden mi, online kurslardan mı, kitaplardan mı ? İlk adım çok zor geliyor, internet sitelerinde kayboluyorum boş boş :( teşekkür ederim
Selamlar,
Öncelikle ben teşekkür ederim.
AngularJs’mi Angular7 mi once ona karar vermek lazım:) Tavsiyem Angular7 tabiki. Öğrenmek için bloğumdan başlayıp, PluralSight’ı öneririm. Msdn subscription alınca ilk 3 ay sanırım ücretsiz oluyor.
egghead.io da Angular için çok iyi bir eğitim portalı. Ama maalesef o da ücretli bir eğitim.
İyi çalışmalar.
Çok teşekkürler hocam, Allah her işinizi rast getirsin :) yol gösteren birinin olması çok güzel bir şey
Merhabalar hocam, bilgiler için çok teşekkürler.
Ben de yeni bir kayıt eklendiğinde veri tabanına eklenen kayıtı il seferde yüklenen verilerin üzerine tek tek yazdırmak istiyorum. Refreshdata fonksiyonundaki IDyi direk sayfaya yazdırdığımda sıkıntı yok. Ama bu ID ile örnekteki gibi veritabanı işlemi yapınca değişen veri çok nadir geliyor , çoğunlukla boş geliyor . Yardım ederseniz çok sevinirim teşekkürler hocam.
public async System.Threading.Tasks.Task RefreshData(int ID)
{
var changeData = await GetData(ID);
Clients.All.refreshData(changeData);
}
public async Task GetData(int ID)
{
var data= await Task.FromResult( db.Canli.Where(t => t.Id == ID).ToString());
return data;
}
Client kısmı :
messagehub.client.refreshData = function (changedData) {
console.log(“Değişen:” + changedData);
$scope.deneme = changedData;
$scope.$apply();
};