1. Ana Sayfa
  2. Dynamics CRM
  3. Dynamics CRM 2015 Kodlama Mimarisi – Bölüm 1 – Veri Sorgulama Yöntemleri

Dynamics CRM 2015 Kodlama Mimarisi – Bölüm 1 – Veri Sorgulama Yöntemleri

Bu noktada 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;


Resim-1

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


Resim-2

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.


Resim-3

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.


Resim-4


Resim-5

Sorguyu Çalıştırma

Bu  sorguyu çalıştıracak metod ise RetrieveMultiple metodudur ve servisi örneklediğimizde karşımıza çı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 sütunları 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”), newColumnSet(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 ettiğim entity’i vermekte. Bu noktadan sonra foreach ile bütün kayıtlar içinde dolaşabilir ve Attributes ile alanlara ulaşabilirim.

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


Resim-6

İ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 innerjoin 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(newFetchExpression(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.


Resim-7

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

Bu konuyla ilgili sorularınızı https://forum.mshowto.org linkini kullanarak ulaşacağınız forum sayfamızda sorabilirsiniz.

Referanslar

https://www.mshowto.org

Yorum Yap

Yazar Hakkında

Barış Kanlıca firmalara CRM ve yazılım konusunda danışmanlık yapmaktadır ve yazılım eğitimleri vermektedir. Dynamics CRM alanında yaptığı çalışmalarından dolayı Microsoft kendisine bu alanda sadece 55 kişide olan MVP(Most Valuable Professional) unvanını ve ödülünü vermiştir. Barış Kanlıca birçok üniversite ve seminerde konuşmacı olmaktadır. Kendisi INETA topluluğunun bir konuşmacısıdır. Ayrıca www.yazgelistir.com, www.yazilimgunlugu.com ve www.nedirtv.com sitelerinde bölüm editörüdür.Uzunca bir süredir Londra'da yaşayan Barış Kanlıca ve kurucusu olduğu Mawens Business Solution'da danışmanlık hizmeti vermeye devam etmektedir.

Yorum Yap