WCF Client Kaynaklarının Elden Çıkarılması

16. Şubat 2020

Blog yazılarıma verdiğim uzun bir aradan sonra ısınma amaçlı olarak, geçtiğimiz hafta uğraştığım bir sorun ve çözümü ile yazılarıma tekrar dönmek istiyorum. Yaklaşık 6 senedir kullandığımız bir WCF uygulamamızı windows service olarak host ediyoruz. Uygulama yıllardır gayet performanslı ve sorunsuz çalışrıken son 6 aydır gün aşırı  farklı sebeplerden sorun oluşturmaya başladı. Bir süredir bakımını yaptığımız uygulamada son karşılaştığımız sorunlardan biri servise gelen taleplerin yoğun olduğu anlarda "Thread was being aborted" hatası ile taleplerin kesilmesi ve işlemlerin yarım kalması idi. İlk şüphelendiğimiz nokta, servise aynı sunucudan iki farklı talep oluştuğunda yeni açılan bağlantının önceki aktif bağlantıyı kopartıyor oluşu idi. Konu bir yandan tutarsız gözüküyor, bir yandan da sorunun sebebinin bu olduğunun tam bir kanıtını bulamıyorduk.

Stackoverflow'da yaptığımız araştırmalar sorunucunda sorunun genel bir programlama hatasından kaynaklandığını, istemci uygulamada oluşturulan istemci proxy nesnelerinin Close metodu ile kapatılmadan elden çıkarılmaması, yoğun anlarda uygulamanın "Thread was being aborted" hatası atmasına sebep olduğunu gördük. Gerçekten de servis çağrılarında sürekli yeni bir istemci nesne yaratıp, istek sonucu dönünce istemciki kapatmadan işlemi sonlandırıyordu uygulamamız. Doğru kullanım ise aşağıdaki gibi kullanılan kaynakları sağlıklı bir şekilde serbest bırakması : 

 

var client = new WCFServiceProxy();
try
{
    client.DoSomeWork();
    client.Close();
}
catch (Exception e)
{
    // log...
    client.Abort();
    throw;
}

 

StackOverflow'da karşılaştığım sorun ve çözüm : “Thread was being aborted” in WCF Service Call

Bulduğumuz çözüm sorunu çözecek fakat ilerde bu yanlış kullanımın önüne geçmeyi sağlamayacaktı. Yazılımcı servis kullanımı sonrası yarattığı istemciyi kapatmayı unuttuğu takdirde ileriki zamanlarda tekrar sorun ortaya çıkacak. Bundan dolayı istemci proxy'nin yaratılmasını sağlayan bir yardımcı sınıfımız olduğu gibi, bu yaratılan istemcinin olması gerektiği gibi kapatılmasından da sorumlu olmalı idi. Yani tek bir noktada istemci sınıfını yaratıp, hayat döngüsünü yöneterek, kullanan yazılımcıdan encapsule etmeliydik.

WCF istemci proxy sınıfını using ile kullanıp gerektiğinde elden çıkarılmasının sağlanması da bir opsiyon fakat bu çözümde yine yazılımcının unutması halinde soruna sebep olabilecek bir durum. Ayrıca Marc Gravell'in şu makalesinde bahsettiği üzere framework'te bulunan bazı sorunlardan dolayı ClientBase sınıfının her zaman düzgün dispose edilemiyor. Marc Gravell makalesinde extension metodlar ile istediğim tarz çözüme bir örnek vermiş. Ancak ben "Indisposable: WCF Gotcha #1" makalesindeki gibi bir çözüm ile proxy istemcisinin yaratılmasını ve sonlandırılmasını sağlayacak komple bir çözümü tercih etmeyi daha uygun görüyorum.

İstemciyi yaratıp kullanan bir örnek :

 

Service<IWCFServiceProxy>.Use(client=>
{
  client.DoSomeWork();
}

 

Use extension metodu :

 

public delegate void UseServiceDelegate<T>(T proxy);

public static class Service<T>
{
    public static ChannelFactory<T> _channelFactory = new ChannelFactory<T>(""); 

    public static void Use(UseServiceDelegate<T> codeBlock)
    {
        IClientChannel proxy = (IClientChannel)_channelFactory.CreateChannel();
        bool success = false;
        try
        {
            codeBlock((T)proxy);
            proxy.Close();
            success = true;
        }
        finally
        {
            if (!success)
            {
                proxy.Abort();
            }
        }
    }
}

 

Yukarıdaki metodda görüldüğü gibi UseServiceDelegate delegate'i ile yazılımcının servis çağrısı, Use extension metodu içerisinde oluşturulan istemci sınıfı kullanarak çalıştırılıp, delegate tamamlanınca istemci olması gerektiği gibi kapatılıyor. Bizim kendi örneğimizde olduğu gibi WCF istemcisine geçilen ek konfigurasyon değerleri var ise, ki bu konfigurasyonları uygulamanın config dosyası yerine kodda geçmeyi uygun gördük, metod içersinde tanımlanabilir. Böylece istemci sınıfın yaratılması ve kapatılması ile ilgili tüm detayları kullanıcı yazılımcıdan saklamış ve kod çoklanmasının da önüne geçmiş oluyoruz.

asp.net , ,

ASP.NET 5 / ASP.NET MVC 6 View Components Yeniliği - Görsel Bileşenler

10. Kasım 2015

 Frameworklerin yeni versiyonlarında sıklıkla mevcut bileşenler tekrar kullanılabilirlik açısından ele alınır ve iyileştirmeler yapılır. ASP.NET 5 ve ASP.NET MVC 6.0 ile partial view ve child action özellikleri ele alınıp view components ile daha tekrar kullanılabilir hale getirilmiş. Tekrar kullanılabilir bir taslak yaratmak için controller'a bağımlılık ortadan kaldırılmış ve asenkron olarak kullanabilme sağlanmış. Bu yazımda öncelikle child action ile tekrar kullanılabilir bir görsel taslak hazırlamaya çalışıp, sonra bu örneği view components ile hazırlayıp kullanmaya çalışacağız.

 

ASP.NET MVC 2 Child Actions ile tekrar kullanılabilir bileşenler

Child Actions konusuna hakim okuyucular bu bölümü atlayıp bir sonraki bölüme devam edebilir. ASP.NET MVC 2 ile gelen Child Actions sayesinde controllerlarımıza tanımladığımız aksiyonları ChildActionOnly attribute'u ile işaretleyip, istediğimiz görsel taslak içerisinde çağırabiliyoruz. Böylece DRY prensibine uygun olarak aksiyonu ve görselini uygulamamız içerisinde defalarca kullanabiliyoruz. Aşağıda child action olarak tanımladığımız bir aksiyonu ve görsel taslağını inceleyebiliriz : 

public class HomeController : Controller
{
	public ActionResult Contact()
	{
		return View();
	}

	[ChildActionOnly]
	public ActionResult Movies()
	{
		List<Movie> movieList = new List<Movie>();
		movieList.Add(new Movie() { ImdbId = "tt0470752", Name = "Ex Machina" });
		movieList.Add(new Movie() { ImdbId = "tt2562232", Name = "Birdman" });
		movieList.Add(new Movie() { ImdbId = "tt0137523", Name = "Fight Club" });
		return View(movieList);
	}
}

Görsel taslak dosyası : Movies.cshtml 

@model List<ViewComponentsTrial.Models.Movie>

<div>Movies : </div>
<ul>
@foreach(var item in Model) {
    <li><a href="http://www.imdb.com/title/@item.ImdbId">@item.Name</a></li>
}
</ul>

Yukarıda hazırlamış olduğumuz tekrar kullanılabilir child action örneğini uygulamamız içerisinde aşağıdaki gibi kullanabiliyoruz : 

@Html.Action("Movies")

View Component'in Hazırlanması ve Kullanılması

Yukarıdaki örnekte child action yardımı ile hazırladığımız film listesini, uygulamamız genelinde kullanılabilecek bir View Component olarak hazırlamak istersek aşağıdaki gibi ViewComponent sınıfından türeyen bir sınıf hazırlamamız gerekiyor.

public class MoviesViewComponent : ViewComponent
{
	public IViewComponentResult Invoke(string imdbId)
	{
		List<Movie> movies = GetMovies();
		if (string.IsNullOrEmpty(imdbId))
		{
			return View(GetMovies());
		}
		else
		{
			return View(GetMovies().Where(m => m.ImdbId == imdbId));
		}
	}

	public List<Movie> GetMovies()
	{
		List<Movie> movieList = new List<Movie>();
		movieList.Add(new Movie() { ImdbId = "tt0470752", Name = "Ex Machina" });
		movieList.Add(new Movie() { ImdbId = "tt2562232", Name = "Birdman" });
		movieList.Add(new Movie() { ImdbId = "tt0137523", Name = "Fight Club" });
		return movieList;
	}
}

 

View Component olarak hazırladığımız sınıfların isminin ViewComponent şeklinde bitmesi gerekiyor. View component'imiz Invoke metodu ile görsel taslakta kullanılacak modeli dönüyor. Hazırladığımız view component'in görsel taslağını aşağıdaki gibi hazırlıyoruz. Burada dikkat edilmesi gereken nokta, görsel taslağın konumlandığı dizin. Görsel taslağımız ~\Views\Shared\Components\Movies dizininde bulunuyor. Yani kullanılmak istenen controller'ın sahip olduğu görsel taslak dizininin altında Components dizini oluşturup bunun altına view componentimiz için bir dizin oluşturmamız gerekiyor. Bu dizinde görsel taslağımızı Default.isml ismi ile kaydediyoruz. Ben örnek olarak kullandığım view component'i tüm görsel taslaklarda kullanabilmek için shared dizini altına konumlandırdım. Dilersek view component diğer dizinler altına konumlanıp sadece konumlandığı dizin altında kullanılması da sağlanabilir. Görsel taslağımıza göz atarsak :

@model IEnumerable<ASP_NET_5_TagHelpers.Models.Movie>
<div>Movies : </div>
<ul>
    @foreach (var item in Model)
    {
        <li><a href="http://www.imdb.com/title/@item.ImdbId">@item.Name</a></li>
    }
</ul>

Şimdi hazırladığımız view component'i kullanmak için yapmamız gereken tek şey sayfalarımızda aşağıdaki gibi çağırmak :

@Component.Invoke("Movies", "")
// Asenkron kullanım : 
@await Component.InvokeAsync("Movies", "")
// Parametreli kullanım : 
@await Component.InvokeAsync("Movies", "tt2562232")

Sonuç

View components, ASP.NET içerisindeki görsel taslakları yeniden kullanılabilir yapmak için getirilmiş ve önceki tekniklere göre çok daha etkili bir teknik. Sadece controller bağlantısının koparılması bile tekrar kullanılabilirlik açısından tercih sebebi olmasını sağlayacaktır. Örneğimde yer alan kod bloklarına ve dosyalara aşağıdaki github projesi ile ulaşabilirsiniz : 

View Components Örnekleri

 

asp.net , , , ,

ASP.NET 5 Tag Helpers - Etiket Yardımcıları

28. Ekim 2015

ASP.NET MVC ile Razor View Engine ilk çıktığında HTML helpers ile tanıştık ve MVC projelerinde yoğun olarak HTML Helper metodlarını kullandık, kullanımı kolaylaştırmak için kendi yardımcı metodlarımızı yazdık. ASP.NET MVC ile alakalı pek çok soru ve takıldığımız pek çok nokta HTML Helper kullanımı ile alakalı oldu. HTML'nin basitliğinden ve yıllardır tecrübe ettiğimiz etiketler ile attributelerden uzaklaştık. Yerine HTML Helper metodlarına geçilen parametrelerle boğuşmaya başladık. Şahsen hala ASP.NET MVC'de HTML Helper ile oluşturacağım bir linke class attribute'u verirken zorlanmıyor değilim.

İşte bu noktada Tag Helpers - etiket yardımcıları ile biz geliştiricilerin yardımına biraz geçte olsa yetişilmiş oldu. Tag Helpers temelde bize kullanım kolaylığından başka yeni bir şey sunmuyor. Razor HTML yardımcıları ile yazabildiğimiz kodu, tag helpers ile daha markup diline yakın olarak yazabiliyoruz, View'larımız daha okunabilir oluyor. Dolayısıyla anlaması ve geliştirmesi daha kolay hale geliyor. ASP.NET 5 ile gelen etiket yardımcılarına ek olarak istersek kendi etiket yardımcılarımızı geliştirebiliyoruz, ki bu kısım bence konunun en heyecan veren yeri.

Bu yazıda öncelikle ASP.NET 5 ile gelen varsayılan etiket yardımcılarına değinip, sonrasında kendi etiket yardımcımızı nasıl yazacağımız ve kullanacağımız üzerinde durmaya çalışacağım.

 

ASP.NET 5 Varsayılan Etiket Yardımcıları

Aşağıdaki kod bloğunda etiket yardımcıları olmadan HTML yardımcıları yardımı ile basit bir view örneği görüyoruz. Buradaki HTML yardımcıları ile oluşturduğumuz üç elemana dikkat edelim. Bir textbox, bir checkbox ve bir link elemanı oluşturuyoruz.

 

@model ASP_NET_5_TagHelpers.Models.ToDoListItem

<label>Açıklama : </label>
@Html.TextBoxFor(m => m.Text)
<br />

<label>Tamamlandı mı ? : </label>
@Html.CheckBoxFor(m => m.IsCompleted, new { id = "todolistitem" + Model.ItemId })
<br />

@Html.ActionLink("Tag Helper Kullanılmış Sayfaya Git", "WithTagHelper", "Home")

 

 Şimdi aynı örneği ASP.NET 5 ile gelen varsayılan etiket yardımcıları ile yapalım. ASP.NET 5 ile gelen varsayılan etiket yardımcılarını kullanmak için yapmamız gereken tek şey @addTagHelper emri ile etiket yardımcılarının bulunduğu assembly'i referans göstermek. Böylece aşina olduğumuz HTML etiketlerine etiket yardımcıları ile gelen ek attibute'ları kullanarak view'ımızı hazırlayabiliyoruz. 

 

@model ASP_NET_5_TagHelpers.Models.ToDoListItem
@addTagHelper "*, Microsoft.AspNet.Mvc.TagHelpers"
    
<label>Açıklama : </label>
<input type="text" asp-for="Text" /><br>

<label>Tamamlandı mı ? : </label>
<input type="checkbox" asp-for="IsCompleted" id="@String.Format("todolistitem{0}", Model.ItemId)" /><br />

<a asp-controller="Home" asp-action="WithoutTagHelper">Tag Helper Kullanılmamış Sayfaya Git</a>

 Örnekte bulunan input elemanlarına tanımladığımız asp-for attributelarına dikkat edelim. Bunlar elemanın, modelin hangi özelliği ile eşleşeceğini belirtiyor. Link elemanına gelen asp-controller ve asp-action attributelarına dikkat edelim, bunlar da link elemanının hangi controller'ın hangi aksiyonu çalışacağını belirtiyoruz. İşler oldukça basitleşmiş ve okunabilir hale gelmiş değil mi ? 

Kendi Etiket Yardımcımızı Geliştirelim

 ASP.NET 5 ile gelen varsayılan etiket yardımcılarının yanı sıra kendi etiket yardımcımızı yazabilmemizi sağlayan sınıflar da mevcut. Microsoft.AspNet.Razor.Runtime.TagHelpers isim alanı altındaki TagHelper sınıfından türeteceğimiz sınıflar vasıtasıyla kendi etiket yardımcımızı geliştirebiliyoruz. Sınıfın public özellikleri view'da kullanacağımız elemanın attibutelarına karşılık geliyor. Process metodu içerisinde ise sayfamız istemciye gönderilirken nasıl render edilmesini istiyorsak öyle HTML elemanları tanımlayarak çıktımızı üretebiliriz.

Örneğin aşağıdaki Bootstrap input group örneğini ele alalım. 

<div class="input-group">
    <input type="text" class="form-control" placeholder="Recipient's username" aria-describedby="basic-addon2">
    <span class="input-group-addon" id="basic-addon2">@@example.com</span>
</div>

 Bu kod bloğunu, tekrar kullanılabilir bir etiket yardımcısına aşağıdaki gibi çevirebiliriz. InputGroupTagHelper sınıfının Id ve Domain özelliklerine dikkat edin. Bunları daha sonra view dosyamızda attribute olarak kullanacağız.

public class InputGroupTagHelper : TagHelper
{
	public string Id { set; get; }

	public string Domain { set; get; }

	public override void Process(TagHelperContext context, TagHelperOutput output)
	{
		// Outer Tag : 
		output.TagName = "div";
		output.Attributes.Add("class", "input-group");

		// Inner Input : 
		var name = new TagBuilder("input");
		name.MergeAttribute("type", "text");
		name.MergeAttribute("class", "form-control");
		name.MergeAttribute("placeholder", "Recipient's username");
		name.MergeAttribute("aria-describedby", this.Id);

		// Span : 
		var span = new TagBuilder("span");
		span.MergeAttribute("class", "input-group-addon");
		span.MergeAttribute("id", this.Id);
		span.InnerHtml = this.Domain;
		
		output.Content.SetContent(name.ToString() + span.ToString());
	}
}

Tanımladığımız etiket yardımcısını view dosyasında aşağıdaki gibi kullanabiliriz : 

@addTagHelper "*, ASP_NET_5_TagHelpers"

<input-group id="basic-addon2" domain="@@gmail.com"></input-group>
 

 

 

Sonuç

Bu yazımda ASP.NET 5 ile gelen tag helpers - etiket yardımcılarını incelemeye çalıştık. Şuan ASP.NET 5 Beta versiyonu olduğu için kod örneklerinde eklemeler ve çıkarmalar olabilir. Hatta bazı HTML elemanlarında varsayılan etiket yardımcılarının istenen şekilde çalışmadığını gördüm. Final sürümünde daha stabil olacaktır. Yazımda bahsettiğim örneklere aşağıdaki github linkinden ulaşabilirsiniz.

ASP.NET 5 TagHelpers Örnekleri

 

asp.net , , ,