cub-e.net

just coding...

Microsoft Dynamics CRM 2015 Coklu Arama (Multi-Entity Quick Find)

Bugune kadar CRM ile ilgilenen pek cok kisi ve firma projeler icerisinde (ki bunlara bende dahilim ozellikle cagri merkezi projelerinde) CRM icin coklu arama ozelligi uygulamasi yazdi. Yani bir tane kutuya CRM icerisinde aramak istediginiz kisi, kurum ya da herhangi birsey yaziyorsunuz ve bu otomatik olarak daha onceden belirlediginiz nesneler icerisinde araniyor ve ekrana getiriliyor. Bugun Dynamics CRM 2015 ile birlikte bu ozellik CRM icersinde gomulu olarak geliyor ve sistem menusunde her zaman ulasilabilir bir yerde duruyor.


Bu ekrani acabilmek icin yukaridaki resimde de godugunuz uzere buyutec simgesine tiklamak gerekiyor. Cikan ekrandaki metin kutusuna birseyler yazip enter'a bastiginizda ise yazdiginiz kelime daha onceden ayarlarlar bolumunde ayarlanan butun nesneler icerisinde aranmakta ve sonuclar karsiniza gelmekte. 

Dilerseniz bu ekrana gecmeden once ayarlar bolumunde nasil ayarlar yapabilecegimize bakalim. Bu ekran ile ilgili ayarlari acmak icin Ayarlar>Yonetim bolumu altinda bulunan Sistem Ayarlari dugmesine tiklamamiz gerekmekte. Acilan ayarlar ekraninda asagiya dogru ilerledigimizde "Hizli Bulmayi Ayarla" yazisi altindaki 2 secenek iste aradigimiz ayarlar.


Hizli bul kayit sinirlarini etkinlestir: Bu ayar arama performansini arttirmak için arama kayitlarini 10.000 kayitla sinirli tutan ozellik.
Arama icin varliklari secin: Bu dugmeye tikladiginizda ise bir pencere acilacak ve bu arama ozelliginin hangi varliklar uzerinde etkili olacagini ayarlamanizi saglayacaktir.


Burada sitediginiz nesneleri ekleyebilir ve cikartabilirsiniz ama unutmamamiz gereken bir sinirimiz mevcut o da max. 10 tane nesne secebiliyoruz. 10'dan fazla nesne sectiginizde su sekilde bir uyari karsiniza cikacaktir.


Iste bu ekran vasitasiyla ayarlamalarimizi yaptiktan sonra gecek arama ekranimiza donebiliriz. Bu ekran uzerinde istedigimizi yazip entrer'a bastigimizda sonuclar ekrana gelecektir.


Bu ekranda bazi onemli ozellikler bulunmakta;
  • her bir nesne uzerindeki + dugmesine tiklayarak yeni kayit ekleyebilirsiniz.
  • herhangi bir kayda tikladiginizda o kayit acilir.
  • arama yapilacak alanlari arama hizli gorunumu uzerinden ayarlayabilirsiniz.
  • O nesne icin arama hizli gorunumunde ayarlanmis ilk uc alan goruntulenir.
  • ekranin sag tarafinda bulunan filtre olcutunu kullanarak kayitlari filtreleyebilirsiniz.
Bu sayede CRM 2015'de yillardir soze edilen ve beklenen bir ozelligi de Dynamics CRM platformu icerisinde gormus olduk.

Satışlarınız Düşmesin Yükselsin

Acikakademi.com sponsorlugunda gerceklestirecegimiz bu online etkinlikte Dynamics CRM 2013 SP1 uzerinde bir satis senaryosunun nasil gerceklestirilecegi anlatilacaktir. 09.10.2014 tarihinde Turkiye saatiyle 20:00 da etkinlik baslayacaktir. Internet uzerinden online olarak izleyebileceginiz etkinlik hakkinda daha fazla bilgiye buraya tiklayarak ulasabilirsiniz.

Sektörünün önde gelen firmalarından Contoso, satışlarını artıramamaktan yakınıyor, firma sahibi Ahmet Bey ise sorunun nereden kaynaklandığını tam teşhis edemiyordu. Satış ekipleri mi mevcut potansiyelin farkında olamıyor, yoksa potansiyellerini bildikleri halde işleri rakiplerine mi kaptırıyorlardı? Ahmet Bey’in bu sorunu çözebilmesi için daha ayrıntılı bilgiye sahip olması gerekiyor, ancak satış ekiplerinden daha fazla bilgi toplamak için girişimde bulunduğunda, tepki alıyordu. Dışa dönük ve satışa odaklanmış bu ekipler, sistemle uğraşmaya ve bilgi girişi yapmaya direniyorlar; tüm zamanlarını satışa ayırmak istiyorlardı. Satış ekiplerinden her görüşmelerini, her teklifi, teklif sonrası her takip adımını sisteme girmelerini istemek hiç de kolay değildi.

Pazarlama bölümü ise ürünlerini etkin tanıtamıyordu. Doğru müşteriye doğru iletişim kanalı ile ulaşmak ve bunu yaparken ekibinin aktivitelerini de denetleyerek, kampanyanın maliyetini düşürmek istiyorlardı. Tabii ki ürün ve pazarlama performanslarının etkin bir şekilde raporlanması da gerekiyordu.

İşte firmasındaki bütün bu sorunlara cevap arayan Ahmet Bey, araştırmasında Microsoft’un Dynamics CRM İş Platformunun, esnek ve genişleyebilir yapısı ile çok iyi bir ürün olduğunu gördü. Satış ve pazarlama bölümündeki sorunları giderebildiği gibi, servis talepleri ile çağrıları yönetebileceğini, servis aktiviteleri ile sahadaki personelin iş yükünü yönetebileceğini öğrendi. Üstelik iş akışları ile sistem otomatik olarak kendisine verilmiş işleri yapmaktaydı. İş akışları sayesinde bir olay ya da değişiklik olduğunda sistem bunu algılayıp gerekli işlemleri yerine getirebilmekteydi. Ayrıca bu sisteme tümleşik olan Web Portalı sayesinde kullanıcılar kendi servis taleplerini açmanın yanında, kendisiyle ilgilenen teknik servis personelinin boş olduğu saatlere randevu da alabiliyordu. Sahadaki teknik servis personeli sistemle entegre tablet ve akıllı cep telefonu arabirimi sayesinde şirket sistemine bağlanabilmekte, böylece müşteri bilgilerine ulaşabilmekte ve üzerlerindeki işleri, servis taleplerini çevrimiçi olarak görebilmekteydiler. Üstelik esnek raporlama arayüzü ile herkes kendi anlayabildiği şekilde raporlar alarak sistemi en yüksek verimle kullanabilmekteydi. 

Dynamics CRM 2013 and Microsoft Dynamics CRM Online nedir?

Microsoft Dynamics CRM 2013 ve Microsoft Dynamics CRM Online güçlü ve gelişmiş bir iş platformu deneyimini kullanıcılarına sunmaktadır. Dynamics CRM içerisine entegre edilmiş gelişmiş uygulama altyapısı sayesinde sadece uygulamayı kullanmakla kalmıyor aynı zamanda onun istediğimiz yerlerini işimize uygun halde yeniden programlayarak hem kendi içinde hem de dış uygulamalarla konuşabilen kendi iş çözümümüzü oluşturabiliyoruz. Bu şekilde genişlemeye biz xRM adını vermekteyiz. Yani herşeyin yönetilebildiği bir platform.


Dynamics CRM Microsoft tarafından çeşitli zamanlarda çıkartılan çeşitli service pack ve rollup paketleriyle genişlemekte ve yeni özellikler kazandırılmaktadır.

Dynamics CRM mevcutta üç farklı çeşitte paketlenmektedir;

  • Microsoft Dynamics CRM 2013 on-premises deployment
  • Microsoft Dynamics CRM 2013 Internet-facing deployment (IFD)
  • Microsoft Dynamics CRM Online

Bu üç çeşitte de aşağıdaki şekillerde son kullanıcı arabirimi sunmaktadır;

  • Microsoft Dynamics CRM Web client
  • Microsoft Dynamics CRM for Microsoft Office Outlook
  • Microsoft Dynamics CRM for Microsoft Office Outlook with Offline Access
  • Microsoft Dynamics CRM for phones and tablets

Yani siz Dynamics CRM’i şirketiniz için kullanmaya başladıktan sonra isterseniz Internet Explorer, Chrome, Safari gibi browserlardan sisteme girebilir isterseniz de cep telefonu ya da Outlook üzerinden de kullanabilirsiniz.

Oluşturduğunuz uygulamaları Dynamics CRM ile Web Servisleri aracılığıyla entegre edebilirsiniz. Bu web servisleri arka tarafta Dynamics CRM’in kalbi olan xRM platform katmanı ile iletişim halinde bulunmaktadır.

Bu katmanda güvenlik gibi genel kısımları içermektedir ve bir uygulamanın çalışması için gerekli olan yapı taşlarını içerir fakat kendisi ilgili nesnelerinin bir koleksiyonun başka bir şey değildir. Nesneler arasındaki iletişimi sağlar ve tekliften siparişe, siparişten faturaya gibi fiyatlandırma mantığı gibi daha genişletilebilir bir mantığı uygulamak için kullanılır.

XRM platformu ayrıca, güvenlik yoluyla verilere ve veritabanına erişimi denetler. Ayrıca iş akışı ve özel iş mantığı uygulamaları (plugins) için süreçlerinin tetiklenmesini denetler. Bu katman Microsoft Exchange Server üzerinden gelen ve giden e-posta işleme alır.

Microsoft Dynamics CRM 2013 'de FetchXML ile Aggregate(Toplama Sorguları) İşlemleri

Bir önceki makalede FetchXML ile neler yapabileceğimize değindik. (Buraya tıklayarak o makaleye ulaşabilirsiniz) Eğer istersek FetchXML ile grouping ve aggregate işlemlerini de yapabilmekteyiz. FetchXML ile aşağıdaki işlemler desteklenmektedir;

·         sum

·         avg

·         min

·         max

·         count(*)

·         count(attribute name)

 

Aggregate yapabileceğimiz sorgu örneği standart olarak bildiğimiz fetchXML kurgusundan farklı değil burada sadece attribute kısmında aggregate ile ilgili keyword yer almakta. Aggregate=true diyerek bu şekilde bir sorgulama yapacağımızı sisteme söylüyoruz. Sonrasında ise sırasıyla hangi alan üzerinden işlem yapılacağını, alias yani etiketin ne olacağını ve aggregate türümüzün ne olacağını sisteme söylüyoruz.

<fetch distinct='false' mapping='logical' aggregate='true'>

  <entity name='opportunity'>

    <attribute name='name' alias='opportunity_count' aggregate='count'/>

  </entity>

</fetch>

Bu şekilde oluşturduğumuz bir sorgu ile aslında şunu demek istiyoruz : “select count('name') as opportunity_count from opportunity” yani standart bir count işleminden fazlası değil yaptığımız.

 

Bu fetchXML’i çalıştırmak için ise aşağıdaki gibi bir kod hazırladım;

 

string opportunity_count = @" <fetch distinct='false' mapping='logical' aggregate='true'>

                                                <entity name='opportunity'>

                                                    <attribute name='name' alias='opportunity_count' aggregate='count'/>

                                                </entity>

                                              </fetch>";

 

                EntityCollection opportunity_count_result = ServiseBaglan().RetrieveMultiple(new FetchExpression(opportunity_count));

 

                foreach (var c in opportunity_count_result.Entities)

                {

                    Int32 aggregate2 = (Int32)((AliasedValue)c["opportunity_count"]).Value;

                    System.Console.WriteLine("Count of all opportunities: " + aggregate2);

                }

 

Burada dikkat edilmesi gereken nokta geriye EntityCollection döndürmesi. Bir önceki makalemden hatırlarsanız normalde bu tarz sorgulama ile biz geriye account, contact gibi sitem içerisindeki nesnelerden oluşmuş bir dizi alıyoruz. FetchXML yapısı gereği tek bir metod kullanıyoruz ve bu metod sonucunda yukarıdaki örnekte olduğu gibi tek bir satır dahi dönecek olsa bile yine sonucu bir collection içerisinde almaktayız. Bu nedenle mecbur foreach içerisinde dönüyoruz ya da collection’un 0. üyesini alıyoruz. Ben örneğimde foreach ile collection içinde döndüm ve gördüğünüz gibi değeri aldım.

 

Yine bir önceki makalemde ileri düzey sorgular bölümünde anlattığım gibi eğer nesne üzerinden direkt bir değer döndürmeyeceksek sonuç her zaman AliasedValue olarak gelmekte. işte bu nedenden dolayı şu şekilde bir convert işlemi yapıyoruz;

 

Int32 aggregate2 = (Int32)((AliasedValue)c["opportunity_count"]).Value

 

Bu işlem sonucunda bu koddan aşağıdaki gibi çıktımızı alabiliyoruz.

 

 

Eğer distinct yapmak istersek de fetchXML içerisine yerleştireceğimiz bir distinct=”true” ifadesiyle ile sisteme bunu beliretebiliriz.

Aşağıda ise distinct yapılmış bir kod örneği bulunmakta;

 

<fetch distinct='false' mapping='logical' aggregate='true'>

  <entity name='opportunity'>

    <attribute name='name' alias='opportunity_distcount' aggregate='countcolumn' distinct='true'/>

  </entity>

</fetch>

 

Bu FetxhXML’i de aşağıdaki kodla çalıştırabiliriz.

 

                string opportunity_distcount = @"

                                           <fetch distinct='false' mapping='logical' aggregate='true'>

                                               <entity name='opportunity'>

                                                  <attribute name='name' alias='opportunity_distcount' aggregate='countcolumn' distinct='true'/>

                                               </entity>

                                           </fetch>";

 

                EntityCollection opportunity_distcount_result = ServiseBaglan().RetrieveMultiple(new FetchExpression(opportunity_distcount));

 

                foreach (var c in opportunity_distcount_result.Entities)

                {

                    Int32 aggregate4 = (Int32)((AliasedValue)c["opportunity_distcount"]).Value;

                    System.Console.WriteLine("Distinct name count of all opportunities: " + aggregate4);

                }

 

                Console.ReadLine();

 

Bu kod sonucunda da aşağıdaki gibi bir cevap alabiliriz sistemden;


 

Gelin olayı biraz renklendirelim. Bu sefer de bir sorgu içerisinde birden fazla aggregate seçeneğinin cevabını isteyelim. Böyle bir işlem için standart fetchXML’in içerisinde sırasıyla hangi özelliklerin bize geri döneceğini belirtiyoruz tabii unutulmaması gereken nokta ise hepsi için ayrı birer alias olması gerektiğidir.

 

<fetch distinct='false' mapping='logical' aggregate='true'>

  <entity name='opportunity'>

    <attribute name='opportunityid' alias='opportunity_count' aggregate='count'/>

    <attribute name='estimatedvalue' alias='estimatedvalue_sum' aggregate='sum'/>

    <attribute name='estimatedvalue' alias='estimatedvalue_avg' aggregate='avg'/>

  </entity>

</fetch>

 

Bu sorgu için ise aşağıdaki kodu hazırladım;

 

                string estimatedvalue_avg2 = @"

                                    <fetch distinct='false' mapping='logical' aggregate='true'>

                                        <entity name='opportunity'>

                                           <attribute name='opportunityid' alias='opportunity_count' aggregate='count'/>

                                           <attribute name='estimatedvalue' alias='estimatedvalue_sum' aggregate='sum'/>

                                           <attribute name='estimatedvalue' alias='estimatedvalue_avg' aggregate='avg'/>

                                        </entity>

                                    </fetch>";

 

                EntityCollection estimatedvalue_avg2_result = ServiseBaglan().RetrieveMultiple(new FetchExpression(estimatedvalue_avg2));

 

                foreach (var c in estimatedvalue_avg2_result.Entities)

                {

                    Int32 aggregate8a = (Int32)((AliasedValue)c["opportunity_count"]).Value;

                    System.Console.WriteLine("Count of all opportunities: " + aggregate8a);

                    decimal aggregate8b = ((Money)((AliasedValue)c["estimatedvalue_sum"]).Value).Value;

                    System.Console.WriteLine("Sum of estimated value of all opportunities: " + aggregate8b);

                    decimal aggregate8c = ((Money)((AliasedValue)c["estimatedvalue_avg"]).Value).Value;

                    System.Console.WriteLine("Average of estimated value of all opportunities: " + aggregate8c);

 

                }

 

                Console.ReadLine();

 

İşte bu şekilde aynı anda count, sum ve avg işlemlerini yapabilmekteyim. Sonuç ise aşağıdaki gibi;

 

 

Bu iki makalede FetchXML’ler üzerinde konuştuk. Umarım faydalı olmuştur.

Microsoft Dynamics CRM 2013 İle Veri Sorgulama Yöntemleri

Giriş
Bu yazı dizimizde sizlere Dynamics CRM’den veri çekmek için kullanabileceğiniz yapıları anlatacağım. FetchXML ile veri çekmek bence şu anda kullanılabilecek en pratik yöntem ama bunun haricinde .Net Language-Integrated Query(LINQ) ile early ve late binding türler üzerinden veri çekebileceğiniz gibi Dynamics CRM’in Query Expression mimarisini kullanarak da veri çekebilirsiniz.

Bu üçü haricinde OData ve Filtered View’ları kullanarak da veri çekebilirsiniz. OData(Open Data Protocol) Rest tabanlı servisler için protokol görevi gören bir veritabanı sorgulama yapısıdır. Filtered View’lar ise standart SQL ile SQL Server üzerinden direkt veri çekmek için kullanabileceğimiz yapılar ama Filtered View ve OData ile geriye CRM obje sınıflarıyla veri döndürememekteyiz. Yani SQL ile bir veri çektiğinizde DataSet ya da DataTable gibi yapılara veriyi çekebiliriz ama FetchXML ile CRM entity sınıfından geriye dönüş alırız bu yüzden kullanım açısından ilk yöntem daha kullanışlıdır.

LINQ ile veriler üzerinde işlem yapabilmek için Organization Service Context sınıfını türetip projeye eklemek gerekmektedir.

Benzerliklerini bir tablo üzerinde karşılaştırırsak;

Sorgu Biçimi

Özellikleri

FetchXML

QueryExpression'ın bütün özelliklerine destek verdiği gibi matematiksel işlemler ve gruplamaya da destek verir. Sorgular XML standartlarına göre yazılır.

QueryExpression

Sorgular bir obje modeli üzerinde icra edilir.

LINQ

QueryExpression'ın limitleriyle sınırlıdır.

 

Yetenekleri bakımından karşılaştırırsak;

 

Odata

QueryExpression

FetchXML

LINQ

Filtered Views

Create, Update, Delete desteği

X

 

 

X

 

Sorgu sonucu kayıtları döndürme

X

X

X

X

X

Rapor yaparken kullanılabilirlik

 

 

X

 

X

Sorgu sonucu birden fazla türde kayıt döndürme

X

X

X

X

X

outer joins yapabilme

X

 

X

 

X

Çalışma zamanında doğrulama

 

X

 

X

 

İlişkisiz nesneleri bağlama (Union)

 

 

 

X

X

Matematiksel İşlemleri destekleme

X (Sınırlı)

 

X

X

X

 

FetchXML

FetchXML en güzel tarafı Dynamics CRM içerisinde Gelişmiş Ara (Advanced Find) ile oluşturduğumuz sorguların da bu şekilde sistemde kaydedilmesi. Ayrıca bu şekilde oluşturduğumuz sorguları da .xml olarak CRM’den alabiliyoruz böylece yazdığımız uygulama ya da rapor içerisinde de kullanabilmekteyiz. Böylece uzun uzun FecthXML hazırlamak yerine sistemin nimetlerinden yararlanarak sorgumuzu hazırlayabilmekteyiz. Bunun için gelişmiş bul içerisinde Fetch XML indir düğmesine tıklıyoruz.



CRM’de de bu kullanıcı sorguları userquery, organizasyon sorguları ise savedquery içerisinde saklanmaktadır.

IOrganizationService.RetrieveMultiple methodu ile FetchXML sorgulaması yapabilmekteyiz bunun için FetchXMLToQueryExpressionRequest mesajını kullanmak gerekmektedir. Ayrıca daha önce de belirttiğim gibi aggregations yani sum, max, min, count gibi matematiksel işlemleri de FetchXML ile yapabilmekteyiz.

Bu makaledeki örneklerde CRM Servisini çağırmak daha önceki Singleton Tasarım Deseni üzerinden geliştirdiğim servise bağlanma metodunu kullanıyorum. Sözünü ettiğim makaleye buradan ulaşabilirsiniz.  Bu noktayı siz de kendinize uygun olarak değiştirebilirsiniz.

Sorguyu Hazırlama

Bu noktada örnek bir fetchXML’i inceleyelim;

<fetch mapping='logical'>

  <entity name='account'>

    <all-attributes/>

  </entity>

</fetch>

FetchXML mutalaka “fetch” kelimeleri arasında yer almalı. Sonrasında ise “entity” kelimesi ile geri dönecek nesnenin türünü söylüyoruz. Sonrasında ise hangi alanların geri döneceğini ve nasıl koşullar olacağını belirtiyoruz. Tabii yukarıdaki örnekte bunlar yok “all-attributes” ile biz bütün alanları geri döndür diyoruz.

Size bu sorguyu SQL ile anlatmam gerekirse : “select * from account” şeklinde olacak. Şimdi işi biraz daha renklendirelim;

 

<fetch mapping='logical'>

  <entity name='contact'>

    <attribute name='fullname'/>

    <attribute name='createdon'/>

    <filter type='and'>

      <condition attribute='jobtitle' operator='eq' value='Purchasing Assistant'/>

    </filter>

  </entity>

</fetch>

 

Bu sorguda ise geriye contact yani ilgili kişi nesnesi geri dönecek ama sadece “fullname” ve “createdon” alanları ile. Ayrıca burada bir kriterimiz de var “jobtitle” alanı “Purchasing Assistant” olacak kayıtları alıyoruz. Yani yine SQL ile anlatırsam : “select fullname, createdon from contact where jobtitle='Purchasing Assistant'” Burada dikkat ettiyseniz operatör diye bir ifade yer almakta. Sorgulama yaparken değerlerin nasıl koşullarda alınması gerektiğini burada belirtiyoruz. Yani aşağıdaki tabloda da görebileceğiniz üzere sorgu ifadeleri kısmında "Koşul İfadesi" kısmında yazan değerler bizim normal sql cümlesinde kullandığımız ifadelere benzemektedir. Tek fark "=","<",">" gibi ifadelerin yerlerine "eq","gt","lt"gibi text bazlı ifadelerin gelmiş olmasıdır. 

 

Koşul

Koşul İfadesi

Değer

equals x

eq

x

does not equal x

ne

x

is greater than x

gt

x

is greater than or equal to x

ge

x

is less than x

lt

x

is less than or equal to x

le

x

begins with x

like

x%

does not begin with x

not-like

x%

ends with x

like

%x

does not end with x

not-like

%x

contains x

like

%x%

does not contain x

not-like

%x%

exists

not-null

 

does not exist

null

 

anytime

not-null

 

yesterday

yesterday

 

today

today

 

tomorrow

tomorrow

 

in next 7 days

next-seven-days

 

in last 7 days

last-seven-days

 

next week

next-week

 

last week

last-week

 

this week

this-week

 

this month

this-month

 

last month

last-month

 

next month

next-month

 

this year

this-year

 

next year

next-year

 

last year

last-year

 

on x

on

x

on or after x

on-or-after

x

on or before x

on-or-before

x

in between

between

 

not between

not-between

 

in

in

 

not in

not-in

 

equals user id

eq-userid

 

does not equal user id

ne-userid

 

equals business id

eq-businessid

 

does not equal business id

ne-businessid

 

 

 

Sorguyu Çalıştırma

 

Bu  sorguyu çalıştırcak metod ise RetrieveMultiple metodudur ve servisi örneklediğimizde karşımıza çaıkmaktadır. Bu metod makalenin başında bahsettiğim QueryExpression sınıfını da alarak işlem yapabilmektedir. Bu metodun bir de kardeşi vardır ve hazır yeri gelmişken bundan da bahsedeyim.

 

Retrieve Metodu ID’si verilen entity nesnesinin bildirdiğiniz sütünlarını bize geri döndürür.  Kullanımı çok basit olan bu metod geriye Entity türünden bir nesne döndürür. Bu nesne zaten bizim parametre olarak verdiğimiz nesne adının kendisidir ve bizim belirttiğimiz sutünları doldurarak getirir. Bu metod aslında sql cümlesi olarak bakarsak “ select alanisimleri from (nesne)entity where entityid = '...' ” işlemini yerine getirmektedir.

Aşağıdaki örnekle devam edelim;

Entity slead = servis.Retrieve("lead", new Guid("7bE545CCD3-9A3A-E011-BA8B-78E7D1623F9D"), new ColumnSet(new string [] { "fullname", "companyname" }));

 

foreach (var item in slead.Attributes)

{

    Console.WriteLine(item.Key + ":" + item.Value);

}

 

Eğer entity’nin bütün alanlarını geri döndürmek istiyorsak new ColumnSet(true) komutunu ColumnSet yerine vermemiz gerekmektedir. Yani eğer siz sorgulayacağımız nesnenin GUID türünden idsini biliyorsanız ve join gibi işlemlerle işiniz yoksa düz mantıkta bir sorgulama yapmak için bu metodu kullanabilirsiniz.

 

RetrieveMultiple Metodu ise gerçek anlamda karışık sorguları yapmamıza olanak tanır. Ama burada tamamen object oriented bir mimari söz konusudur yani temelde bir sorgulama cümlesi olmadan QueryExpression ya da QueryByAttribute sınıflarının örnekleri üzerinden sorgulama işlemi yapılmasına da olanak sağlar.

 

Bizim odak konumuz ise fetchXML olduğu için konuyu fazla dağıtmadan devam edelim.

Bu fetchXML sorgusunu çalıştırmak için ise şu şekilde bir kod yazdım;

 

string fetchXml = @"<fetch mapping='logical'>

                                 <entity name='contact'>

                                    <attribute name='fullname'/>

                                    <attribute name='createdon'/>

                                     <filter type='and'>

                                        <condition attribute='jobtitle' operator='eq' value='Purchasing Assistant'/>

                                     </filter>

                                 </entity>

                             </fetch>";

 

 

                EntityCollection contactlist = (EntityCollection)ServiseBaglan().RetrieveMultiple(new FetchExpression(fetchXml));

                foreach (var cnt in contactlist.Entities)

                {

                    Console.WriteLine(cnt.Attributes["fullname"].ToString() + ":" + cnt.Attributes["createdon"].ToString());

                }

 

                Console.ReadLine();

 

Sorgu Sonucu

 

İşte biraz önce bahsettiğim RetrieveMultiple metoduna fetchXML’i veriyorum. Dönüşte ise sistem bana EntityCollection içerisinde talep ettğim entity’i vermekte. Bu noktadan sonra foreach ile bütün kayıtar içinde dolaşabilir ve Attributes ile alanlara ulaşabilirim.

 

Bu kodu çalıştırınca da aşağıdaki gibi sonucu almaktayım;

 

İleri Seviye Sorgular

 

Yukarıda temel bilgileri verdikten sonra ileri seviye bilgilerle buradan devam edebiliriz.

 

<fetch version='1.0' output-format='xml-platform' mapping='logical' distinct='false'>

  <entity name='lead'>

    <attribute name='fullname' />

    <attribute name='createdon' />

    <filter type='and'>

      <condition attribute='ownerid' operator='eq-userid' />

      <condition attribute='statecode' operator='eq' value='0' />

    </filter>

  </entity>

</fetch>

 

Bu sorguda ise “lead” türünden nesneleri geri döndürmekteyiz. “fullname” ve “createdon” alanlarını istemekteyiz sorgu ile ama “ownerid” yani kayıtların sahibi “eq-userid” diyerek servise kim bağlandıysa onun kayıtları olacak ve durumları da aktif olacak.

 

Düz mantıktaki tek bir nesne üzerinden sorgular işte bu şekilde yapılmakta ama sistem bundan daha fazlasına izin verebilmekte yani biz eğer istersek inner ya da outer join yaparak başka nesneler ile de ilişki içerisindeki kayıtları da geriye döndürebiliriz. Aşağıdaki örnek üzerinden açıklamaya çalışayım;

 

<fetch version='1.0' output-format='xml-platform' mapping='logical' distinct='false'>

  <entity name='account'>

    <attribute name='name' />

    <attribute name='address1_city' />

    <attribute name='telephone1' />

    <order attribute='name' descending='true' />

    <filter type='and'>

      <condition attribute='name' operator='like' value='{0}%' />

    </filter>

    <link-entity name='contact' from='contactid' to='primarycontactid' alias='kisi'>

      <attribute name='emailaddress1' />

      <filter type='and'>

        <condition attribute='firstname' operator='like' value='{0}%' />

      </filter>

    </link-entity>

    <link-entity name='systemuser' from='systemuserid' to='createdby' visible='false' link-type='outer' alias='kullanici'>

      <attribute name='firstname' />

    </link-entity>

  </entity>

</fetch>

 

Burada temelde yine bir nesne üzerinde yani “account” nesnesi üzerinden hareket ediyor gibi gözüksek de “link-entity” düğümleriyle işi genişletiyoruz. Örnekte görebileceğiniz üzere “contact” ve “systemuser” nesneleri üzerinden de geriye alan döndürdüğümüz gibi bunlar üzerinden de sorgulama yapabilmekteyiz. “link-entity” içerisinde sisteme hangi nesne ile link yapacağımızı ve bu nesnelerin hangi alanlar üzerinde birbirleriyle ilişki içerisinde olduklarını söylemekteyiz. Ayrıca “alias” vererek de kodun ilerleyen kısımlarında buradan gelecek alanlar için bir tanımlayıcı da oluşturabilmekteyiz.

 

Burada ben “link-type” olarak “outer” kullandım ama siz isterseniz “inner join” yapmak için “inner” de kullanabilirsiniz.

 

Bu arada belirtmeliyim ki “order” komutu ile de belli bir alan üzerinden kayıtların “ascending” ya da “descending” olarak sıralanmasını da sağlayabilmekteyiz.

 

Örnekte ben firma adı ve kişi adı için konsoldan parametre almaktayım bu yüzden orada “{0}” ifadesini görmektesiniz.

 

Bu sorguyu daha raht anlamanız için SQL cümlesine çevirecek olursam;

 

select name, address1_city, telephone1, kisi.emailaddress1, kullanici.firstname from account inner join contact kisi on kisi.contactid = account.primarycontactid

inner join systemuser kullanici on kullanici.systemuserid = account.createdby

where account.name like '%%' and kisi.firstname like '%%'

order by account.name desc

 

Böyle bir sorgu yazmamız gerekirdi. Bu kodu çalıştıracak örnek uygulama ise şu şekilde;

 

string fetch = @"

                                   <fetch version='1.0' output-format='xml-platform' mapping='logical' distinct='false'>

                                  <entity name='account'>

                                    <attribute name='name' />

                                    <attribute name='address1_city' />

                                    <attribute name='telephone1' />

                                    <order attribute='name' descending='true' />

                                    <filter type='and'>

                                      <condition attribute='name' operator='like' value='{0}%' />

                                    </filter>

                                    <link-entity name='contact' from='contactid' to='primarycontactid' alias='kisi'>

                                      <attribute name='emailaddress1' />

                                      <filter type='and'>

                                        <condition attribute='firstname' operator='like' value='{0}%' />

                                      </filter>

                                    </link-entity>

                                    <link-entity name='systemuser' from='systemuserid' to='createdby' visible='false' link-type='outer' alias='kullanici'>

                                      <attribute name='firstname' />

                                    </link-entity>

                                  </entity>

                                </fetch>";

 

                Console.WriteLine("bir karakter yazın:");

                fetch = string.Format(fetch, Console.ReadLine());

 

                EntityCollection groupby1_result = ServiseBaglan().RetrieveMultiple(new FetchExpression(fetch));

 

                foreach (var c in groupby1_result.Entities)

                {

                    Console.WriteLine("ad:" + c["name"].ToString());

                    Console.WriteLine("sehir:" + c["address1_city"].ToString());

                    Console.WriteLine("telefon:" + c["telephone1"].ToString());

                    Console.WriteLine("kisi eposta:" + ((AliasedValue)c["kisi.emailaddress1"]).Value.ToString());

                    Console.WriteLine("kullanici:" + ((AliasedValue)c["kullanici.firstname"]).Value.ToString());

                    Console.WriteLine("\n");

                }

 

 

                Console.ReadLine();

Burada bir noktanın üzerinde durmamız gerekmekte. Kişi ve Kullanıcı üzerindeki alanlardan veri alabilmek için “kisi” ve “kullanici” isimli alias’ları kullanmıştık. İşte bu alanlardan veri okuyacağımız zaman şu şekilde bir kullanıma ihtiyacımız bulunmakta;

 

((AliasedValue)c["kullanici.firstname"]).Value.ToString()

 

Yani gelen değeri önce “AliasedValue”’e parse etmemiz sonrasında ise “Value” üzerinden değerini almalıyız.

 

Çıktımız ise şu şekilde. Yani adı “a” ile başlayan bir firma ve ona adı “a” ile başlayan birinci ilgili kişi kaydı olarak eklenmiş bir kayıt bulunmakta.

 


 

Eğer XSD dosyalarını okumayı biliyorsanız fetchXML üzerinde nereye hangi değerlerin nasıl gelebileceğini anlayabilirsiniz. Ayrıca bunu dosya haline getirerek Visual Studio’ya tanıtırsanız fetchXML yazarken denetleme yaparak size hataları da gösterecektir. Ama şu anda en güzel fetchXML oluşturma yöntemi daha önce de belirttiğim gibi Gelişmiş Ara aracını kullanmaktır.

 

<?xml version="1.0" encoding="utf-8" ?>

<xs:schema id="fetch" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema"

xmlns:mstns="http://tempuri.org/fetch/unique">

  <xs:annotation>

    <xs:documentation>Schema name: fetch-schema</xs:documentation>

  </xs:annotation>

  <!--

 

condition element - used for capturing entity and link-entity

"where" clause criteria

 

-->

  <!-- [XDR-XSD] "value" element  -->

  <xs:element name="value" type="xs:string"></xs:element>

  <!-- [XDR-XSD] "condition" element  -->

  <xs:element name="condition">

    <xs:complexType>

      <xs:choice minOccurs="0" maxOccurs="unbounded">

        <!-- -->

        <!--

The attribute "value" is used for all operators that compare to a single value (for example, eq).

The element "value" is used for operators that compare to multiple values (for example, in).

Some operators require neither the attribute "value" or the element "value" (for example, null).

-->

        <xs:element name="value" minOccurs="0" maxOccurs="unbounded">

          <xs:complexType>

            <xs:simpleContent>

              <xs:extension base="xs:string">

                <xs:attribute name="uiname" type="xs:string" />

                <xs:attribute name="uitype" type="xs:string" />

              </xs:extension>

            </xs:simpleContent>

          </xs:complexType>

        </xs:element>

      </xs:choice>

      <!-- -->

      <xs:attribute name="column" type="xs:string" />

      <xs:attribute name="attribute" type="xs:string"></xs:attribute>

      <xs:attribute name="entityname" type="xs:string"></xs:attribute>

      <xs:attribute name="operator" use="required" type="operator"></xs:attribute>

      <!--

The attribute "value" is used for all operators that compare to a single value (for example, eq).

The element "value" is used for operators that compare to multiple values (for example, in).

Some operators require neither the attribute "value" or the element "value" (for example, null).

-->

      <xs:attribute name="value" type="xs:string"></xs:attribute>

      <xs:attribute name="aggregate" type="AggregateType"></xs:attribute>

      <xs:attribute name="alias" type="xs:string"></xs:attribute>

      <xs:attribute name="uiname" />

      <xs:attribute name="uitype" />

      <xs:attribute name="uihidden" type="TrueFalse01Type" />

    </xs:complexType>

  </xs:element>

  <!--

 

filter element - used for constructing complex conditionals

 legal on entity and link-entity

 

-->

  <!-- [XDR-XSD] "filter" element  -->

  <xs:element name="filter">

    <xs:complexType>

      <xs:choice minOccurs="0" maxOccurs="unbounded">

        <!-- -->

        <xs:element ref="condition" minOccurs="0" maxOccurs="500" />

        <xs:element ref="filter" minOccurs="0" maxOccurs="unbounded" />

      </xs:choice>

      <!-- -->

      <xs:attribute name="type" default="and">

        <xs:simpleType>

          <xs:restriction base="xs:NMTOKEN">

            <xs:enumeration value="and" />

            <xs:enumeration value="or" />

          </xs:restriction>

        </xs:simpleType>

      </xs:attribute>

      <xs:attribute name="isquickfindfields" type="xs:boolean" />

    </xs:complexType>

  </xs:element>

  <!--

 

attribute elements - used for selecting attributes from the

 surrounding entity / link-entity, these

 values are returned as part of the fetch

 

-->

  <!-- [XDR-XSD] "all-attributes" element  -->

  <xs:element name="all-attributes">

    <xs:complexType></xs:complexType>

  </xs:element>

  <!-- [XDR-XSD] "attribute" element  -->

  <xs:complexType name="FetchAttributeType">

    <xs:attribute name="name" use="required" type="xs:string"></xs:attribute>

    <xs:attribute name="build" type="build"></xs:attribute>

    <xs:attribute name="addedby" type="xs:string" />

    <xs:attribute name="alias" type="xs:string"></xs:attribute>

    <xs:attribute name="aggregate" type="AggregateType"></xs:attribute>

    <xs:attribute name="groupby" type="FetchBoolType"></xs:attribute>

    <xs:attribute name="dategrouping" type="DateGroupingType"></xs:attribute>

    <xs:attribute name="usertimezone" type="FetchBoolType"></xs:attribute>

  </xs:complexType>

  <!--

 

order element - used to specify a sort order

 

-->

  <!-- [XDR-XSD] "order" element  -->

  <xs:complexType name="FetchOrderType">

    <xs:choice minOccurs="0" maxOccurs="unbounded">

      <!-- -->

    </xs:choice>

    <!-- -->

    <xs:attribute name="attribute" type="xs:string"></xs:attribute>

    <xs:attribute name="alias" type="xs:string"></xs:attribute>

    <xs:attribute name="descending" default="false" type="xs:boolean"></xs:attribute>

  </xs:complexType>

  <!--

 

link-entity element - used for joining one entity to its "parent"

 

-->

  <!-- [XDR-XSD] "link-entity" element  -->

  <xs:complexType name="FetchLinkEntityType">

    <xs:choice minOccurs="0" maxOccurs="unbounded">

      <!-- -->

      <xs:element ref="all-attributes" minOccurs="0" />

      <xs:element name="attribute" type="FetchAttributeType" minOccurs="0" maxOccurs="unbounded" />

      <xs:element name="order" type="FetchOrderType" minOccurs="0" maxOccurs="1" />

      <xs:element ref="filter" minOccurs="0" />

      <xs:element name="link-entity" type="FetchLinkEntityType" />

    </xs:choice>

    <!-- -->

    <xs:attribute name="name" use="required" type="xs:string"></xs:attribute>

    <xs:attribute name="to" type="xs:string"></xs:attribute>

    <xs:attribute name="from" type="xs:string"></xs:attribute>

    <xs:attribute name="alias" type="xs:string"></xs:attribute>

    <xs:attribute name="link-type" type="xs:string"></xs:attribute>

    <xs:attribute name="visible" type="xs:boolean"></xs:attribute>

    <xs:attribute name="intersect" type="xs:boolean"></xs:attribute>

  </xs:complexType>

  <!--

 

entity element - used for specifying the root element for a fetch, only

 one root entity is allowed in a given fetch, all others

 are dependent on this entity and are marked as

 link-entity

 

-->

  <!-- [XDR-XSD] "entity" element  -->

  <xs:complexType name="FetchEntityType">

    <xs:choice minOccurs="0" maxOccurs="unbounded">

      <!-- -->

      <xs:element ref="all-attributes" minOccurs="0" />

      <xs:element name="attribute" type="FetchAttributeType" minOccurs="0" maxOccurs="unbounded" />

      <xs:element name="order" type="FetchOrderType" minOccurs="0" maxOccurs="unbounded" />

      <xs:element name="link-entity" type="FetchLinkEntityType" />

      <xs:element ref="filter" minOccurs="0" />

    </xs:choice>

    <!-- -->

    <xs:attribute name="name" use="required" type="xs:string"></xs:attribute>

  </xs:complexType>

  <!--

 

fetch element - root element for the query

 

-->

  <!-- [XDR-XSD] "fetch" element  -->

  <xs:element name="fetch" type="FetchType"/>

  <xs:complexType name="FetchType">

    <xs:choice minOccurs="0" maxOccurs="unbounded">

      <!-- -->

      <xs:element name="entity" type="FetchEntityType" />

      <!-- This is for the Reports view only -->

      <xs:element name="order" type="FetchOrderType" minOccurs="1" maxOccurs="1" />

    </xs:choice>

    <!-- -->

    <xs:attribute name="version"/>

    <xs:attribute name="count" type="xs:integer"/>

    <xs:attribute name="page" type="xs:integer"/>

    <xs:attribute name="paging-cookie" type="xs:string"/>

    <xs:attribute name="utc-offset" type="IntOrEmpty"/>

    <xs:attribute name="aggregate" type="xs:boolean"/>

    <xs:attribute name="distinct" type="xs:boolean"/>

    <xs:attribute name="top" type="xs:integer"/>

    <xs:attribute name="mapping">

      <xs:simpleType>

        <xs:restriction base="xs:NMTOKEN">

          <xs:enumeration value="internal" />

          <xs:enumeration value="logical" />

        </xs:restriction>

      </xs:simpleType>

    </xs:attribute>

    <xs:attribute name="min-active-row-version" type="xs:boolean" use="optional" default="false"/>

    <xs:attribute name="output-format">

      <xs:simpleType>

        <xs:restriction base="xs:NMTOKEN">

          <xs:enumeration value="xml-ado" />

          <xs:enumeration value="xml-auto" />

          <xs:enumeration value="xml-elements" />

          <xs:enumeration value="xml-raw" />

          <xs:enumeration value="xml-platform" />

        </xs:restriction>

      </xs:simpleType>

    </xs:attribute>

    <xs:attribute name="returntotalrecordcount" type="xs:boolean" use="optional" default="