ASP.Net Core, Cookie Expiration and Mysterious Logout on IIS

Working with cookie expirations on Asp.Net Core may be a bit confusing. Although settings are simple, varierity of settings can mislead you.

IsPersistent

First setting I want to mention is IsPersistent. When it is set to true, It will be kept even if browser closed (persistent cookie). As opposite when it is set to false, cookie will be cleared when the browser is closed. (session cookie)

ExpireTimeSpan

This settings helps us to set expiration date of the ticket stored in cookie. For example, the setting below will make the ticket in the cookie invalid after 15 mins.

options.ExpireTimeSpan = TimeSpan.FromMinutes(15);

SlidingExpiration

Sometimes we want to extend expire timespan if client uses the application actively. With this setting, It will be reset expiration time if client make a request after more than half of the expiretimespan interval passed.

If a browser send a request with a cookie that has expired ticket, server will ignore it.

ExpiresUtc

ExpiresUtc setting helps you to set an absolute expiration to cookie. This settings overrides ExpireTimeSpan setting value. This setting will also ignore SlidingExpirationSetting.

ExpiresUtc = DateTime.UtcNow.AddMinutes(20)

Another important point is that when you set ExpiresUtc, If you don’t set IsPersistent setting, cookie will be created as session cookie.

Mysterious Logout on IIS Server

Despite all of these settings you may experience that users are loging out after some minutes eg 20mins. Most probably you will not catch this behaviour on local while developing your application. It is related with some settings on IIS.

To fix this behaviour, first you need to go to advanced settings of application pool.  You will see a setting called “Idle Time-out (minutes)” and must set as 0. Its default value is 20mins. It means that if no new request comes for 20 mins, worker process will be shut down.

When an app restarted or worker process restarted and If the keys related with authentication kept in memory;

Cookie based authentication tokens will be invalid and users will need to log in again.

So to keep keys persistent, we need to set one more setting on advanced settings of Application Pool; Load User Profile must be set to True. So keys will be stored in a folder on operation system. (%LOCALAPPDATA%/ASP.NET/DataProtection-Keys)

Sources: https://docs.microsoft.com/en-au/aspnet/core/host-and-deploy/iis/?view=aspnetcore-3.1

https://docs.microsoft.com/en-us/aspnet/core/security/authentication/cookie?view=aspnetcore-3.0#absolute-cookie-expiration

 

Sharing sessions between subdomains – Asp.Net Mvc Apps on IIS

Sometimes, you need to share sessions between different apps on different sub domains. There are some steps to achieve this behaviour and I will try to explain them today.

If we want apps to behave like single app on different subdomains, firstly they must use same cookies. With shared cookies, users won’t have to logged in for each app. They will be able to logged in / out only once for all apps.  In order to share cookies we need to add a setting under system.web section in webconfig for cookies:

With this setting, any subdomain under yourmaindomain.com use the same cookies. But there is one more step here, all apps have to same machinekey setting on webconfig. Machinekey attribute is used while encrypting and decrypting the data for the webapplication in ASP.NET.

Before move further, Lets create two websites on our web server.

iis create web site

After creating two websites, for one, select MachineKey feature and click Generate Keys. Copy Validation Key and Decryption Key. Then open other app’s Machine Key feature and paste copied values to related fields.

These values will be pasted to a web config attribute too.

This attribute will be under system.web section in webconfig.

These changes helps us to use shared cookies without any problem. But to achieve our goal completely, we need to do one more thing. We have to store session state in database, so all applications will share same session data. By adding setting below to web config and editing to connection string, all session data will be shared.

 

WCF Bindings

Daha önce de karşıma çıkan wcf binding seçimi ile ilgili bir görsel bu. Arşiv olması ve en azından fikir vermesi açısından paylaşmak istedim.

choosebindings

Yabancılarla Konuşma! – Law Of Demeter

strangerdangerNesne yönelimli programlamanın kıyısından köşesinden bir ilişkiniz olduysa eğer encapsulation, abstraction, loosely coupling gibi terimleri duymuşsunuzdur. Bu terimler aslında kolay gibi gözüken ama gerçekleştirmesi, özellikle kodlar ve istekler büyüdükçe, o kadar da kolay olmayan şeylerden bahsederler.

Bugün bunların gerçekleştirilmesinde bize yardımcı olan, kimilerine göre bir pattern kimilerine göre bir pratik olan Law of Demeter’dan (LoD) bahsedeceğim. Özetle LoD başlıkta belirttiğim gibi “Yabancılarla Konuşma!” der.  Bu pratiğe göre bir nesneye ait metod aşağıdaki dört şekilde metod çağırmaldır.

  1. Nesnenin kendisine ait metodlar.
  2. Metoda parametre ile gelen nesnelere ait metodlar.
  3. Metodun içerisinde oluşturulmuş bir nesneye ait metodlar.
  4. Nesnenin direk erişimi olan nesne ve özelliklere ait metodlar.

Bu maddeleri kısaca birer kod parçacığı ile gösterecek olursak.

  1. Nesnenin kendisine ait metodlar.
  2. Metoda parametre ile gelen nesnelere ait metodlar.
  3. Metodun içerisinde oluşturulmuş bir nesneye ait metodlar.
  4. Nesnenin direk erişimi olan nesne ve özelliklere ait metodlar.

Eğer kodlar içerisinde aşağıdaki gibi zincirleme bir metod çağrımı görüyorsanız. Bazı istisnalar olsa da büyük bir ihtimalle LoD’a uymayan bir kod yazmış bulunuyorsunuz.

Daha anlaşılır olması adına şöyle de yazabiliriz.

Tabii LoD’u sadece zincirleme metod çağrımına karşı olan bir pratik olarak düşünmek doğru olmaz. LoD temelde şuna dayanır:

Bir bilgiyi nesnenin dışarısından çekmek yerine nesnenin kendisinden almalısınız.

Yukarıdaki örnek üzerinden düşünecek olursak hesap nesnesi OkuMusteri’den dönen Musteri nesnesi üzerinden dönen IletisimAdresi üzerinden Ilce’ye ulaşıyor. Eğer hesap nesnesinde bir ilçe bilgisi gerekiyor ise dış dünyanın öncelikle müşterinin okunduğunun sonrasında IletisimAdresinin okunduğunun (belki de ileride değişecek, ResmiAdres okunacak) bilgisine ihtiyacı yok. Onlar sadece hesaptaki ilçe bilgisini istiyorlar. Bu durumda aşağıdaki gibi bir kullanım tercih edilebilir.

Yeni OkuIlce metodu kendi içerisinde IletisimAdresini ya da ResmiAdresi seçebilir. Bu değişiklik metod kullanıcılarını etkilemeyecektir.

Tabii, diğer pattern ve pratiklerde olduğu gibi LoD’un kullanımının tercih edileceği ya da vazgeçilmesinin gerekeceği durumlar olacaktır. Bunun kararının verilmesi için biraz acı ve biraz tecrübe gerekecektir. 🙂 Ancak geliştirme sırasında LoD’u göz önünde bulundurmak oldukça fayda sağlayacaktır.

 

 

 

 

Action, Func ve Predicate Delegeleri

Delegeler metodları işaret eden ve işaret ettikleri metodlarla aynı imzaya sahip olan yani aynı tipte parametrelere ve dönüş değerlerine sahip olan yapılardır.  Delegeleri event tabanlı programlamada, asenkron programlamada, thread programlamada vb bir çok yerde kullanırız. Makalenin asıl amacı genel olarak delege yapısı olmadığı için detaylarına girmeyeceğim.

Pekala yazımızın konusu olan Action, Func ve Predicate neyi temsil eder? Başlıktan da anlaşılacağı üzere bunlar da .Net framework içerisinde bulunan, bizler için önceden hazırlanmış olan delegelerdir. Çok fazla kelimelerle uğraşmadan kod üzerinde görelim.

Action delegesinin imzasına baktığınızda aşağıdaki gibi bir kod göreceksiniz.

Burada Action delegemizin T tipinde yani herhangi bir tipte parametre alan sonrasında da void dönüş yapan metodları işaret edebildiğini görüyoruz.

Örneğin normal delegeler ile aşağıdaki şekilde yazdığımız bir kodu

Action delegesi ile aşağıdaki gibi yazabiliriz.

Action delegesi olduğunun farkına varmasak da kodlarımız içinde lambda ile birlikte bu delegeyi oldukça fazla kullanmaktayız. Örneğin aşağıdaki kodda (kodun anlamsız olduğunun farkındayım amaç net şekilde gösterebilmek 🙂 )

ForEach methodunun tanımına gittiğimizde Action tipinde bir parametre aldığını görebiliriz.

Yani aslında biz ForEach içerisine long tipinde bir değer alan ve içerisinde bu değerin karesini alıp MessageBox ile gösteren bir fonksyion yazdık.

Func delegesi Action’a benzer bir yapıdadır ama Actiondan farklı olarak bir dönüş değeri vardır. Func delegesinin tanımlarına baktığımızda iki farklı kullanım görebiliriz. Hiç parametre almadan bir değer dönebilir.

Ya da bir parametre alıp bir değer dönebilir.

Aşağıdaki kod örneğinde butonun click eventinde öncelikle Func delegesi parametre almayıp string dönen bir metodu işaret ederken, sonrasında delegemiz string bir değişken alıp, string bir değişken dönen bir metodu işaret etmektedir.

Func delegesini de farkında olmasak da linq sorgularımızda kullanmaktayız.  Listeler üzerinde çalıştırdığımız Select , Where vb komutlarında tanımlarına baktığımızda Func tipinde parametre aldıklarını görürüz.

Predicate delegesi ise tam olarak Func<T, bool> parametresinin yaptığı işi yapar. Yani herhangi bir tipteki değişkeni alır ve geriye boolean bir değer döner.  Kullanımı da benzer şekildedir.

Kısa bir şekilde Action, Func ve Predicate delegelerini ve kullanımlarını anlatmaya çalıştım. Eğer herhangi bir sorunuz, eleştiriniz, öneriniz varsa yorum yazarsanız sevinirim.

Çevik Kalın 🙂

 

NUnit System.ArgumentException (w/ Entity Framework)

 

Entity Framework ile veritabanı işlemlerinizi yaptığınız projelerinizi NUnit ile test etmeye kalktığınızda aşağıdaki gibi bir hata alacaksınız.

System.ArgumentException: The specified named connection is either not found in the configuration, not intended to be used with the EntityClient provider, or not valid.

Bu hatadan kurtulmak için bağlantı bilgilerinizin yer aldığı App.config dosyasını NUnit projeniz ile aynı yere kopyalayıp ismini NUnitProjenizinIsmi.config şekilde değiştirmeniz yeterli.

 

Observer Design Pattern

Şu aralar daha önceden üzerinden geçtiğim tasarım desenleri ile uğraşıyorum tekrar. Ama bu sefer biraz daha pratik şekilde ilerlemeye çalışıyorum. İlerdikçe de burada hem bana ilerde hatırlamam için not olması hem de ihtiyacı olanların faydalanması için paylaşmaya çalışıyorum.

Son yazımda Strateji tasarım deseninden bahsetmiştim. Bu kez ise Observer tasarım deseninden bahsetmeye çalışacağım. Örneğin bir alışveriş sitesinin kampanyalarından haberdar olmak için maillist’ine kayıt olduğunuzu düşünün, daha sonra bir başkasının da aynı listeye abone olduğunu düşünelim. Bir kampanya olduğunda siteden abone olan herkese bu kampanya gönderilir.  Daha sonradan artık kampanya bilgilerini almak istemezseniz listeden çıkarsınız ve artık yeni kampanya bilgileri diğer abonelere gönderilir.

Observer tasarım deseni de uygulamalarımızdaki bu tarz işlemlerimizi yapmamızı kolaylaştıran bir desendir. Örneğin bir nesnenin durumu değiştiğinde uyarılacak nesneler listesine eklenmiş nesnelerimiz otomatik olarak uyarılır.

Bu desen ile ilgili örneğimi bir önceki strateji deseni örneğim üzerinden yapacağım. Böyle olması biraz daha karışık olmasına sebep oldu ama ezbere kaçmayı önlememi sağladı. Ayrıca iki deseni bir örnek de birleştirmiş oldum. Öncelikle eklememiz gereken yeni arayüz ve sınıfları vereceğim, daha sonra değişiklik yapmamız gereken kısımlardan bahsedeceğim.

İlk olarak biri yayıncı(subject) biri de dinleyici (subscriber,observer) olmak üzere iki interface tanımlayacağım. Daha sonra kullanacağım bütün dinleyici ve yayıncı sınıfları bu sınıflardan türeteceğim.

public interface IYayinci
{
void dinleyiciKaydet(IDinleyici dinleyici);
void dinleyiciCikar(IDinleyici dinleyici);
void dinleyicileriUyar();
}
public interface IDinleyici
{
void guncelle(ArrayList args);
}

Burada IYayinci arayüzümüzde üç adet metodumuz var bunlar uyarılacak nesneleri eklememizi, çıkarmamızı ve nesnenin durumu değiştiğinde listemizdeki nesnelerin uyarılmasını sağlıyor. IDinleyici arayüzündeki metodumuz ise durum değişikliği olduğunda yapılması gereken işlemleri içeren metoddur.

İkinci olarak yayıncı sınıfımızı oluşturuyoruz. Bu sınıfımızda arayüzden aldığı metodları implemente ediyoruz. Ayrıca ben bir adet renkDegisti metodu ekledim ki bu metodu da renk değişimi olduğunda kullanacağız.

public class RenkYayinci : IYayinci
{
ArrayList _dinleyiciler;
ArrayList _args;
public RenkYayinci()
{
_dinleyiciler = new ArrayList();
_args = new ArrayList();
}
public void dinleyiciKaydet(IDinleyici dinleyici)
{
_dinleyiciler.Add(dinleyici);
}
public void dinleyiciCikar(IDinleyici dinleyici)
{
int i = _dinleyiciler.IndexOf(dinleyici);
if (i >= 0)
{
_dinleyiciler.RemoveAt(i);
}
}
public void dinleyicileriUyar()
{
for (int i = 0; i < _dinleyiciler.Count; i++)
{
IDinleyici dinleyici = (IDinleyici)_dinleyiciler[i];
dinleyici.guncelle(_args);
}
}
public void renkDegisti(string renk)
{
_args.Clear();
_args.Add(renk);
dinleyicileriUyar();
}
}

Burada renkDegisti metoduna renk değişkenini almama rağmen bunu arraylist’e atma sebebim IDinleyici arayüzünün sadece renk için özelleşmesini engellemek ve daha sonra da kullanmak istememdir. Daha sonra dinleyicileriUyar() metodunu çağırıyoruz ve bütün ekli nesneleri haberdar ediyoruz.

Artık eski sınıflarımızda değişiklik yapmaya başlayabiliriz. Öncelikle Daire ve Kare Sınıflarımızı aşağıdaki şekle getiriyoruz.

public class Kare : Sekil, IDinleyici
{
private RenkYayinci _renkYayinci;
public Kare(Dictionary<string, int> degerler, RenkYayinci renkYayinci)
{
_cevre = new KareCevre();
_degerler = degerler;
this._renkYayinci = renkYayinci;
this._renkYayinci.dinleyiciKaydet(this);
}
public void guncelle(ArrayList args)
{
Console.WriteLine(“\nKare kenar renkler değişti ” + args[0].ToString());
}
}

 

public class Daire : Sekil, IDinleyici
{
private RenkYayinci _renkYayinci;
public Daire(Dictionary<string, int> degerler, RenkYayinci renkYayinci)
{
_cevre = new DaireCevre();
_degerler = degerler;
this._renkYayinci = renkYayinci;
this._renkYayinci.dinleyiciKaydet(this);
}
public void guncelle(ArrayList args)
{
Console.WriteLine(“\nDaire çember içi renkler değişti” + args[0].ToString());
}
}

Sınıf tanımlamalarımızda koyu ile işaretli yerleri değiştiriyoruz. Burada öncelikle constructor metodumuzda parametre olarak aldığımız yayıncıya nesnemizi ekliyoruz. Daha sonra dinleyici arayüzünden alınan guncelle metodunu implemente ediyoruz.

Artık desenimizi deneyebiliriz. Önceki yazımızda bulunan örnekteki main metodunu aşağıdaki şekilde değiştiriyoruz.

static void Main(string[] args)
{
Dictionary<string, int> dd = new Dictionary<string, int>();
dd.Add(“BirKenar”, 4);
RenkYayinci renkYayinci = new RenkYayinci();
Sekil sk = new Kare(dd, renkYayinci);
renkYayinci.renkDegisti(“mavi”);
Console.WriteLine(sk.cevreHesapla(dd).ToString());
Console.ReadLine();
}

Programımızı derleyip çalıştırdığımızda karşımızda

Kare kenar renkler değişti mavi

16

çıktısını görebiliriz.

Strategy Design Pattern

Strateji tasarım deseni geliştirdiğimiz uygulama içinde algoritmaları sınıflandırmamızı ve çalışma anında kullanacağımız algoritmayı seçmemizi sağlar. Bu algoritmalar kendi içinde kapsüllenerek (encapsulate) programın geri kalanından soyutlanır ve uygulamamızın loosely coupled bir yapıda olmasına yardım eder. Örneğin bir maliyet hesabında LIFO mu yoksa FIFO mu kullanacağımızı çalışma anında belirlemek istiyorsak strateji tasarım desenini kullanarak bunu nesne yönelimli programlama ilkeleri doğrultusunda yapabiliriz.

Algoritmaların seçim işlemini if else blogları ile yapabilirdik. Ancak küçük bir değişiklikte uygulamamızın içine müdahale etmek zorunda kalırdık. Ayrıca yeni algoritmalar ekledikçe bu if else bloglarımız gittikçe uzayacaktı. Ya da işlemi kalıtım yolu ile de gerçekleştirebilirdik. Bir metodu üst bir sınıfta tanımlayıp, alt sınıflarda bu metodu override edebilirdik. Ancak bu durumda da bir değişiklik durumunda bütün alt sınıflarda kodu tekrar düzenlememiz gerekecekti. Ya da eklediğimiz her yeni sınıf için yeniden metodu override etmemiz gerekecekti. Bu durumda kodların yeniden kullanılabilirliği konusunda başarısız olacaktık.

Bu deseni nasıl uygulayabileceğimizi bir örnekle daha iyi gösterebiliriz. Ben nesne yönelimli programlama ile ilgili örnekler verirken daha anlaşılır ve daha uygulanabilir olması nedeniyle mümkün olduğunca şekiller üzerinden gitmeyi tercih ediyorum. Yine vereceğim bu örnekte Strateji tasarım desenini kullanarak Şekil sınıfından türeyen daire ve kare sınıflarının çevrelerini hesaplayan basit bir uygulama yazacağım.

Öncelikle çevre hesaplama algoritmalarını bir araya toplamam gerekiyor. Kapsülleme işlemini gerçekleştirmek ve çevre hesaplama algoritmalarını belli bir formatta tutmak için öncelikle bir interface yazıyorum.

public interface ICevre
{
int hesapla(Dictionary<string, int> degerler);
}

Bundan sonra çevre hesaplama algoritmalarımı tutacak bütün sınıfları bu interface’den türeteceğim. Bu interface’imiz bir adet hesapla metodu içeriyor. Interface’imizden sonra ise çevre hesaplayacak sınıflarımızı oluşturacağız.

public class DaireCevre : ICevre
{
private int cevresi = 0;
public int hesapla(Dictionary<string, int> degerler)
{
cevresi = 2*degerler[“Pi”]*degerler[“r”];
return cevresi;
}
}

DaireCevre sınıfımız ICevre interface’inden aldığı hesapla metodunu implemente ediyor. Aynı şekilde KareCevre sınıfını da yazıyoruz. Bu sınıf da kendine özgü algoritmasıyla çevre hesaplamasını yapıyor.

public class KareCevre : ICevre
{
private int cevresi = 0;
public int hesapla(Dictionary<string, int> degerler)
{
cevresi = degerler[“BirKenar”]*4;
return cevresi;
}
}

Artık çevre ile ilgili hesaplamalarımızı yaptığımıza göre kullanacağımız sınıfları yazmaya geçebiliriz. İlk olarak şekillerimi türeteceğim bir Sekil sınıfı yazıyorum.

public class Sekil
{
public ICevre _cevre;
public Dictionary<string, int> _degerler;

public int cevreHesapla(Dictionary<string, int> degerler)
{
return _cevre.hesapla(degerler);
}
}

Bu sınıfımızda bütün algoritma sınıflarımızı kapsayabilmesi için bir adet ICevre interface’i nesnesi oluşturuyoruz. Böylece loosely coupled özelliğini de sağlamış oluyoruz. Yeni ekleyeceğimiz, çıkaracağımız ya da değiştireceğimiz algoritmalar uygulamanın geri kalanını etkilemiyor. Ayrıca parametreler için bir adet Dictionary nesnesi tanımladım. Burada gördüğümüz cevreHesapla metodu bizim dışarıdan erişeceğimiz ve hesaplama işlemlerini yaptıracağımız metod. Kendi içinde hangi şeklinde çevresi hesaplanacak ise ona ait metodu çağırıyor.

Artık kullanacağımız sınıfları yazabiliriz.

public class Kare : Sekil
{
public Kare(Dictionary<string, int> degerler)
{
_cevre = new KareCevre();
_degerler = degerler;
}
}

public class Daire : Sekil
{
public Daire(Dictionary<string, int> degerler)
{
_cevre = new DaireCevre();
_degerler = degerler;
}
}

Bu sınıflarımız da constructor metod çağrıldığında ICevre türünden olan nesnemize ilgili cevre sınıfı nesnesi(DaireCevre, KAreCevre) oluşturularak atanıyor. Böylece doğru hesapla fonksiyonunun çağrılması sağlanıyor.

Son olarak uygulamamızı deneyeceğimiz kısmı yazabiliriz.

static void Main(string[] args)
{
Dictionary<string,int> dd = new Dictionary<string,int>();
dd.Add(“BirKenar”,4);
Sekil sk = new Kare(dd);
Console.WriteLine(sk.cevreHesapla(dd).ToString());
Console.ReadLine();
}

Böyle bir denemede ekrana 16 yazacaktır.

EnableViewState vs ViewStateMode

Geliştirdiğimiz ASP.NET uygulamalarında durum yönetimini ihtiyaçlarımıza göre birçok şekilde yapabiliriz. Bu seçeneklerden bir tanesi de ViewState yapısıdır. Hepimiz uygulamalarımızda bilinçli ya da bilinçsiz ViewState’leri kullanmışızdır. Bu bizi geleneksel asp ve php’deki kontrollerin durumunu korumak için yaptığımız ekstra işlerden kurtarır.

Ama ViewState’ler her zaman göründüğü kadar masum olamayabiliyor. Bazen ViewState’ler inanılmaz ölçülerde büyüyebiliyor ve bu durumda sayfa yükleme zamanlarınız farkedilir ölçüde artabiliyor. Bu yüzden ASP.NET uygulamalarımızda mümkün olduğunca durumunun tutulması gerekli olmayan kontrollerin ViewState’lerini kapatmamız gerekir.

Bu işlemi ASP.NET 4.0’dan önce EnableViewState özelliği ile yapıyorduk.  Böylece durumu tutulmasını istemediğimiz kontrollerin bu özelliğini false yaparak bu sorundan kurtulabiliyorduk. Ama EnableViewState’lerde tasarımı ile ilgili bir bug vardı. Eğer sayfamızda 100 kontrol içinden sadece 10 tanesinin durumunu tutmak istiyorsak, sayfamızın EnableViewState özelliğini false yapıp sadece bu 10 kontrolün özelliğini true yapmamız yeterli olmalıydı ama olmuyordu. Bu işlemi ancak sayfanın EnableViewState özelliğini true yapıp 90 kontrolün özelliklerini false yaparak sağlayabiliyorduk. Bunun da oluşturacağı zaman ve iş kaybını tahmin edebilirsiniz.

Ancak ASP.NET 4.0 ile gelen ViewStateMode özelliğini bizi bu sıkıntıdan kurtarmaktadır. Bu özellik parent kontrolün durumuna bakmaksızın kontrollerin durumlarını tutup tutmamamızı belirlememizi sağlar. ViewStateMode durumunu üç şekilde belirleyebiliriz.

  1. Inherit : Bu durumda kontrol parent kontrolün aynı şekilde davranacaktır.
  2. Enabled : Bu durumda parent kontrolün değeri disabled olsa bile kontrolün durum bilgileri tutulacaktır.
  3. Disabled : Bu durumda ise parent kontrolün durumu enabled olsa bile kontrolün durum bilgileri tutulmayacaktır.

NOT: TextBox,CheckBox ve RadioButton kontrollerinde EnableViewState özelliğini false yapsanız bile bu kontroller bazı durumlarını tutmaya devam etmektedir. İlgili bilgiyi buradan bulabilirsiniz.

 

Regular Expressions – Gruplar

Bir regular expression’da parantez arasına alınmış ifadeler grupları oluştururlar. Örneğin

(\w+)\s\d*

ifadesinde (\w+) bir grup oluşturur. İfadenin tamamı varsayılan olarak 0 indisli ilk gruptur. Grupların sıralaması dıştan içe doğru ve soldan sağa doğrudur. Yani en kampsamlı grup varsayılan gruptan sonra gelecektir. Daha sonra iç gruplarda soldan sağa doğru artan indisler alacaktır. Bir gruba şu şekilde isim verebiliriz.

(?<firstWord>\w+)\s\d*

Düzenli ifademizdeki tüm grupların değerlerine ve indislerine Match tipinde bir nesne üzerinden aşağıdaki örnekteki gibi ulaşabiliriz.

Match match = Regex.Match(input, pattern);

string degeri = match.Groups[1].Value;

int indis = match.Groups[1].Index;

Grupların en çok işimize yarayacağı durum backreference’ların kullanımıdır. Backreference’ler bir regular expression içinde tekrar eden yapıların gruplar vasıtasıyla tekrar yazılmadan kullanılmasını sağlayan yapılardır. Temel de iki çeşit backreference vardır.

Numbered Backreference :

Numbered backreference’da daha önce belirttiğimiz grubu tekrar yazmak yerine bir sayı aracılığıyla refere ederiz. Bir örnek vermek gerekirse

(\w+)\s\1

Bu regular expression’da \1  (\w+) grubunu refere etmektedir. Yani ifade (\w+)\s(\w+) şeklinde düşünebilir.

Eğer örneğimizde \1 yerine \2 yazmış olsaydık, ikinci bir grubumuz olmadığı için compiler bir hata mesajı verecek ve uygulamamızı derlememize engel olacaktı.

Numbered backreference ile karşılaşacağımız bir sıkıntı kullandığımız rakamların gerçek değerler ile çakışması ihtimalidir. Yani kullandığımız rakam bir backreference da olabilir bir octal kod da. Bu yüzden rakamların yorumlanmasında bir takım kurallar mevcuttur.

  1. \1 ve \9 arasındaki değerler herzaman backreference olarak yorumlanır.
  2. Birden fazla basamaklı bir sayının ilk rakamı 8 veya 9 ise (\81, \90 vb) normal değer olarak yorumlanır.
  3. \10 dan büyük değerler grup var ise backreference yok ise octal kod olarak yorumlanır.

 

Named Backreference :

Named backreference yukarıda bahsettiğimiz rakam karmaşıklıklarından kurtulamamızı sağlar. Named Backreference’da grubumuza bir isim veririz ve önceki örnekde rakamla ulaştığımız gibi bu kez grubumuza grubun ismiyle ulaşırız. Bir önceki örneğin named backreference gösterimi aşağıdaki gibi olacaktır.

(?<grubum>\w+)\s\k<grubum>

bu kez gördüğümüz gibi grubum adını verdiğimiz gruba ulaşmak için \k<grubum> şeklinde bir yapı kullandık.