NHibernate Linq, QueryOver ve Ölümüne Group, Count İşlemi

nhibernate_logoO kadar direnmeme ve iş ile alakalı olarak yazı yazmıyacağımı belirtmeme rağmen sonunda dayanamadım ve ufak bir not olarak bilgiyi burada paylaşmak istedim.

Öncelikli olarak yapmak istediğim ne ve buna engel olan faktör nedir diye kısa bir bilgi vereyim.

Belli bir veriyi gruplayarak bunların erkek ve bayanlara dağılım oranını bulmamız gerekiyordu. Hızlı ve pratiklik getirdiğinden dolayı NHibernate Linq ile hızlı bir şekilde grupladık ve count aldırdık.

Hiç birşey NHibernat’te özelliklede Linq’te bu kadar kolay olabilir mi?

Tabiki de hayır!

Heyecanlı  aksiyonlu ve bir o kadar da öğretici bir serüven ile yine karşımıza yepyeni bir sorun ile karşımızda.

Bu kadar hikaye yeterli, öncelikli olarak sıkıntılı Linq sorgumuza bir göz atalım.

var sqlQuery = (from patientMotionDiagnostic in Session.Query()
join patientMotion in Session.Query() on patientMotionDiagnostic.PatientMotion.Id equals patientMotion.Id
join icd in Session.Query() on patientMotionDiagnostic.ICD.Id equals icd.Id
join patientEntry in Session.Query() on patientMotion.PatientEntry.Id equals patientEntry.Id
join identification in Session.Query() on patientEntry.Identification.Id equals identification.Id
join serviceUnit in Session.Query() on patientMotion.ServiceUnit.Id equals serviceUnit.Id
group new
{
identification.Gender
}
by new
{
ServiceUnitId = serviceUnit.Id,
ServiceUnitName = serviceUnit.Name,
IcdName = icd.IcdName,
IcdId = icd.Id
}
into groupedData
select new IcdStatisticsListForPoliclinicOriginModel
{
IcdId = groupedData.Key.IcdId,
ServiceUnitId = groupedData.Key.ServiceUnitId,
ServiceUnitName = groupedData.Key.ServiceUnitName,
IcdName = groupedData.Key.IcdName,
MaleCount = groupedData.Count(x=>x.Gender == GenderEnum.MALE),
FemaleCount = groupedData.Count(x => x.Gender == GenderEnum.FEMALE)
}).ToList();

Yazmış olduğumuz Linq çalışıyor, bir hata vermiyor ama istediğimiz gibi çalışmıyor. 25. ve 26. satırlarda ki koşullu Count aldırma ne yazık ki çalışmıyor. Ürettiği sorgu;

count(*) as MaleCount,

count(*) as FemaleCount

olarak üretiyor. Bizim istediğimiz ise count içerisinde cinsiyeti erkek olanların sayısının bulunması. Yani aslında bizim istediğimiz;

count(IDENTIFICATION.GENDER = 1) as MaleCount,

count(IDENTIFICATION.GENDER = 2) as FemaleCount

Uzun süre internette araştırma yapıp, NHibernate Test kodlarını inceledikten sonra bu sorunun Linq ile çözülemiyeceğinde karar kılıp QueryOver’a çevirdik. Aşağı da QueryOver’a çevrilmiş hali.

PatientMotionDiagnostic patientMotionDiagnosticAlias = null;
ICD icdAlias = null;
PatientMotion patientMotionAlias = null;
ServiceUnit serviceUnitAlias = null;
PatientEntry patientEntryAlias = null;
Identification identificationAlias = null;

IcdStatisticsListForPoliclinicOriginModel model = null;

var sql = Session.QueryOver(() => patientMotionDiagnosticAlias);
sql = sql.Inner.JoinAlias(() => patientMotionDiagnosticAlias.ICD, () => icdAlias);
sql = sql.Inner.JoinAlias(() => patientMotionDiagnosticAlias.PatientMotion, () => patientMotionAlias);
sql = sql.Inner.JoinAlias(() => patientMotionAlias.ServiceUnit, () => serviceUnitAlias);
sql = sql.Inner.JoinAlias(() => patientMotionAlias.PatientEntry, () => patientEntryAlias);
sql = sql.Inner.JoinAlias(() => patientEntryAlias.Identification, () => identificationAlias);

return
sql.Select(Projections.Group(() => serviceUnitAlias.Id).WithAlias(() => model.ServiceUnitId),
Projections.Group(() => serviceUnitAlias.Name).WithAlias(() => model.ServiceUnitName),
Projections.Group(() => icdAlias.Id).WithAlias(() => model.IcdId),
Projections.Group(() => icdAlias.IcdName).WithAlias(() => model.IcdName),
Projections.Group(() => icdAlias.IcdCode).WithAlias(() => model.IcdCode),
Projections.Sum(Projections.Conditional(Restrictions.Eq(Projections.Property(() => identificationAlias.Gender), GenderEnum.FEMALE), Projections.Constant(1), Projections.Constant(0))).WithAlias(() => model.FemaleCount),
Projections.Sum(Projections.Conditional(Restrictions.Eq(Projections.Property(() => identificationAlias.Gender), GenderEnum.MALE), Projections.Constant(1), Projections.Constant(0))).WithAlias(() => model.MaleCount)
)

.TransformUsing(Transformers.AliasToBean())
.ToDbPagedList(search.page, search.limit);

Kodlar biraz üst üste geldi bunun nedenide destansı QueryOver yazım şekli. Fakat QueryOver ile yazımlar çoğu zaman bu şekilde uzun uzun oluyor.

23. ve 24. satırlara dikkat edelim. Burada Projections.Conditional kullanarak bunu yapabiliyoruz. Ama tek bir fark ile. Count yerine burada Sum kullandık. Aslında Burada kullandığım Sum ile Count bizim istediğimizi karşıladı. Nedenide iç kısımda ki eşitlik sorgulaması. Dedik ki eğer GENDER 2 ise 1 sonucunu ver, aksi takdirde 0 sonucunu ver. Bu şekilde de Sum ile topladığımız zaman bize istediğimiz sonucu verdi. Bu tabiki bizim bu sonucu çözmek için kullandığımız bir çözüm.

Devam edelim.

Restrictions.Eq ise bizim Sum içerisinde koşul yazmamı sağlayan yardımcımız. Standart şekilde if else mantığı burada yürütülüyor. Yani önce diyoruz ki identificationAlias.Gender alanı GenderEnum.MALE eşit ise sonuç olarak Projections.Constant(1) ile sabit 1 değerini gönder, yok efendim biz bayanız diyorsa da Projections.Constant(0) ile toplama işlemine katılması engelleniyor.

 İlk başta biraz itici birazda soğuk geliyor değil mi. Birde bunu kabul edene kadar çektiğimiz çileyi siz düşünün :)) Ama şunu açık yüreklilikle size söyliyebilirim bir defa yazdığınız bir kod çok elzem bir durum olmadığı sürece sorun çıkartmıyor ve oldukça stabil çalışıyor. Ehh zamanla seviyorsunuz. Nede olsa evlendikten sonra insan zamanla alışıyor ve sevmeye başlıyor :) Ama EntityFramework’un şu anda üzerinde çalıştığımız projeyi kaldıracak altyapısının ve esnekliğinin olmadığını düşünürsek en uygun eş adayı yine de NHibernate.

Devamını Oku