23 Şubat 2020 Pazar

SignalR Kullanımı (.Net Core - Angular)

     S.A. Arkadaşlar,
     Bugünkü yazımızda karşılaştığımız bir problemi ele alacağız. Bunun sonucunda bu problemin geçici çözümünü ve daha sonra nasıl daha doğru bir çözüme geçtiğimizi anlatacağım. Başlıktan da anlaşılacağı üzere konu web socket ile ilgilidir, biz de bu alt yapıyı kullanan SingalR kütüphanesini ele aldık. Buraya geçmeden önce başta uyguladığımız çözüm istemci tarafından sürekli olarak istek göndermekti, fakat bizim için geçici bir çözümdü, bunun yerine belirli bir sürede sürekli istemek atmak yerine, her hangi bir değişiklik olduğunda bunu tüm istemcilere göndermek olmalıydı ve biz bunu gerçekleştirdik.
      Girişte SignalR mantığı ile ufak bir giriş yaptık, gerçek zamanlı veri ile ilgili işlemler yapmak istiyorsanız doğru yerdesiniz. Bunun örneklerinden biri mesajlaşma uygulamalarıdır. Şöyle ki, telefona mesaj gelip gelmediğini öğrenmek için her defasında uygulamayı açmak zorunda olduğunuzu düşün. Kulağa ne kadar da garip geliyor, fakat web socket ile buna gerek kalmıyor. Mesaj bize geldiğinde bildirim geliyor ve biz o mesajı okuyoruz. Aslında genel mantık da böyledir, istemci her an istek atmak yerine sunucunun kendisine bilgi vermesini bekliyor. Gelen istekle sunucu istemciyi dürter ve bu şekilde istemci artık bu olaydan haberdardır.

     Sıfırdan .Net Core ve Angular proje kurulumu yapmayacağım. Hazır olan bir projeye nasıl entegre edileceğini anlatmaya çalışacağım.

      Projem .Net Core 2.1 (LTS) (bu yazıyı yazarken 3.1 (LTS) geçirdim, bir kaç ufak değişikliği de ona göre belirteceğim.) ve Angular 8 kullanmaktayım. İlk olarak Startup.cs içine gidelim ve kullanacağımız yapıları buraya tanıtalım.
public void ConfigureServices(IServiceCollection services)
{
    ...
    services.AddSignalR();
    ...
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    app.UseSignalR(routes =>
    {
        routes.MapHub<LogHub>("/loghub");
     });
    //üstteki kullanım 3.1 ile birlikte deprected(ileride kaldırılabilir) gözükmektedir.
    //bu vesileyle aşağıdaki çözüm önerilmektedir.
     app.UseSignalR(routes =>
     {
        routes.MapHub<LogHub>("/loghub");
     });
}

      Şimdi de Hub'dan türeyen LogHub sınıfınızı oluşturalım.
public class LogHub : Hub
{
}

     Gerekli ayarlamaları yaptıktan sonra şimdi de controller tarafında kullanmaya geçelim.
private readonly IHubContext<LogHub> _hub;
public HelloController(IHubContext<LogHub> hub)
{
    hub = _hub;
}
[HttpPost()]
public async Task<IActionResult> Post(Customer item)
{
    base.Post(item);//Farklı şekillerde kaydetme işlemleri yapılabilir.
    int result = await yourContext.SaveChangesAsync();//EF Core kullanıyorum.
    if (result > 0) //İşlem başarılı ise istemciye haber gönderiyorum.
    {
        await _hub.Clients.All.SendAsync("postCustomer", item);
        return Ok("İşlem Başarıyla Kaydedildi.");
    }
}

     Yukarıdaki kod içerisinde yorum satırlarına küçük açıklamalar ekledim, fakat bütünlük açısından üzerinden geçmekte fayda görüyorum. Depency Injection ile hub'ı içeri aldık. Daha sonra bir kaydetme işlemi yapıyoruz. Bu kaydetme işlemi, silme veya güncelleme de olabilir. Daha sonra bu işlem başarılı ise obje ile birlikte istemciye gönderilir. İstemcinin bunu nasıl kullanacağı ise yapılan işe göre değişir. Örneğin kullanıcı liste sayfasını yenilemeden kayıtları izlemek istiyor. Yeni kayıt gelince bunu isteğe göre aşağıya veya yukarı gönderebiliriz. Tüm listeyi tekrardan da çağırabiliriz, fakat bunun iyi bir pratik olmayacağını düşünüyorum. Tüm listeyi çekmek daha maliyetli olacağından konuyu bu şekilde çözmek daha mantıklı olacaktır.

    Back-end tarafta bunları yaptıktan sonra şimdi front-end tarafa geçelim. Öncelikle sizlere ilk çözüm yöntemimizi göstermek istiyorum. Aslında bu hızlı yol almamız için gereken geçici bir yöntemdi.
interval: any;
this.interval =  setInterval(() => {
    // yapılması gereken işleri buraya yazabiliriz.
  },3000
);
ngOnDestroy() {
      clearInterval(this.interval);
}

   Buraya belirtilen saniyelerde back-end tarafa istekte bulunuyordu. Verinin gelip gelmemesine bakılmaksızın bir istek vardı. Bu da gereksiz bir maliyete sebebiyet veriyordu. İstemci sayısı arttıkça maliyet de artıyor olacaktır. Bunun yerine sadece istenilen değişimler olduğunda istemciyi uyaracak bir sistem kaçınılmaz oldu.

    Şimdi artık yeni sistemimi tasarlamaya geçebiliriz. Öncelikle SignalR paketimizi yüklememiz gerekecek "npm install @aspnet/signalr --save". Daha sonra hangi servis veya component'te kullanılacaksa oraya kütüphaneyi import etmemiz gerekecek.
import {HubConnection, HubConnectionBuilder} from '@aspnet/signalr';
private hubConnection: HubConnection;
protected createConnection() {
    this.hubConnection = new HubConnectionBuilder()
      .withUrl('yourUrl' + '/loghub')
      .build();

    this.hubConnection.start().then(() => {
    }).catch((err) => {
      return console.error(err.toString());
    });

    this.hubConnection.on('postCustomer', res => {
      //istenilen işlemleri buraya yapabilirsiniz.
    });
}

     İlk başta bir bağlantı açıyoruz. Bağlantıyı açtığımız yere kullanacağımız adresi veriyoruz. Burada dikkat etmemiz gereken konu "loghub" olacaktır. Back-end tarafta da bunu kullanmıştık. Aynı olmalarına dikkat etmek gerekiyor.

    Daha sonra bağlantı dinlenmeye başlıyor. Her hangi bir hata ile karşılaşırsak bunu konsola yazdırıyoruz.

    Şimdi asıl işi yaptığımız yere geliyoruz. Burada bağlantıyı dinliyoruz. Back-end tarafta her hangi bir müşteri eklediğimizde (postCustomer) burası uyarılacaktır. Yine burada da kullandığımız ifadelerin aynı olmasına dikkat etmeliyiz. Çünkü ona göre hangi back-end, ön tarafta hangi metodu uyaracağını bilmesi gerekmektedir. İlk başta buranın uyarılıp uyarılmadığını kontrol etmek açısından konsola gelen veriyi (res) yazdırabilirsiniz. Daha sonra gelen veriye göre kendi işleminizi yapabilirsiniz. Bizim örnekte müşteri objesini gönderiyoruz (item). O zaman burada olması gereken,  listeye bunu eklememiz olacaktır. Çünkü kullanıcı müşteri listesinde sayfayı yenilemeden gelen veriyi göstermek istiyorduk. Gelen veriyi listeye eklersek istediğimizi elde etmiş olacağız.

     Burada ekleme işlemini başarıyla gerçekleştirdik. Bunun yanında güncelleme ve silme işlemleri de mevcut. Aslına bakarsak mantık çok benzer. Bu yüzden kodları paylaşmadan teorik olarak ikisinden de bahsetmek istiyorum. Kaydetme yerine güncelleme işlemi yaptığımızda, ön taraftaki güncelleme metodumuz tetiklenir. Gelen verinin id bilgisi ile satırı bulup yeni veri ile güncelliyoruz. Bu sayede tüm listeyi tekrar çekmekten de kurtuluyoruz. Silme işlemini de yine benzer bir mantıkla gerçekleştirip ön taraf tetiklendiğinde id bilgisi ile o satırı silip tüm listeyi çekmekten kurtuluyoruz.

     Bu şekilde bir yazının sonuna gelirken  genel çerçevede karşılaştığımız bir problemi ve bunun çözüm aşamalarını aktarmaya çalıştım. Bu konuda emeği geçen ekip arkadaşlarım Aras, Taner ve Seyfi'ye teşekkürlerimi iletmek isterim.

     Kullandığımız yapıları daha iyileriyle geliştirmek dileğiyle.

Hiç yorum yok:

Yorum Gönder