Dynamics CRM 2015 Kodlama Mimarisi v1.0

Transkript

Dynamics CRM 2015 Kodlama Mimarisi v1.0
Dynamics CRM 2015
Kodlama Mimarisi v1.0 Baris KANLICA
Dynamics CRM 2015 Kodlama Mimarisi v1.0 - Baris KANLICA
Icerik
Yazar Hakkinda ........................................................................................................................................ 3
E-Kitap Hakkinda ..................................................................................................................................... 3
Dynamics CRM Servis Mimarisi ............................................................................................................... 4
Device ID bilgisini vermek istemezsek ................................................................................................. 4
Device ID bilgisini vermek istersek ...................................................................................................... 5
Ek Bilgi ................................................................................................................................................. 5
Organization Servis .................................................................................................................................. 5
Execute Metodu .................................................................................................................................. 7
Early ve Late Binding Arasındaki Farklar.................................................................................................. 8
CrmSvcUtil.exe ile Early-Bound Sınıflar Oluşturmak ......................................................................... 11
Veri Turleri Hakkinda ............................................................................................................................. 14
OptionSetValue ................................................................................................................................. 15
EntityReference ................................................................................................................................. 15
Null Değer Atama .............................................................................................................................. 15
Veri Sorgulama ...................................................................................................................................... 15
FetchXML ............................................................................................................................................... 17
Sorguyu Hazırlama............................................................................................................................. 17
Sorguyu Çalıştırma............................................................................................................................. 20
Sorgu Sonucu ..................................................................................................................................... 22
İleri Seviye Sorgular ........................................................................................................................... 22
FetchXML ile Aggregate(Toplama Sorguları) İşlemleri .......................................................................... 30
Create/Update/Delete Metodları.......................................................................................................... 33
Dynamics CRM 2015 Plug-in Mimarisi................................................................................................... 39
Plug-in Execution Context ................................................................................................................. 40
Organization Servise Erisme .............................................................................................................. 41
Notification Servise Erisme................................................................................................................ 41
Input ve Output Parametreleri .......................................................................................................... 41
Pre ve Post Entity Imajlari ................................................................................................................. 42
Plug-in’ler Icerisindeki Hatalari Yakalamak ........................................................................................... 43
Plug-in Yapici Metodlari ........................................................................................................................ 45
Hadi Plug-in Yazalim .............................................................................................................................. 47
Veritabanina gitmeden kayitlari değiştirmek .................................................................................... 47
Update aninda update edilmemiş değerlere ulaşmak ...................................................................... 49
Uzerinde calistigim nesnenin id’si nerede? ....................................................................................... 52
Plug-in’ler arasinda bilgi paylasimi .................................................................................................... 52
1|Page
Dynamics CRM 2015 Kodlama Mimarisi v1.0 - Baris KANLICA
Plug-in Registration Tool’u Kullanmak .................................................................................................. 54
Plug-in’i Debug Etmek ........................................................................................................................... 60
Servis’lere Attach olarak Debug Etme ............................................................................................... 60
Plug-in Profiller’i kullanarak Debug etme.......................................................................................... 62
2|Page
Dynamics CRM 2015 Kodlama Mimarisi v1.0 - Baris KANLICA
Yazar Hakkinda
Yaklasik 15 yildir profesyonel olarak yazilim
geliştirmekteyim. Bundan yaklasik 10 yil once
Microsft Dynamics CRM 1.2 versiyonu ile
tanistiktan sonra CRM yazilim danismani olarak
yillardir Microsoft Dynamics CRM projeleri
içerisinde cesitli pozisyonlarda görev aldim.
Turkiye’de CRM alaninda danismanlik yapan
Omerd Business Solutions’in kurucu ortagi
olarak yillardir Yazilim Departman Mudur’u
unvanıyla bu sirkette görev yapmaktayım.
2 senedir de bu sirketin yurtdisi uzantisi olarak
kurulan Mawens Business Solution’da Genel
Mudur’u sifatiyla Londra’da yasamaktayim ve
CRM projeleri geliştirmeye burada devam
etmekteyim.
Bugune kadar 100’den fazla firmaya 150’den fazla CRM projesi geliştirdim ve bu alanda yaptigim
calismalardan dolayi Microsoft’tan 5 kere Dynamics CRM MVP odulunu ve unvanini aldim.
Bilkom-Apple tarafından da Dijital Yasam Kocu unvanini almis bulunmaktayım.
Turkiye’de Edirne’den Elazig’a kadar ve yurtdisinda Londra’da birçok universite, kurum ve etkinlikte
konusmaci/egitmen olarak bulundum.
Bircok sitede de editör ve yazar olarak yazilarim yayinlanmaktadir.
Bana ulaşmak icin asagidaki kişisel bloğum olan www.cub-e.net ‘i takip edebilir ve [email protected]
adresine mail atabilirsiniz.
E-Kitap Hakkinda
Bu kitap Dynamics CRM alaninda yazilim yapan/yapmak isteyen herkese aciktir. Kodlar c# dilinde
Visual Studio’da yazilmis ve bunlar uzerinden anlatilmaktadir. Bu kitaptaki ornekleri yapabilmeniz icin
1. CRM 2015
2. CRM 2015 SDK
3. Visual Studio 2012-2013
Araclarina ihtiyaciniz bulunmaktadir.
Bu e-kitap www.cub-e.net ‘teki yazilarin biraraya toplanmis halidir.
Ilereyen zamanlarda diger yazilarimi da 2015 versiyonuna guncelleyerek daha genis bir e-kitap
olusturmayi amaclamaktayim.
Umarim hepinize faydali olur.
3|Page
Dynamics CRM 2015 Kodlama Mimarisi v1.0 - Baris KANLICA
Dynamics CRM Servis Mimarisi
Dynamics CRM üzerinde uygulama geliştirebileceğiniz çok güçlü bir mimari ile gelmektedir.
Bu mimarinin temel yapı taşı ise WCF servisleridir. Bu yazımda sizlere CRM'in temel web
servisleri olan Discovery ve Organization Servislerinin yapısını ve ne işe yaradıklarını
anlatacağım.
CRM için kod geliştirirken her ne kadar sdk'nın içinden çıkan dll'leri kullansak da bu dll'ler
vasıtasıyla ilk önce servis bağlantısı oluşturmamız gerekmektedir. Yani verilere ulaşmak ve
veri yazmak için WCF servislerini kullanmak zorundayız. Bu servislerle daha hızlı ve güvenli
bir şekilde CRM platformuna entegre olmamızı sağlamaktadır.
WCF standartlaşmış bir teknoloji olduğu için biz yazılım geliştiricilere yeni özellikler
sunmakta ve sürekli kendi içerisinde gelişmektedir. Dynamics CRM yapısında entity ve
attribute katmanlı bir mimari bulunmaktadır. Yani biz CRM'in Business Logic katmanına
müdahele edip yazılımlarımızı onunla entegre hale getirmekteyiz. Temel olarak nesne
katmanı Entity isimli nesneden türemiştir. late-bound türlerde bu nesneyi sıklıkla
kullanmaktayız. Early-bound olarak program geliştirme metodolojisini tercih edersek Entity
class'ı ile işimiz olmamaktadır ama arka planda bütün nesnelerin bu class'dan türediği
unutulmamalıdır. Bu konuya aşağıda bir örnekle tekrar değineceğim. CRM servislerine
OrganizationServiceProxy ve DiscoveryServiceProxy sınıflarıyla bağlanmaktayız. Aşağıdaki
kod bize bunu göstermektedir;
using (OrganizationServiceProxy _serviceProxy =
new OrganizationServiceProxy(organizationUri, homeRealmUri, userCredentials,
deviceCredentials)) ;
Discovery Servis
Dynamics CRM 4.0 versiyonundan itibaren multi-tenant bir mimaridedir. Yani tek bir
uygulama birden fazla organizasyonu içerisinde barındırmaktadır. Yazılımcı olarak birden
fazla organizasyon arasında geçiş yapmamız gerekiyorsa sistemde hangi organizyonlar
olduğunu sorgulama işini Discovery servis ile yapmamız gerekmektedir. Bu servis yazma
okuma işlemlerinin yapılacağı ana servise bağlanmamıza yardımcı olacak ve
organizasyonların bilgisini bize döndürecektir. Aşağıdaki kod bize bu servise nasıl
bağlanacağımızı ve organizasyonların bilgisine nasıl ulaşacağımızı göstermektedir.
Device ID bilgisini vermek istemezsek
Uri dInfo = new Uri("http://192.168.5.102/XRMServices/2011/Discovery.svc");
ClientCredentials clientcred = new ClientCredentials();
DiscoveryServiceProxy dsp = new DiscoveryServiceProxy(dInfo, null, clientcred, null);
dsp.Authenticate();
RetrieveOrganizationsRequest rosreq = new RetrieveOrganizationsRequest();
RetrieveOrganizationsResponse r = (RetrieveOrganizationsResponse)dsp.Execute(rosreq);
foreach (OrganizationDetail o in r.Details)
{
Console.WriteLine("Organizasyon Adı : " + o.FriendlyName);
}
Console.ReadLine();
4|Page
Dynamics CRM 2015 Kodlama Mimarisi v1.0 - Baris KANLICA
Device ID bilgisini vermek istersek
Normalde dokümantasyonlarda bu sekilde bir ayrim göremezsiniz. Ben bunu deneyimlerime
dayanarak yapıyorum. Aslinda yukarıdaki kod da Online Organizasyon’a baglanir ama
DiscoveryService’in varsayilan yapici metoduna 4. Parametreyi gönderirseniz yani
DeviceCredential null olmazsa calisir.Sorun ise DeviceCredential’i oluşturmak icin CRM
SDK\SampleCode\CS\HelperCode klasöründeki DeviceIdManager.cs kodunu kendi
kodunuza implemente etmeniz gerekmekte.
Ek Bilgi
Bu kodla discovery servise bağlandıktan sonra ClientCredentials'ı kullanarak hali hazırda
oturum açmış kullancının bilgisini sisteme göndermekteyiz. Ama her zaman bunu kullanmak
istemeyebiliriz yani başka bir kullanıcı adı ve şifre ile sisteme girmek istediğimizde
ClientCredentials'a kullanıcı adı, şifre bilgilerini verebiliriz.
clientcred.Windows.ClientCredential = new System.Net.NetworkCredential("kullanici
adi", "sifre", "domain");
Yukarıdaki kodun çalışabilmesi için
System.ServiceModel.Description,System.Runtime.Seriliazation, Microsoft.Crm.Sdk.Proxy
ve Microsoft.Xrm.Sdk dll'lerinin referanslara eklenmiş olduğundan emin olun. Ayrıca
uygulamayı .Net Framework 4.5.2'de derlemek gerekiyor.
Organization Servis
Dynamics CRM 2015'in ana web servisi Organization Servis'tir. Bu servis içerisinde entity
instance'ları üzerinde işlem yapabileceğimiz 6 metod ve execute metodu bulunmaktadır. CRM
Servis sınıfını oluşturabilmek için Organization Service Proxy'nin bir instance'ını
5|Page
Dynamics CRM 2015 Kodlama Mimarisi v1.0 - Baris KANLICA
oluşturmamız gerekmekte. Daha sonrasında ise Organization Servis sınıfını bunu göre
ayarlayacağız.
Uri organizationUri = new
Uri("https:// !organizasyon crm url!
/XRMServices/2011/Organization.svc");
ClientCredentials clientcred = new ClientCredentials();
clientcred.UserName.UserName = " kulllanici adi ";
cientcred.UserName.Password = " sifre ";
OrganizationServiceProxy _serviceproxy = new OrganizationServiceProxy(organizationUri,
null, clientcred, null);
IOrganizationService _service = (IOrganizationService)_serviceproxy;Yukarıdaki
kodun
çalışabilmesi için System.ServiceModel.Description, Microsoft.Xrm.Sdk ve
Microsoft.Xrm.Sdk.Proxy dll'lerinin referanslara eklenmiş olduğundan emin olun. Ayrıca
uygulamayı .Net Framework 4.5.2'de derlemek gerekiyor. Oluşturduğumuz bu servisi daha
sonra programımızın ilerleyen bölümlerinde çağırıp işlemler yapacağız.
Tabi burada belirtilmesi gereken bir konu da işlem yapmak istediğiniz nesnenin üzerinde
yetkilerinizin olmasıdır. Bir kayıt oluşturabilmek için oluşturma yetkisine sahip olmanız
gerekmektedir. Sadece okuma yetkisiyle bir kayıt oluşturamazsınız. Bir sonraki yazımda
sizlere entity mimarisi ve yazma okuma işlemlerini anlatacağım.
Yukaridaki kod orneginde biz bir kullanici adi ve sifre ekledik. Eger her kullanicinin kendi
kullanici haklariyla sistem üzerinde işlem yapmasini istiyorsaniz yukarıdaki kodda kullanici
adi ve sifre kodunu silip asagidaki kodu eklemeniz gerekir.
ClientCredentials clientcred = new ClientCredentials();
Boylece sistemde oturum acmis kullanici bilgileri ile devam etmis olacagiz.
Bilmemiz gereken diğer bir konu da eger Early Bind veri tipi kullanacaksak servisi
oluştururken ek bir komut daha girmemiz gerekmektedir. Yani linq ile sorgulama ve
generated code üzerinde işlemler yapabilmek icin oluşturduğunuz servis nesnesine bu sekilde
işlem yapacaginizi belirtmeniz lazim iste bunun icin asagidaki satiri eklememiz gerekmekte.
_serviceproxy.EnableProxyTypes();
Bu arada CRM servisi olusturmanin yeni bir yöntemi daha bulunmakta. Bu da
CrmConnection nesnesini kullanmak. Bu nesneye erişebilmek icin Microsoft.Xrm.Client
dll’ini referans olarak eklemeniz gerekmekte.
CrmConnection crmConnection = CrmConnection.Parse("Url=https://! Crm organizasyon url
!/XRMServices/2011/Organization.svc; Username= kullanici adi; Password= sifre");
OrganizationService service = new OrganizationService(crmConnection);
Boylece servis oluşturma işlemini 2 satirda da halledebilmekteyiz. Tabii bu bilgileri ben bu
sekilde kod içerisine koymus gibi oldum size gostemek icin ama projelerde tabii ki bunlari ya
app.config, web.config içerisine ya da belli bir lokasyonda tuttuğumuz xml dosyasi içerisinde
tutuyoruz ki bu bilgiler degistiginde sürekli kod degisikligi yapmak zorunda kalmayalım.
6|Page
Dynamics CRM 2015 Kodlama Mimarisi v1.0 - Baris KANLICA
Execute Metodu
CRM Servisi içerisindeki Execute Metodu Request ve Response yani Talep ve Yanit seklinde
calismaktadir. Sistem üzerinde yapacaginiz butun hareketleri bu sekilde yapabilirsiniz ki buna
ayri metodlari bulunan Create, Update, Delete bile dahildir.
Konu aslinda basit olduğundan çokça açıklanacak bir tarafi da yok gibi ama calisma yapisina
bakacak olursak siz bir talepte bulunursunuz o da size yanit verir diye kisaca özetleyebiliriz.
Execute metodunun alabileceği Request’lere makalenin sonunda yer vereceğim. Ama
oncelikle sunu da belirteyim ki Request nesnesi opsiyonel parametrelere de sahiptir. Bu
parametreler sunlardir.
Parameter
SolutionUniqueName
Description
Messages
AddPrivilegesRoleRequest
CreateRequest
Islemin yapilacagi DeleteRequest
Cozumun Adi
MakeAvailableToOrganizationTemplateRequest
Eslenen kayitlar
SuppressDuplicateDetection bulunsun mu
bulunmasin mi
UpdateRequest
CreateRequest
UpdateRequest
Kullanima dair ornek kod ise su sekilde;
Account target = new Account();
target.Name = "Fabrikam";
CreateRequest req = new CreateRequest();
req.Target = target;
req["SuppressDuplicateDetection"] = true;
req["SolutionUniqueName"] = "MySolutionName";
CreateResponse response = (CreateResponse)_service.Execute(req);
Eger isterseniz Execute Metodunu asenkron olarka da calistirabilirsiniz. Bu ekranda
donmalari ve kullanicilarin ekrandan işlem bitmeden cikmalarini önleyecektir. Bunun için
yapmaniz gereken ExecuteAsyncRequest mesajini geçmek olacaktır.
ExecuteMultipleRequest ile de toplu daha aktarimlarinda kullanabileceğiniz mesajdir.
7|Page
Dynamics CRM 2015 Kodlama Mimarisi v1.0 - Baris KANLICA
xRM Mesajlarinin tamamina bu adresten ulaşabilirsiniz : https://msdn.microsoft.com/enus/library/gg334698.aspx
CRM mesajlarina da bu adresten ulaşabilirsiniz : https://msdn.microsoft.com/enus/library/gg309482.aspx
Early ve Late Binding Arasındaki Farklar
Dynamics CRM 2011 ve sonrasinda CRM içerisinde programcılar kendilerine birden fazla
programlama biçimi seçebilirler. Dynamics CRM’de WSDL kullanarak early-bound tipleri ve
Dynamics Entity sınıflarını kullanarak da late-bound programlama yapabilirsiniz. Plug-in ve
Workflow yazabilmek için late-bound tipleri kullanmamız gerekmektedir.
Dynamics CRM 4.0’da entity sınıflarının duruşu aşağıdaki şekildeki gibiydi;
Şimdi ise Dynamics CRM 2011’de bu yapı şu şekilde değişti;
DynamicEntity sınıfı artık Entity sınıfı isimli bir sınıfla yer değiştirdi. Bunun anlamı ise build
time yani early bound tiplerle, runtime yani late bound tiplerin artık tek sınıftan türemesinin
gerçekleşmiş olduğudur.
Dynamics CRM’de artık WSDL’e direkt ulaşamıyoruz. Daha önceki makalemde de
anlattığım gibi 2 tane dll’i referans olarak eklemek ve servise bağlantı kurmak gerekmektedir.
8|Page
Dynamics CRM 2015 Kodlama Mimarisi v1.0 - Baris KANLICA
Late-Bound olarak isimlendiren mimaride siz Entity sınıfından bir nesne türetmeli ve bu
nesnenin attribute collection’da da değerlere yer vermelisiniz. Tabii burada değer alanlarının
crm içerisindeki logical name’lerini kesinlikle bilmeniz gerekmektedir.
Aşağıdaki örnek bize late-bindig yapısının nasıl işlediğini gösterecektir;
Entity lead = new Entity("lead");
lead.Attributes["subject"] = "Deneme Firmasi";
lead.Attributes["firstname"] = "Baris";
lead.Attributes["lastname"] = "KANLICA";
lead.Attributes["companyname"] = "Mawens Business Solutions";
lead.Attributes["numberofemployees"] = 20;
Guid leadid = service.Create(lead);
Entity sınıfını kullanmaya başladığımız zaman late-binding’e giriş yapmış oluyoruz. Entity
türünden oluşturduğumuz nesneye biz Lead türünden bilgileri içine dolduracağız diyoruz.
Contact, Opportunity gibi CRM içerisindeki bütün nesneleri bu şekilde oluşturabiliriz.
Daha sonra ise alanların logical name’lerini vererek bunların değerleri veriyoruz. Burada .net
tabanlı türleri kullanabilmekteyiz. CRM 4.0’da CrmBoolean, CrmNumber gibi türlere
çeviriler yaparak nesnelerin içini doldururken artık buna ihtiyacımız bulunmamaktadır.
service isimli WCF tabanlı servisimizden türemiş nesnenin Create Metodunu kullanarak
nesnemizi CRM içerisinde bir kayıt olarak oluşturuyoruz.
9|Page
Dynamics CRM 2015 Kodlama Mimarisi v1.0 - Baris KANLICA
Early-Bound olarak isimlendirilen mimaride de ise CrmSvcUtil.exe aracını kullanarak bir
OrganizationServiceContext türetmelisiniz. Daha sonra ise bu aracın türettiği nesneleri
kullanabilir hale gelebilirsiniz. Tabii burada her bir nesnenin şema isimleriyle hareket
ettiğimiz unutulmamalıdır.
Aşağıdaki örnek bize early-binding yapısının nasıl işlediğini gösterecektir;
CrmDataContext orgContext = new CrmDataContext(ServiseBaglan());
var contact = new Contact()
{
FirstName = "Alan",
LastName = "Smith"
};
orgContext.AddObject(contact);
orgContext.SaveChanges();
Burada ServiseBaglan() daha önceki makelemde belirttiğim gibi IOrganizationService
Interface’inden türemiş bir sınıfı teşkil etmektedir. Yani CRM’in WCF servisine hazır ve
açık bir bağlantıdır.
Contact ise CRM içerisinde kişiler entity’sidir, Account, Lead, Invoice,Quote gibi daha bir
çok entity bulunmaktadır.
Firstname ve Lastname ise kişinin adı ve soyadı için değerleri temsil etmektedir ve bu bilgi
OrganizationContext’ten gelmektedir. Bir sonraki yazında CrmDataContext isimli bu
OrganizationContext’in CrmSvcUtil.exe aracılığıyla nasıl oluşturulduğu göstereceğim.
Daha sonra ise bu doldurduğumuz contact nesnesini OrganizationContext’e teslim ediyoruz
ve işlem yapılması için SaveChanges() metodumuzu çağırıyoruz.
10 | P a g e
Dynamics CRM 2015 Kodlama Mimarisi v1.0 - Baris KANLICA
Farklılıkları ise şu şekilde sıralayabiliriz;




Early-Bound Entity Sınıflarının en büyük avantajı compile time da bize bütün hataları
göstermesidir. Yani uygulamamızı yazarken ya da derlerken türler arası uyumsuzluk
ya da yanlış değer atama gibi bütün yanlışlıklarımızı gözler önüne sermektedir.
CrmSvcUtil bize tüm CRM mimarisini örneklerken bütün nesneleri ve onun ilişkilerini
de getirmektedir. Böylece nesne dönüşümleri ve tür güvenliği de sağlanmış
olmaktadır.
Visual Studio içerisinde inteli sense özelliğini kullanmamızı sağlar.
Dynamics CRM 4.0’dan beri WSDL ile çalışan kişiler bu yeni yapıda hiçbir farklılık
hissetmeyeceklerdir.
CrmSvcUtil.exe ile Early-Bound Sınıflar Oluşturmak
Early-Bound tiplerle çalışabilmek için obje modelini bilmeye ihtiyacımız vardır. İşte
CrmSvcUtil.exe bize bu kodu üretecek olan programdır.
Program early-bound .Net Framework sınıflarını ve entity modellerini Microsoft Dynamics
CRM 2015 içerisinden almakta ve bize bir .cs dosyası halinde vermektedir. Bu noktadan
sonra üretilen bu .cs dosyasını ya Visual Studio ile kodunuzun bir parçası olarak kullanabilir
ya da bir dll haline getirip projenize referans olarak ekleyebilirsiniz. Bu sayede Visual Studio
içerisinde intelli-sense özelliği ile kod geliştirebilirsiniz.
Eğer isterseniz uygulama her bir entity için ayrı bir partial class’da oluşturabilir.
CRM’in SDK’sında bin klasörü içerisinde bulabileceğiniz bu aracı command prompt ile
çalıştırabilirsiniz.
crmsvcutil.exe
/url: buraya Organization Service’in adresi gelecek
/out: çıktının hangi dosyaya olacağı bilgisi
/username : servise bağlanılacak kullanıcı adı
/şifre : servise bağlanılacak şifre
Eğer CRM’de zaten kullanıcı olan bir kişi ile oturum açtıysanız k.adı ve şifre
belirtmenize gerek yok.
11 | P a g e
Dynamics CRM 2015 Kodlama Mimarisi v1.0 - Baris KANLICA
Bu şekilde gerekli cümleyi command promt’a yazdığınızda yukarıdaki ekranda olduğu gibi
.cs dosyanızı alabilirsiniz.
Ek olarak aşağıdaki parametreleri de verebilirsiniz;
/serviceContextName: Eğer .cs dosyanızı LINQ Service Context vasıtasıyla LINQ
sorgularını da desteklemesini istiyorsanız bu özelliği kullanmalısınız. Buraya türetilecek
servis context’inin adını girmelisiniz örneğin “CrmDataContext” gibi.
Burada ek olarak şunu da belirtmeliyim ki bu komutu varsayalında ben hep kullanıyorum. Bu
komutu kullandığımız zaman 2 şeyi unutmamız gerekmekte;
1. Bir OrganizationContext oluşmakta artık onu kullanmamız gerekmekte, aşağıdaki
örnekte olduğu gibi;
CrmDataContext orgContext = new CrmDataContext(ServiseBaglan());
2. Crm Servis çağrısına şu özelliği eklememiz gerekmekte;
_serviceproxy.EnableProxyTypes();
Bu iki maddenin detayını LINQ ile veri sorgulama makalemde daha detaylı anlatacağım.
/namespace : Varsayılanda .cs dosyası bir namespace olmadan türetilir bu özelliği
kullanarak kodu bir namespace altında toplayabilirsiniz.
/language : CrmSvcUtil.exe varsayılanda C# kodu üretil eğer VB kodu üretmek
istiyorsanız bu özelliğe VB değerini vermeniz gerekmekte.
Bu tool aşağıdaki örnekte olduğu gibi bir kod üretmektedir;
/// <summary>
/// Bir müşteriyi veya potansiyel müşteriyi temsil eden işletme. Ticari işlemlerde
faturalanan şirket.
/// </summary>
[System.Runtime.Serialization.DataContractAttribute()]
[Microsoft.Xrm.Sdk.Client.EntityLogicalNameAttribute("account")]
[System.CodeDom.Compiler.GeneratedCodeAttribute("CrmSvcUtil", "5.0.9688.1244")]
public partial class Account : Microsoft.Xrm.Sdk.Entity,
System.ComponentModel.INotifyPropertyChanging,
System.ComponentModel.INotifyPropertyChanged
{
12 | P a g e
Dynamics CRM 2015 Kodlama Mimarisi v1.0 - Baris KANLICA
/// <summary>
/// Default Constructor.
/// </summary>
public Account() :
base(EntityLogicalName)
{
}
public const string EntityLogicalName = "account";
public const int EntityTypeCode = 1;
public event System.ComponentModel.PropertyChangedEventHandler
PropertyChanged;
public event System.ComponentModel.PropertyChangingEventHandler
PropertyChanging;
private void OnPropertyChanged(string propertyName)
{
if ((this.PropertyChanged != null))
{
this.PropertyChanged(this, new
System.ComponentModel.PropertyChangedEventArgs(propertyName));
}
}
13 | P a g e
Dynamics CRM 2015 Kodlama Mimarisi v1.0 - Baris KANLICA
Veri Turleri Hakkinda
Microsoft Dynamics CRM 2011 ve Microsoft Dynamics CRM Online'da programlama
modeli .NET'in temel türlerini kullanacak şekilde değiştirildi.
Bu tabloda beni en çok şaşırtan ise Customer, Lookup, Owner nesnelerinin artık
EntityReference türünden sadece bir değer almaları. CRM'i yeni öğrenler için işler gerçekten
kolaylaştırılmış. Artık kod yazarken CRM ile başlayan nesnelerimiz yok.
Aşağıdaki tablo bize Microsoft Dynamics CRM 4.0 ile CRM 2011, 2013 ve 2015 arasındaki
tür dönüşümünü göstermektedir.
Özellik Adı
AttributeTypeCode.Boolean
Microsoft Dynamics CRM 2011,
2013, 2015 Türü
bool ya da System.Boolean
Microsoft Dynamics
CRM 4.0 Türü
CrmBoolean
AttributeType.CalendarRules EntityCollection
DynamicEntity[] or
calendarrule[]
AttributeType.Customer
EntityReference
Customer
AttributeType.DateTime
System.DateTime
CrmDateTime
AttributeType.Decimal
decimal ya da System.Decimal
CrmDecimal
AttributeType.Double
double ya da System.Double
CrmFloat
AttributeType.Integer
int ya da System.Integer
CrmNumber
System.Object
AttributeType.Internal
Kayıtlarda Kullanılmaz.
Kayıtlarda Kullanılmaz
AttributeType.Lookup
EntityReference
Lookup
AttributeType.Memo
string ya da System.String
System.String
AttributeType.Money
Money
CrmMoney
AttributeType.Owner
EntityReference
Owner
AttributeType.PartyList
EntityCollection or ActivityParty[]
activityparty[] or
DynamicEntity []
AttributeType.Picklist
OptionSetValue
Picklist
AttributeType.PrimaryKey
System.Guid
Key
AttributeType.String
System.String
System.String
AttributeType.State
OptionSetValue yada oluşturulan
enumeration kullanılmalı
EntityNameStateInfo
AttributeType.Status
OptionSetValue ya da int
Status
AttributeType.Uniqueidentifi
er
System.Guid
UniqueIdentifier
System.Object
AttributeType.Virtual
Kayıtlarda Kullanılmaz
Kayıtlarda Kullanılmaz
Eski Tür
Yeni Tür
CrmAttributeType Class
(MetadataService)
Microsoft.Xrm.Sdk.Metadata.AttributeTypeCo
de
Moniker Class (CrmService)
Microsoft.Xrm.Sdk.EntityReference
SecurityPrincipal Class (CrmService)
Microsoft.Xrm.Sdk.EntityReference
14 | P a g e
Dynamics CRM 2015 Kodlama Mimarisi v1.0 - Baris KANLICA
OptionSetValue
OptionSetValue’a değer atamak için ilk önce OptionSetValue türünden bir nesne
oluşturmanız gerekmektedir. Burada dikkat çekmek istediğim konu ise eğer state alanı ile
çalışacaksanız (yani firma için aktif/pasif, teklif için açık/kazanıldı/kaybedildi gibi) earlybound sınıflarda bunlar için mutlaka bir enumaration oluşturulmakta. Ama late bound
sınıflarda bu durumu programcı yönetmektedir.
Örnek olarak adres üzerindeki bir optionset alana değer atama aşağıdaki şekilde olmaktadır;
OptionSetValue osv = new OptionSetValue(1);
contact.Attributes["address1_freighttermscode"] = osv;
EntityReference
CRM sisteminde iki entity’yi birbirine bağlamak için lookup nesnesini kullanmak zorundayız.
Lookup’lar üzerinde programatik işlem yapabilmek için EntityReference nesnesini
kullanmaktayız. Bu nesneye Lookup alana reference vermek istemiz nesnenin türü ve Id’sini
vermemiz gerekmektedir.
Aşağıdaki örnekte parentAccountId atama yapılacak nesnenin guid cinsinden Id’si olmalı;
EntityReference parentaccountid = new EntityReference("account", parentAccountId));
accountEntity.Attributes["parentaccountid"] = parentaccountid;
ioService.Update(accountEntity);
Null Değer Atama
CRM 4.0’dan farklı olarak .Net Type türleri kullanıldığı için null değer atama işlemi artık
sadece alana değer vermekten ibaret oldu. İşte birkaç örnek;
EntityAdi.IndustryCode = null;
EntityAdi.AccountId = Guid.Empty;
EntityAdi.AccountNumber ="";
EntityAdi.Address1_Country = String.Empty;
Veri Sorgulama
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.
15 | P a g e
Dynamics CRM 2015 Kodlama Mimarisi v1.0 - Baris KANLICA
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
QueryExpression'ın bütün özelliklerine destek verdiği gibi matematiksel
işlemler ve gruplamaya da destek verir. Sorgular XML standartlarına göre
FetchXML
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
outer joins yapabilme
X
X
X
X
X
X
X
Çalışma zamanında
doğrulama
X
X
İlişkisiz nesneleri bağlama
(Union)
Matematiksel İşlemleri
X
destekleme
(Sınırlı)
16 | P a g e
X
X
X
X
X
Dynamics CRM 2015 Kodlama Mimarisi v1.0 - Baris KANLICA
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'>
17 | P a g e
Dynamics CRM 2015 Kodlama Mimarisi v1.0 - Baris KANLICA
<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
18 | P a g e
Dynamics CRM 2015 Kodlama Mimarisi v1.0 - Baris KANLICA
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
19 | P a g e
Dynamics CRM 2015 Kodlama Mimarisi v1.0 - Baris KANLICA
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-BA8B78E7D1623F9D"), newColumnSet(new string [] { "fullname", "companyname" }));
foreach (var item in slead.Attributes)
20 | P a g e
Dynamics CRM 2015 Kodlama Mimarisi v1.0 - Baris KANLICA
{
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();
21 | P a g e
Dynamics CRM 2015 Kodlama Mimarisi v1.0 - Baris KANLICA
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>
22 | P a g e
Dynamics CRM 2015 Kodlama Mimarisi v1.0 - Baris KANLICA
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>
<linkentity name='systemuser' from='systemuserid' to='createdby' visible='false' linktype='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.
23 | P a g e
Dynamics CRM 2015 Kodlama Mimarisi v1.0 - Baris KANLICA
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 a
ccount 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' />
24 | P a g e
Dynamics CRM 2015 Kodlama Mimarisi v1.0 - Baris KANLICA
</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.
25 | P a g e
Dynamics CRM 2015 Kodlama Mimarisi v1.0 - Baris KANLICA
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">
<!-- -->
26 | P a g e
Dynamics CRM 2015 Kodlama Mimarisi v1.0 - Baris KANLICA
<!-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" />
27 | P a g e
Dynamics CRM 2015 Kodlama Mimarisi v1.0 - Baris KANLICA
<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
28 | P a g e
Dynamics CRM 2015 Kodlama Mimarisi v1.0 - Baris KANLICA
-->
<!-- [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"
29 | P a g e
Dynamics CRM 2015 Kodlama Mimarisi v1.0 - Baris KANLICA
-->
<!-- [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
FetchXML ile Aggregate(Toplama Sorguları) İşlemleri
Bir önceki bolumde FetchXML ile neler yapabileceğimize değindik. 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.
30 | P a g e
Dynamics CRM 2015 Kodlama Mimarisi v1.0 - Baris KANLICA
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.
31 | P a g e
Dynamics CRM 2015 Kodlama Mimarisi v1.0 - Baris KANLICA
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>
32 | P a g e
Dynamics CRM 2015 Kodlama Mimarisi v1.0 - Baris KANLICA
</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;
Create/Update/Delete Metodları
CRM servis nesnesi bize kayıtlar üzerinde işlem yapma özelliği sağlamaktadır. Servis içinden
yapmak istediğimiz harekete uygun metodu çağırmamız gerekmektedir. Tabii burada
unutulmaması gereken konu servisi çağıran kullanıcının çağrılan metodda işlem yapmaya
yetkili olması gerekmektedir.
Create Metodu CRM 2015 içerisinde bir entity içerisinde yeni bir nesne oluşturmamıza
olanak tanır. Metod parametre olarak entity türünden bir nesne alır ve yeni oluşturulmuş
nesnenin GUID türünden değerini geri döndürür.
Aşağıdaki örnek bu metodun late-bound sınıfla kullanımını göstermektedir.
// Entity nesnesinin yeni bir instance’ini olusturuyoruz
Entity account = new Entity("account");
33 | P a g e
Dynamics CRM 2015 Kodlama Mimarisi v1.0 - Baris KANLICA
// Gerekli attribute’lara atama yapiyoruz.
account["name"] = "Örnek Firma";
// Örnek Firma adında bir firma karti olusturuyoruz.
_accountId = ioService.Create(account);
Aşağıdaki örnek bu metodun early-bound sınıfla kullanımını göstermektedir.
Contact contact = new Contact()
{
FirstName="Deneme",
LastName="Kisisi",
Address1_City="İstanbul",
};
Guid contactGuid =_service.Create(contact);
Update Metodu CRM 2011 içerisinde bir entity içerisinde bir nesneyi güncellememizi sağlar.
Metod parametre olarak entity türünden bir nesne alır. Güncellenecek nesnenin id’si mutlaka
parametre olarak verilmelidir.
Guid gContact = new Guid("7bE545CCD3-9A3A-E011-BA8B-78E7D1623F9D");
Contact contact = new Contact()
{
ContactId = gContact,
FirstName="Test",
LastName="Kisisi",
Address1_City="Ankara",
};
_service.Update(contact);
Delete Metodu CRM 2011 içerisinde Id’sini verdiğiniz bir nesneyi sistemden silmeye yarar.
Metod parametre olarak silinecek nesnenin Id’si yanında bu nesnenin türünü ister.
Guid gContact = "7bE545CCD3-9A3A-E011-BA8B-78E7D1623F9D";
_service.delete("contact", gContact);
Bir programcı olarak çok standart olan bu işlemleri isterseniz bir class mantığı altında
birleştirelim. Bu sayede daha yönetilebilir bir CRUD (Create, Read, Update, Delete) yapısı
oluşturabiliriz. Ben bu sınıf için Process adını kullandım ve Process sınıfı içerisinde şu anda
Oluşturma, Güncelleme ve Silme işlerimi yapmaktayım. İlerleyen makalelerde Okuma
yapılarını incelerken onları da bu sınıfa dâhil ederiz.
Burada öncelikle açıklamalıyım ki bir önceki makalede yer alan Singleton tasarım deseniyle
CRM servisini oluşturma yazımdaki class’tan faydalanarak servisi çağırma işlemini
gerçekleştirdim. O yazıyı okumak isterseniz buraya tıklayınız
Ek olarak hata olaylarını kontrol etmek için Result isimli bir class kullandım bu class’ın
içeriği şu şekilde
public class Result
{
public string Message { get; set; }
public bool isError { get; set; }
public Object BusinessObject { get; set; }
public Result(string _Message, bool _isError, Object _BusinessObject)
{
34 | P a g e
Dynamics CRM 2015 Kodlama Mimarisi v1.0 - Baris KANLICA
Message = _Message;
isError = _isError;
BusinessObject = _BusinessObject;
}
public Result(string _Message, bool _isError)
{
Message = _Message;
isError = _isError;
BusinessObject = null;
}
}
Görüldüğü üzere çok basit bir class bize sonucun başarılı mı başarısız mı olduğunu
döndürecek o kadar.
Bunun haricinde bir de Base isimli bir class’ım var ki Process sınıfını aslında bu class’dan
türetmekteyim. Şu anda bu class’ı sadece ErrorNumber ve ErrorDetail gibi hata oluştuğunda
bilgi almamızı sağlayacak iki property ile kullanmaktayım ama ileride farklı propertyler de
eklenecek.
public class Base
{
public int ErrorCode { get; set; }
public string ErrorDetail { get; set; }
}
Bu iki class’ımı açıkladıktan sonra asıl yapıda kullanacağımız metodlara gelelim. İlk önce
create metodunu inceleyelim. Hatırlayacağınız üzere CRM servisinde Create metdonun
Update metodundan tek farkı id’ye ihtiyaç duymamasıydı. Çünkü bu metod id’yi üretip bize
geri döndürecek. Ben de kurguyu buna göre planladım.
public Result Create(Entity EntityForCreate)
{
try
{
Service service = Service.GetService();
if (service == null)
throw new Exception("Service is null");
if (service != null && service.ErrorCode > 0)
throw new Exception(service.ErrorDetail);
Guid EntityID = service.OrganizationService.Create(EntityForCreate);
DetailedLog.CreateLog("Entity Created! Type: " +
EntityForCreate.LogicalName + ", ID : " + EntityID,
System.Diagnostics.EventLogEntryType.Information);
return new Result("", false, EntityID);
}
catch (Exception ex)
{
ErrorCode = 100;
ErrorDetail = ExceptionHandler.HandleException(ex);
DetailedLog.CreateLog(System.Reflection.Assembly.GetExecutingAssembly().GetName().Name
35 | P a g e
Dynamics CRM 2015 Kodlama Mimarisi v1.0 - Baris KANLICA
+ " : " +
System.Reflection.MethodBase.GetCurrentMethod().ToString()
+ " : " + ErrorDetail,
System.Diagnostics.EventLogEntryType.Error);
return new Result(ErrorDetail, true);
}
}
Aslında yapı çok basit servis nesnemi çağırıyorum, servis oluşurken hata var mı diye
bakıyorum eğer hata varsa hatayı fırlatıyorum. (bu noktada şunu belirtmeliyim ki catch
bölümünde yer alan DetailLog ve ExceptionHandler benim daha önceden yazmış olduğum
hata yönetimi ile ilgili class’lar catch bölümünü siz de istediğiniz gibi düzenleyebilirsiniz)
Eğer hata yok ise OrganizationService metodumu kullanarak nesnemi oluşturuyorum. Bu
noktada da bir hata yoksa servis bana oluşturduğu nesnenin id’sini döndürüyor yok eğer hata
varsa zaten kod catch bloğuna düşüyor. İşte bu kadar.
Şimdi gelin diğer metodlara bir göz atalım;
public Result Update(Entity EntityForUpdate, Guid EntityID)
{
try
{
Service service = Service.GetService();
if (service == null)
throw new Exception("Service is null");
if (service != null && service.ErrorCode > 0)
throw new Exception(service.ErrorDetail);
service.OrganizationService.Update(EntityForUpdate);
DetailedLog.CreateLog("Entity Updated! Type: " +
EntityForUpdate.LogicalName + ", ID : " + EntityID,
System.Diagnostics.EventLogEntryType.Information);
return new Result("", false);
}
catch (Exception ex)
{
ErrorCode = 100;
ErrorDetail = ExceptionHandler.HandleException(ex);
DetailedLog.CreateLog(System.Reflection.Assembly.GetExecutingAssembly().GetName().Name
+ " : " +
System.Reflection.MethodBase.GetCurrentMethod().ToString()
+ " : " + ErrorDetail,
System.Diagnostics.EventLogEntryType.Error);
return new Result(ErrorDetail, true);
}
}
Update metodu neredeyse Create metodunun aynısı tek fark daha önce de ifade ettiğim gibi
güncellenecek nesnenin id’sini alması. Bunu almalı ki neyi güncellediğini bilebilsin. Aslında
burada şöyle bir mantıksal kargaşa var update metodu ek olarak id’yi almamakta zaten sizin
ona update edilmesi için vereceğiniz entity’nin Id alanına vermelisiniz. Benim burada ek
olarak almamdaki amaç onu loglamak için.
36 | P a g e
Dynamics CRM 2015 Kodlama Mimarisi v1.0 - Baris KANLICA
Bir de Delete metoduna göz atalım;
public Result Delete(string EntityName, Guid EntityID)
{
try
{
Service service = Service.GetService();
if (service == null)
throw new Exception("Service is null");
if (service != null && service.ErrorCode > 0)
throw new Exception(service.ErrorDetail);
service.OrganizationService.Delete(EntityName, EntityID);
DetailedLog.CreateLog("Entity Deleted! Type: " + EntityName + ", ID :
" + EntityID,
System.Diagnostics.EventLogEntryType.Information);
return new Result("", false);
}
catch (Exception ex)
{
ErrorCode = 100;
ErrorDetail = ExceptionHandler.HandleException(ex);
DetailedLog.CreateLog(System.Reflection.Assembly.GetExecutingAssembly().GetName().Name
+ " : " +
System.Reflection.MethodBase.GetCurrentMethod().ToString()
+ " : " + ErrorDetail,
System.Diagnostics.EventLogEntryType.Error);
return new Result(ErrorDetail, true, null);
}
}
Delete metodu entity’nin adını ve id’sini almakta. Bu iki veriyi de servise vermekteyiz.
Class’ın tamamına bakacak olursak;
public class Process : Base
{
public Result Create(Entity EntityForCreate)
{
try
{
Service service = Service.GetService();
if (service == null)
throw new Exception("Service is null");
if (service != null && service.ErrorCode > 0)
throw new Exception(service.ErrorDetail);
Guid EntityID = service.OrganizationService.Create(EntityForCreate);
DetailedLog.CreateLog("Entity Created! Type: " +
EntityForCreate.LogicalName + ", ID : " + EntityID,
System.Diagnostics.EventLogEntryType.Information);
return new Result("", false, EntityID);
}
catch (Exception ex)
37 | P a g e
Dynamics CRM 2015 Kodlama Mimarisi v1.0 - Baris KANLICA
{
ErrorCode = 100;
ErrorDetail = ExceptionHandler.HandleException(ex);
DetailedLog.CreateLog(System.Reflection.Assembly.GetExecutingAssembly().GetName().Name
+ " : " +
System.Reflection.MethodBase.GetCurrentMethod().ToString()
+ " : " + ErrorDetail,
System.Diagnostics.EventLogEntryType.Error);
return new Result(ErrorDetail, true);
}
}
public Result Update(Entity EntityForUpdate, Guid EntityID)
{
try
{
Service service = Service.GetService();
if (service == null)
throw new Exception("Service is null");
if (service != null && service.ErrorCode > 0)
throw new Exception(service.ErrorDetail);
service.OrganizationService.Update(EntityForUpdate);
DetailedLog.CreateLog("Entity Updated! Type: " +
EntityForUpdate.LogicalName + ", ID : " + EntityID,
System.Diagnostics.EventLogEntryType.Information);
return new Result("", false);
}
catch (Exception ex)
{
ErrorCode = 100;
ErrorDetail = ExceptionHandler.HandleException(ex);
DetailedLog.CreateLog(System.Reflection.Assembly.GetExecutingAssembly().GetName().Name
+ " : " +
System.Reflection.MethodBase.GetCurrentMethod().ToString()
+ " : " + ErrorDetail,
System.Diagnostics.EventLogEntryType.Error);
return new Result(ErrorDetail, true);
}
}
public Result Delete(string EntityName, Guid EntityID)
{
try
{
Service service = Service.GetService();
if (service == null)
throw new Exception("Service is null");
if (service != null && service.ErrorCode > 0)
throw new Exception(service.ErrorDetail);
service.OrganizationService.Delete(EntityName, EntityID);
38 | P a g e
Dynamics CRM 2015 Kodlama Mimarisi v1.0 - Baris KANLICA
DetailedLog.CreateLog("Entity Deleted! Type: " + EntityName + ", ID :
" + EntityID,
System.Diagnostics.EventLogEntryType.Information);
return new Result("", false);
}
catch (Exception ex)
{
ErrorCode = 100;
ErrorDetail = ExceptionHandler.HandleException(ex);
DetailedLog.CreateLog(System.Reflection.Assembly.GetExecutingAssembly().GetName().Name
+ " : " +
System.Reflection.MethodBase.GetCurrentMethod().ToString()
+ " : " + ErrorDetail,
System.Diagnostics.EventLogEntryType.Error);
return new Result(ErrorDetail, true, null);
}
}
}
Class’ımızı bu şekilde oluşturduktan sonra gelin onu bir konsol uygulaması yardımıyla çağıralım ve iş
başında görelim.
static void Main(string[] args)
{
try
{
Entity lead = new Entity("lead");
lead.Attributes["subject"] = "Fuardan Gelenler";
lead.Attributes["firstname"] = "Barış";
lead.Attributes["lastname"] = "KANLICA";
lead.Attributes["companyname"] = "Omerd Business Solutions";
Process process = new Process();
Result result = process.Create(lead);
if (result.isError)
throw new Exception(result.Message);
Console.WriteLine("Lead created : " +
result.BusinessObject.ToString());
Console.ReadLine();
}
catch (Exception ex)
{
Console.WriteLine("Error : " + ex.Message);
Console.ReadLine();
}
}
Gördüğünüz üzere Entity nesnemi çağıyor ve ona lead adını veriyorum. İçerisi istediğim
bilgiler ile dolduruyor ve Process class’ımın içindeki Create metodu vasıtasıyla CRM
içerisinde oluşmasını sağlıyorum. İşte hepsi bu kadar :)
Dynamics CRM 2015 Plug-in Mimarisi
Sizlere bugun Dynamics CRM icerisindeki Plug-in Mimarisinden soz etmek isitiyorum. Plug-in’ler
IPlugin arayuzunden turetilmis kod parcaciklaridir ve CRM’in icerisinde belli bir sira icerisinde
calisirlar. Kabaca tariff edersek bunlar birer .dll dosyalaridir ve CRM’e bu dosyaya ne zaman bakmasi
39 | P a g e
Dynamics CRM 2015 Kodlama Mimarisi v1.0 - Baris KANLICA
gerektigini biz soyleriz. Plug-in’ler olay bazli olarak calisirlar. Yani herhangi bir kayit olusturuldugunda,
guncellendiginde, silindiginde vb.. olaylar oldukca biz ilgili ayari yapmissak calisirlar.
Plug-in’lerin en güzel yani pre ve post olarak calismaya ayarlanabilmeleridir. Plug-in’ler olay bazli
calisirlar demiştim iste bu olay olmadan önceki kaydin son hali üzerinden ve olay olduktan sonraki
hali üzerinden işlem yapmaniza olanak sağlarlar.
Plug-in’lerin calismasi icin Microsoft.Xrm.Sdk.dll ve Microsoft.Crm.Sdk.Proxy.dll dosyalarinin
referanslara eklenmis olmasi gerekmektedir. Tam yeri gelmisken bahsedeyim eger siz ucuncu parti
bir .dll kullaniyorsaniz (yani kendi yazdiginiz siniflarin oldugu ya da diger uygulamalardan aldiginiz) bu
.dll’lerin ilgili serverin assembly klasöründe olduğundan emin olun yoksa plug-in calismayabilir.
public class MyPlugin: IPlugin
{
public void Execute(IServiceProvider serviceProvider)
{}
}
Detaya inecek olursak IPlugin arayuzunden turerilmis bir sinif içerisinde Execute metodu yer
almalidir. Bu metod parametre olarak IServiceProvider arayuzunden türetilmiş bir bilgi yiginini içerir.
Yani CRM kod içerisinde yapacagimiz işlemlerde bize CRM içerisinde olan olaylardan bize bilgi tasir ki
biz de bu bilgileri kodun içerisinde kullanalim. Ne gibi veriler içinde tasimakta derseniz cok fazla detay
verebilirim mesela su anda hangi kullanicinin işlem yaptigi, tasidigi nesnenin turunu, eger pre-plugin
ise değerlerin değişmeden önceki halini vb… bir cok veri içermekte.
Simdi sirasiyla gelen veri yiginlarini inceleyelim.
Plug-in Execution Context
Calisma zamaninda oluşan veriler bu yapi içerisinde yer almaktadır. Bunlara kodun calisma hiyerarşisi
ve entity bilgileri de dahildir.
IPluginExecutionContext context = (IPluginExecutionContext)
serviceProvider.GetService(typeof(IPluginExecutionContext));
Bir olay olduğunda kayit edilmiş bir plug-in’e bu veriler aktarılır aslinda o anda calisan butun plugin’lere bu veriler aktarılır ama execution pipeline denen sralamaya uyarak aktarılır once pre sonra
post pluginlere veri aktarılır. Hatta siz pre-plugin ile bir veriyi değiştirirseniz post-plugin’e o veri
aktarılır.
Tabii burada yeri gelmişken bahsedeyim burada sozu edilen kodlarin sonsuz döngüye girmemeleri
için sistem içerisinde Depth denen bir anahtar yer almaktadır. Varsayilanda bu bir plugin’i arda arda 8
kere calistirir ve durdurur. Boylece sistemin bir kod yanlisigi ile çökmesi engellenmiştir. Bu değer
değiştirebilir bir değerdir.
Sistemin calismasi da aslinda su mantiga dayanmakta;
40 | P a g e
Dynamics CRM 2015 Kodlama Mimarisi v1.0 - Baris KANLICA
Yani Event Execution Pipeline’a bir mesaj girdiginde Pre-Event -> Platform Islemleri (Yani CRM’in
kendi ic isleyisi) -> Post-Event seklinde islenmekte. Bu dongu senkron ve asenkron yapilar için böyle
ilerlemekte.
Organization Servise Erisme
CRM içerisinde işlem yapabilmek her zaman bir servis nesnesine ihtiyaç duymaktayız. Iste kullanicinin
hareketi neticesinde acilmiş bu servisi bize kullanmamiz için geçirmekte serviceProvider nesnesi.
IOrganizationServiceFactory serviceFactory =
(IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOr
ganizationServiceFactory));
IOrganizationService service =
serviceFactory.CreateOrganizationService(context.UserId);
Notification Servise Erisme
Senkron olarak işaretlenmiş plug-in’ler Microsoft Azure Service Bus’a veri mesaj gondebilirler.
IServiceEndpointNotificationService turunden olan bu bilgi Azure Service Bus’a gönderilir. Bu sayede
Azure Service Bus içerisinde endpoint oluşturulmuş ve endpoint’i dinleyen 3. Parti bir servis ile
iletişime geçebilmektedir.
Input ve Output Parametreleri
InputParameters nesnesi su anda yapilan hareketin yani su anda tetiklenmiş olayin bilgisini ve su
anda üzerinde işlem görülen entity’nin bilgisini içerir. Bu bilgiye erişmek için “Target” nesnesine
41 | P a g e
Dynamics CRM 2015 Kodlama Mimarisi v1.0 - Baris KANLICA
bakmamiz gerekmektedir ve bu nesneyi alip Entity class’ina çevirebiliriz. Input nesneleri Request
message yapisindadir.
if (context.InputParameters.Contains("Target") &&
context.InputParameters["Target"] is Entity)
{
// Obtain the target entity from the input parameters.
Entity entity = (Entity)context.InputParameters["Target"];
}
Fakat unutmaniz gereken bir nokta var her mesaj Entity nesnesini içermeyebilir. Ornegin
DeleteRequest; Entity değil EntityReference dondurur.
if (context.InputParameters.Contains("Target") &&
context.InputParameters["Target"] is EntityReference)
{
// Obtain the target entity from the input parameters.
EntityReference entity =
(EntityReference)context.InputParameters["Target"];
}
Benzer sekilde OutputParameters da Response message içerir. Ama sunu unutmayin ki senkron postevent ve asenkron plug-in’ler OutputParameters turunden nesneler içerirler.
Pre ve Post Entity Imajlari
Bu konuyu okurken sakin Ingilizce Images kelimesinin resim anlamiyla karistirmayin buradaki anlami
verinin o anki goruntusu seklinde ifade etmek daha doğru olur. Aslinda tam Ingilizce tabiriyle
snapshot. PreEntityImages ve PostEntityImages verileri sistem tarafından size gönderilir ama siz
ozellikle beklediğiniz alanlari plug-in’in kayit işlemi sirasinda sisteme soyleyebilrsiniz.
Burada tabii ki bir mantik çerçevesi olduğunu da unutmayin Create aninda bir nesnenin preImage’i
olamayacagi gibi Delete işleminden sonra da bir postImage beklemeyin.
Simdi bu bilgileri verdikten sonra butun bunlari birleştirerek bir plug-in temel goruntusune bakalim.
Aciklamalar kodun içinde.
using System;
using System.ServiceModel;
using Microsoft.Xrm.Sdk;
public class MyPlugin: IPlugin
{
public void Execute(IServiceProvider serviceProvider)
{
// Sandbox içerisinde calisan plug-in’ler TracingService’den
yararlanabilirler.
ITracingService tracingService =
(ITracingService)serviceProvider.GetService(typeof(ITracingService));
//Context’i elde ediyoruz.
IPluginExecutionContext context = (IPluginExecutionContext)
serviceProvider.GetService(typeof(IPluginExecutionContext));
// InputParameters’dan gelen verileri aliyoruz
42 | P a g e
Dynamics CRM 2015 Kodlama Mimarisi v1.0 - Baris KANLICA
if (context.InputParameters.Contains("Target") &&
context.InputParameters["Target"] is Entity)
{
// Target ile entity’e erisiyoruz.
Entity entity = (Entity)context.InputParameters["Target"];
// Beklediginiz entity geldi mi diye kontrol ediyoruz.
if (entity.LogicalName != "account")
return;
// CRM Servisi elde ediyoruz
IOrganizationServiceFactory serviceFactory =
(IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFac
tory));
IOrganizationService service =
serviceFactory.CreateOrganizationService(context.UserId);
try
{
/ Iste buradan sonrasi size kalmis istediğiniz kodu yazabilirsiniz.
}
catch (FaultException<OrganizationServiceFault> ex)
{
throw new InvalidPluginExecutionException("An error occurred in
MyPlug-in.", ex);
}
catch (Exception ex)
{
tracingService.Trace("MyPlugin: {0}", ex.ToString());
throw;
}
}
}
}
Plug-in’ler Icerisindeki Hatalari Yakalamak
Senkron calisan plug-in’ler sandbox’da olsun ya da olmasin herhangi bir hata ile karsilastiklarinda
geriye kullaniciya uyari gösterecek bir yapiya sahiptirler ve bu durumu Dynamics CRM yönetir. Yani
siz sadece hatayi geriye dondurursunuz.
Asenkron calisan yapilar için CRM içerisinde System Job(AsyncOperation) isimli bir bolum yer
almaktadır. Iste asenkron hatalari da buradan takip edebilirsiniz.
Senkron calisan plug-in’lerde ise hata mesajlarini InvalidPluginException turunden bir hata
göndererek kontrol edebilirsiniz. Message ozelligine herhangi bir değer gönderirseniz sistem onu
gösterir aksi takdirde varsayilan hata mesaji görüntülenir.
Ayrica sunu da belirteyim Sandbox içinde calismayan plug-in’ler için hata mesajlari sistemin calistigi
serverdaki Olay Goruntuleyici içerisinde Uygulama hatalari bolumu içerisine de kaydedilir.
Plug-in içerisinde uygun gordugunuz yerde su sekilde hata fırlatabilirsiniz:
throw new InvalidPluginExecutionException("The account number can only be set by the
system.");
43 | P a g e
Dynamics CRM 2015 Kodlama Mimarisi v1.0 - Baris KANLICA
Bir plug-in hata firlattiginda CRM su sekilde bir uyari vermektedir.
Log dosyasini incelediğimizde de detaylari almaktayız. Bizim gönderdiğimiz mesaja dikkat edin lütfen;
Unhandled Exception:
System.ServiceModel.FaultException`1[[Microsoft.Xrm.Sdk.OrganizationServiceFault,
Microsoft.Xrm.Sdk, Version=7.0.0.0, Culture=neutral,
PublicKeyToken=31bf3856ad364e35]]: The account number can only be set by the system.
Detail: <OrganizationServiceFault
xmlns="http://schemas.microsoft.com/xrm/2011/Contracts"
xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<ErrorCode>-2147220970</ErrorCode>
<ErrorDetails
xmlns:a="http://schemas.datacontract.org/2004/07/System.Collections.Generic">
<KeyValuePairOfstringanyType>
<a:key>CallStack</a:key>
<a:value i:type="b:string" xmlns:b="http://www.w3.org/2001/XMLSchema">
at Microsoft.Crm.Sdk.Samples.AccountNumberPlugin.Execute(IServiceProvider
serviceProvider)
at PluginProfiler.Library.PluginAppDomainProxy.ExecuteCore(Stopwatch watch,
ProfilerExecutionReport report, Object instance, Object executionParameter)
at
PluginProfiler.Library.AppDomainProxy.Execute(ProfilerExecutionConfiguration
configuration, ProfilerExecutionReport report)
</a:value>
</KeyValuePairOfstringanyType>
</ErrorDetails>
<Message>The account number can only be set by the system.</Message>
<Timestamp>2015-04-08T15:29:50.7437667Z</Timestamp>
<InnerFault i:nil="true" />
<TraceText i:nil="true" />
</OrganizationServiceFault>
Ama siz temel bir hata yönetim sinifina sahip olmak ve yazdiginiz butun kodlarda kullanmak isterseniz
su sekilde bir Exception mimarisini yazdiginiz kodda kullanabilirsiniz. Fakat burada unutmamaniz
gereken nokta siz hata fırlatmaz hatalari kendiniz Handle ederseniz CRM kullaniciya hata mesaji
göndermeyecektir. Bunun için InvalidPluginException’i siz firlatmalisiniz.
Asagidaki kodu Plug-in’ler içerisinde kullanmanizi pek tavsiye etmem cunku sistem gayet detayli bir
geri bildirim yapmakta ama bir hata aliyor ve isin içinden cikamiyorsaniz bu kodu denemenizde fayda
olabilir. Hatayi serverda ya da CRM içinde bir yerlere yazdırıp incelebilrisiniz. Yine uygun gordugunuz
bir yerde kullaniciya hata göstermek istiyorsaniz “InvalidPluginExecutionException” firlatmayi
unutmayin.
catch (FaultException<Microsoft.Xrm.Sdk.OrganizationServiceFault> ex)
44 | P a g e
Dynamics CRM 2015 Kodlama Mimarisi v1.0 - Baris KANLICA
{
Console.WriteLine("The application terminated with an
error.");
Console.WriteLine("Timestamp: {0}", ex.Detail.Timestamp);
Console.WriteLine("Code: {0}", ex.Detail.ErrorCode);
Console.WriteLine("Message: {0}", ex.Detail.Message);
Console.WriteLine("Inner Fault: {0}",
null == ex.Detail.InnerFault ? "No Inner Fault" : "Has
Inner Fault");
}
catch (System.TimeoutException ex)
{
Console.WriteLine("The application terminated with an
error.");
Console.WriteLine("Message: {0}", ex.Message);
Console.WriteLine("Stack Trace: {0}", ex.StackTrace);
Console.WriteLine("Inner Fault: {0}",
null == ex.InnerException.Message ? "No Inner Fault" :
ex.InnerException.Message);
}
catch (System.Exception ex)
{
Console.WriteLine("The application terminated with an
error.");
Console.WriteLine(ex.Message);
// Display the details of the inner exception.
if (ex.InnerException != null)
{
Console.WriteLine(ex.InnerException.Message);
FaultException<Microsoft.Xrm.Sdk.OrganizationServiceFault>
fe = ex.InnerException
as
FaultException<Microsoft.Xrm.Sdk.OrganizationServiceFault>;
if (fe != null)
{
Console.WriteLine("Timestamp: {0}",
fe.Detail.Timestamp);
Console.WriteLine("Code: {0}", fe.Detail.ErrorCode);
Console.WriteLine("Message: {0}", fe.Detail.Message);
Console.WriteLine("Trace: {0}", fe.Detail.TraceText);
Console.WriteLine("Inner Fault: {0}",
null == fe.Detail.InnerFault ? "No Inner Fault" :
"Has Inner Fault");
}
}
}
Plug-in Yapici Metodlari
Bir plug-in için Microsoft Dynamics CRM’de opsiyonel olarak kullanabileceğiniz yapici
metod(constructor) türleri mevcuttur. Hic parametre vermeden yapici metod cagirabileceginiz gibi
bir ya da iki parametre vererek de cagirabilirsiniz.
SamplePlugin isimli plug-in için 3 cesit yapici metod ornegi asagidaki gibidir.
public SamplePlugin()
public SamplePlugin(string unsecure)
45 | P a g e
Dynamics CRM 2015 Kodlama Mimarisi v1.0 - Baris KANLICA
public SamplePlugin(string unsecure, string secure)
Yapici metodun ilk parametresi public yani unsecure bilgi yigini içermelidir. Ikinci parametre ise nonpublic (secure) bilgi yigini içermelidir. Buradan da anlayabileceğiniz uzere secure string encrypted
yani sifrelenmis veri unsecure ise unencrypted yani sifrelenmemis değer içermelidir. Office Outlook
client da calisan bir plug-in yazdiysaniz bilmelisiniz ki secure string offline yani cevrimdisi modda
calismayacaktir.
Bu bilgileri bir plug-in’e Plugin Registration Tool vasitasiyla bir step’in kaydi sirasinda sisteme
iletiyoruz. Bu mesajlar için ayrilmis 2 alan bulanmaktadır.
Step içerisinde bu ayarlamalari yaptıktan sonra yazmis olduğumuz degerlere kod içerisinden asagidaki
gibi ulaşabilirsiniz.
private readonly string _unsecureString;
private readonly string _secureString;
public AdvancedPlugin(string unsecureString, string secureString)
{
if (String.IsNullOrWhiteSpace(unsecureString) ||
String.IsNullOrWhiteSpace(secureString))
{
throw new InvalidOperationException
("Unsecure and secure strings are required by the Advanced Plug-in, but not
provided.");
}
_unsecureString = unsecureString;
_secureString = secureString;
}
46 | P a g e
Dynamics CRM 2015 Kodlama Mimarisi v1.0 - Baris KANLICA
Hadi Plug-in Yazalim
Plug-in’ler hakkında temel bilgileri öğrendiğimize gore artik plug-in yazabiliriz. Asagida vereceğim
orneklerde bir plug-in içerisinde yapabileceğiniz temel işlemleri anlatmaya calisacagim. Bu kodlara
CRM SDK\SampleCode\CS\Plug-ins içerisinden ulaşabilirsiniz.
Veritabanina gitmeden kayitlari değiştirmek
Daha once de ifade ettiğim gibi CRM içerisinde bir kayit veritabanina gitmeden Pre-Operation(PreEvent) adiminda kaydettiğiniz bir plug-in ile kullanicinin oluşturmak istediği kayda ulaşabilirsiniz.
Asagidaki kod oluşturulan bir account(firma) nesnesinin içerisine eger yok ise bir numara oluşturarak
bunu accountnumber(müşteri numarasi) alanina vermekte böylece kayitta olmayan bir alan
veritabanina bu alan eklenmiş bir sekilde gidecek.
Kodda da görebileceğiniz uzere ilk once Execute metodumuzu oluşturuyoruz. Biliyorsunuz ki bu
metod parametre olarak içine aldigi ServiceProvider ile bize ihtiyacimiz olan butun verileri sunacak.
Ilk once Context’i ServiceProvider’dan türetiyoruz. Daha sonra da Context içerisindeki Target’in bir
Entity mi olup olmadigina bakıyoruz. Tam bu noktada gelin Context nesnesinin içerisine bir bakalim.
Asagidaki ekran goruntusunu bu plug-in’i debug ettiğim anda aldim. Ilerleyen bölümlerde bir plugin’in nasil debug edileceğini anlatacagim. Simdilik Context’e odaklanalim.
Gorebileceginiz uzere Context UserId, BusinessUnitId, MessageName, PrimaryEntityName,
CreatedOn gibi o anda isimize yarayacak birçok veri yiginini içermekte. Iste Plug-in içerisinde
ihtiyacimiz olanlari buradan alip kullanacagiz.
47 | P a g e
Dynamics CRM 2015 Kodlama Mimarisi v1.0 - Baris KANLICA
Entity ise onu entity sinifindan bir nesne haline getiriyoruz.
Bu sefer bu oluşturduğumuz yeni entity nesnesi account turunden bir nesne midir diye bakıyoruz.
Daha sonra Icinde accountnumber diye bir alan var mi diye bakıyoruz. Yoksa iste tam bu noktada
veritabanina doğru yolculuğa cikmis olan kullanicinin bu kaydına müdahale edip içerisine bizim
urettigimiz numara ile accountnumber nesnesini doldurarak entity mize veriyoruz. Artik içerisinde
accountnumber alani da var.
/// <summary>
/// A plug-in that auto generates an account number when an
/// account is created.
/// </summary>
/// <remarks>Register this plug-in on the Create message, account entity,
/// and pre-operation stage.
/// </remarks>
//<snippetAccountNumberPlugin2>
public void Execute(IServiceProvider serviceProvider)
{
// Obtain the execution context from the service provider.
Microsoft.Xrm.Sdk.IPluginExecutionContext context =
(Microsoft.Xrm.Sdk.IPluginExecutionContext)
serviceProvider.GetService(typeof(Microsoft.Xrm.Sdk.IPluginExecutionContext));
48 | P a g e
Dynamics CRM 2015 Kodlama Mimarisi v1.0 - Baris KANLICA
// The InputParameters collection contains all the data passed in the
message request.
if (context.InputParameters.Contains("Target") &&
context.InputParameters["Target"] is Entity)
{
// Obtain the target entity from the input parameters.
Entity entity = (Entity)context.InputParameters["Target"];
//</snippetAccountNumberPlugin2>
// Verify that the target entity represents an account.
// If not, this plug-in was not registered correctly.
if (entity.LogicalName == "account")
{
// An accountnumber attribute should not already exist because
// it is system generated.
if (entity.Attributes.Contains("accountnumber") ==
false)
{
// Create a new accountnumber attribute, set its value, and
add
// the attribute to the entity's attribute collection.
Random rndgen = new Random();
entity.Attributes.Add("accountnumber",
rndgen.Next().ToString());
}
else
{
// Throw an error, because account numbers
must be system generated.
// Throwing an InvalidPluginExecutionException will cause the
error message
// to be displayed in a dialog of the Web application.
throw new InvalidPluginExecutionException("The account number
can only be set by the system.");
}
}
}
}
Umarim birsey dikkatinizi çekmiştir. Entity içerisine direkt alani Attributes.Add metodu ile ekliyoruz.
Yani bir update işlemi yapmıyoruz zaten kayit daha veritabanina gitmedi doğal olarak CRM Service
nesnesinin bir instance’ini olusturmamiza da gerek kalmadi.
Update aninda update edilmemiş değerlere ulaşmak
Baslik biraz karisik gelebilir ama aslinda tam olarak da durum bu update aninda update edilmemiş
alanlara ulaşmak istiyorsaniz birazdan bahsedecegim yöntemi uygulamniz gerekmekte. Peki biz neye
neden ulasamiyoruz diye soracak olursaniz aciklayayim. Dynamics CRM’in Create aninda kayit ile ilgili
elde ettiği butun bilgileri bize Target’tan türettiğimiz entity içerisinde verir. Asagidaki ekran
goruntusunde de bu durumu görebilirsiniz.
49 | P a g e
Dynamics CRM 2015 Kodlama Mimarisi v1.0 - Baris KANLICA
Update aninda durum bundan farkli sistem bize sadece (doğal olarak) update edilmiş alanlari
vermektedir. Asagidaki ekran goruntusune bakabilirsiniz.
Ben bu contact üzerinde sadece jobtitle alanini güncelledim. Sistem jobtitle ve yaninda ihtiyaç
duyulacak birkaç bilgiyi daha Target’a vermekte o kadar.
Simdi konu basligina dönecek olursak iste tam bu update aninda ben update edilmemiş bir alanin
değerine ulaşmak istersem ne yaparim? Sistemde bunun için Image yani o kaydin o anki snapshot’ini
almamizi sağlayan bir ozellik var.
Herhangi bir plug-in step’i üzerinde sag tuşa basarak “create new image” seçeneğini seçtiğimizde
asagidaki ekran karsimiza gelecektir.
50 | P a g e
Dynamics CRM 2015 Kodlama Mimarisi v1.0 - Baris KANLICA
Bu ekranda Pre ve Post olarak istediğimiz alanlari parametre olarak seçebilir ve bunlara genel bir isim
verebiliriz. Ben Target dedim.
Iste bu ayarlamayi yapdiginizda asagidaki ekran goruntusundeki gibi update aninda değişmeyen ama
sizin erişmek istediğiniz alan/alanlar PreEntityImages içerisinde hazir olacaktır.
51 | P a g e
Dynamics CRM 2015 Kodlama Mimarisi v1.0 - Baris KANLICA
Hangi Message’da hangi image’lara ualasabileceginizin listesi asagidaki gibidir.
Mesaj
Create
Create
Update
Update
Delete
Delete
Stage
PRE
POST
PRE
POST
PRE
POST
Pre-Image
Hayir
Evet
Evet
Evet
Evet
Evet
Post-Image
Hayir
Evet
Hayir
Evet
Hayir
Hayir
Peki kod tarafında buna nasil ulasacagiz derseniz o da su sekilde olacak;
Once image nesnesine ulaşıyoruz:
Entity image = (Entity)context.PreEntityImages["Target"];
Sonra image içerisinden istediğimiz alana erişiyoruz:
String descriptionMessage = "Old full name: " + image["fullname"];
Uzerinde calistigim nesnenin id’si nerede?
Update edilmis ya da post-operation durumdaki bir nesnenin id’sine ihtiyaç duyarsaniz su sekilde
elde edebilirsiniz:
Guid id = new Guid(context.OutputParameters["id"].ToString());
Ya da
Guid id = context.PrimaryEntityId;
Plug-in’ler arasinda bilgi paylasimi
Eger bir plug-in içinde oluşturduğumuz bir veriyi diğer plug-in’lerin de erişmesini istiyorsak
“SharedVariables” yapisini kullanmamiz gerekmekte. SharedVarabiles aslinda bir parametre
kolleksiyonu ve içerisinde paylaşmak istediğiniz nesneleri saklayabilirsiniz.
Asagida 2 tane plug-in yer almakta. Ilk plug-in (PreEventPlugin)
context.SharedVariables.Add("PrimaryContact", (Object)contact.ToString());
52 | P a g e
Dynamics CRM 2015 Kodlama Mimarisi v1.0 - Baris KANLICA
Kodu ile Contact isimli Guid nesnesini “PrimaryContact” adinda SharedVariables içerisinde
saklamakta.
Ikinci plug-in ise Post-event aninda calismakta ve SharedVariables’den istediği değeri örnekteki gibi
almaktadır.
Boylece plug-inler arasi veri transferi ve yapilmis olmakta.
Guid contact =
new Guid((string)context.SharedVariables["PrimaryContact"]);
public class PreEventPlugin : IPlugin
{
public void Execute(IServiceProvider serviceProvider)
{
// Obtain the execution context from the service provider.
Microsoft.Xrm.Sdk.IPluginExecutionContext context =
(Microsoft.Xrm.Sdk.IPluginExecutionContext)
serviceProvider.GetService(typeof(Microsoft.Xrm.Sdk.IPluginExecutionContext));
// Create or retrieve some data that will be needed by the post event
// plug-in. You could run a query, create an entity, or perform a
calculation.
//In this sample, the data to be passed to the post plug-in is
// represented by a GUID.
Guid contact = new Guid("{74882D5C-381A-4863-A5B9-B8604615C2D0}");
// Pass the data to the post event plug-in in an execution context shared
// variable named PrimaryContact.
context.SharedVariables.Add("PrimaryContact", (Object)contact.ToString());
}
}
public class PostEventPlugin : IPlugin
{
public void Execute(IServiceProvider serviceProvider)
{
// Obtain the execution context from the service provider.
Microsoft.Xrm.Sdk.IPluginExecutionContext context =
(Microsoft.Xrm.Sdk.IPluginExecutionContext)
serviceProvider.GetService(typeof(Microsoft.Xrm.Sdk.IPluginExecutionContext));
// Obtain the contact from the execution context shared variables.
if (context.SharedVariables.Contains("PrimaryContact"))
{
Guid contact =
new Guid((string)context.SharedVariables["PrimaryContact"]);
// Do something with the contact.
}
}
}
53 | P a g e
Dynamics CRM 2015 Kodlama Mimarisi v1.0 - Baris KANLICA
Plug-in Registration Tool’u Kullanmak
Ilk plug-in’imizi yazdıktan sonra geldi onu CRM içerisine eklemeye. Bu işlem için Plugin Registration
Tool dediğimiz CRM SDK içerisinden cikan bir uygulamayi kullanacagiz. Bu uygulama sayesinde hem
plug-in hem de custom workflow’lari CRM içerisine ekleyebilmekteyiz.
SDK\Tools\PluginRegistration\PluginRegistration.exe yolu ile ulaşabileceğiniz uygulamayi
calistirdiginizda sizden bağlanmak istediğiniz server ile ilgili bilgileri isteyecektir.
Dynamics CRM Online için Online’i seçebilirsiniz ama unutmayin ki Office 365 hesabi kullaniyorsaniz
Office 365’i seçmeniz gerekmekte. Ikisi de Online ama yetki mekanizmaları farkli.
Eger On-Premises yani Microsoft disinda host edilen bir CRM’e erişmek istiyorsaniz o zaman OnPremises seçeneğini seçmeniz gerekmekte. IFD’ler için de bu seçeneği kullanabilirsiniz.
54 | P a g e
Dynamics CRM 2015 Kodlama Mimarisi v1.0 - Baris KANLICA
Eger “Always display list of available orgs” seçeneğiniz seçerseniz bağlanmak istediğiniz kullanici ile
erişebileceğiniz organizasyonlarin listesini görüntüleyebilirsiniz.
Basarili bir sekilde giriş yaptiginizda asagidaki gibi bir ekran karsiniza gelecektir.
1. Plug-in’i sisteme kayit edebilmek için yukarıdaki “Register” düğmesine tikliyoruz ve ardindan
“Register New Assembly” ‘ ye tikliyoruz.
55 | P a g e
Dynamics CRM 2015 Kodlama Mimarisi v1.0 - Baris KANLICA
2. Step#1 bolumundeki … düğmesine tikliyarak kayit ettirmek istediğimiz .dll’i seçiyoruz.
3. Step#2 bolumunde kaydetmek istediğimiz plug-in class’ini seçiyoruz.
4. Step#3 bolumunde 2 tane seçeneğimiz bulunmakta;
a. Sandbox : Bu seçeneği seçer isek plug-in bir Sandbox içerisinde calisacak yani dis
ortamdan izole edilecek. Boylece bu plugin sistem içerisinde calisacak ama sisteme
zarar veremeyecek ve izlenebilir olacak. Kisacasi yazdiginiz bir plug-in production
ortamina tasimadan once test etmek için bu senecegi kullaniyoruz.
b. None : hiçbir kisitlama olmadan .dll içerisindeki kodlar icra edilir.
5. Step#4 bolumunde ise plug-in nerede duracagini seçmemizi istemekte.
a. Database: tavsiye edilen yöntem budur. Boylece dll işletim sistemi kaynakli
sorunlardan izole edilir. Veritabani yedeklendikçe dll de içinde olduğundan
yedeklenecektir ve herhangi bir durumda geriye dönmenizi sağlar.
56 | P a g e
Dynamics CRM 2015 Kodlama Mimarisi v1.0 - Baris KANLICA
b. Disk: Sistemin varsayilan dll yerleştirme yeri olan CRM Kurulum
Yolu\Server\bin\assembly klasörü içerisinden dll’i okur. Her bir server icin dll’lerin
belirtilen klasöre konmasi gerekmektedir.
c. GAC: Global Assembly Cache üzerinden dll’leri okur.
Bu noktada bir not ileteyim eger server üzerinde calisan kodu debug etmek isterseniz yine
server\bin\assembly klasörüne .pdb uzantili debug symbol’lerinizi yerleştirmeniz gerekmekte.
Ikinci bir not da eger serverda custom code execution kapaliysa açmak için server üzerinde
powershell ile su kodlari calistirmaniz gerekmekte:
Add-PSSnapin Microsoft.Crm.PowerShell
$setting = get-crmsetting customcodesettings
$setting.AllowExternalCode="True"
Degerleri kontrol etmek için bu komutlari calistirabilirsiniz :
set-crmsetting $setting
get-crmsetting customcodesettings
Ayarlari tersine çevirmek için “AllowExternalCode”’a “False” değerini vermeniz yeterli.
Butun bu adimlari tamamladıktan sonra “Register Selected Plugin” düğmesine tikliyoruz. Plug-in
kaydetmediki ilk adimi gerçekleştirmiş olduk sira diğer adimlarda :)
Bu noktada plug-in’i hangi event(ler) için yazdiysak onun için adim(lar) eklememiz gerekiyor. Plug-in
anlatirken hep bir olay olduğunda yani veritabanina bir kayit eklendiğinde, silindiğinde ya da bir alani
güncellendiğinde tetiklenebilir gibi orneklerle anlatıyoruz ama aslinda olay bundan daha derin gelin
simdi custom entity’ler için yani bizim oluşturduğunuz varliklar için sistem üzerinde nasil olaylarin
tetiklenmelerini yakalayabiliyoruz. Literaturde bu konu message olarak geçmekte yani CRM
eventlarina mesaj adi verilmekte.
Ownership
Type
Message
Availability
Entity
Supported
Deployment
Assign
User-owned
entities only
Server
Server
Create
User-owned
and
organizationowned entities
Both
Server
Delete
User-owned
and
organizationowned entities
Both
Server
Message Name
57 | P a g e
Dynamics CRM 2015 Kodlama Mimarisi v1.0 - Baris KANLICA
GrantAccess
User-owned
entities only
Server
Server
ModifyAccess
User-owned
entities only
Server
Server
Retrieve
User-owned
and
organizationowned entities
Both
Server
RetrieveMultiple
User-owned
and
organizationowned entities
Both
Server
RetrievePrincipalAccess
User-owned
entities only
Both
Server
RetrieveSharedPrincipalsAndAccess User-owned
entities only
Both
Server
RevokeAccess
User-owned
entities only
Server
Server
SetState
User-owned
and
organizationowned entities
Both
Server
SetStateDynamicEntity
User-owned
and
organizationowned entities
Both
Server
Update
User-owned
and
organizationowned entities
Both
Server
Listede de yer aldigi gibi Retrieve, RetrieveMultiple yani veritababindna sorgulama ya da SetState
yani bir kaydin durumun değişmesi gibi birçok farkli mesaj için plug-in’i tetikletebilmekteyiz.
58 | P a g e
Dynamics CRM 2015 Kodlama Mimarisi v1.0 - Baris KANLICA
Lutfen sunu unutmayin yukarıdaki liste sadece custom entity’ler için campaign, campaignactivity, list
gibi entity’ler için farkli mesajlar da mevcut tum listeye SDK içindeki “Message-entity support for
plug-ins.xlsx” isimli dosyadan ulaşabilirsiniz.
Simdi yeni bir adim ekleyerek bir mesaj için plug-in’imizin tetiklenmesini saglayalim. Bunun için plugin üzerinde sag tuşa tıklayarak ya da yukarıdaki “Register” düğmesine tıklayarak acilan menüden
“Register New Step”’e tikliyoruz. Karsimiza asagidaki gibi bir pencere cikacak:
Message: Yukarida bahsettigim mesajlardan birini buraya yazabilirsiniz. Hangi mesaji yazarsaniz plugin bu olay icin calisacak. Create/Update gibi mesaj isimleri yazarken otomatik olarak tamamlamaya
calistigini göreceksiniz. Her bir mesaj icin ayri step’ler tanimlaniz gerekmektedir.
Primary Entity: Bu plug-in hangi entity yani varlik üzerinde calisacak. Buraya account, contact gibi bir
varlik adi yazabilirsiniz.
Secondary Entity: Bu plug-in’i ikinci bir varlik icin tanıtacaksak buraya yazabiliriz.
Event Pipeline Stage of Execution: Bu kisimda plug-in’i pre yani veri veritabanina gitmeden mi
calistiracagiz yoksa post yani kaydedildikten sonra mi calistiracagiz bunu seçiyoruz.
Execution Mode: (sadece post da ikisinden birini seçebilmekteyiz) kod senkron yani sistemde
kullanici ile etkileşimli ayni anda mi hareket etsin yoksa asenkron yani kullanicidan bagimsiz arka
tarafta sessizce mi calissin bunu seciyoruz.
Deployment: Bu kod server da mı calissin yoksa Outlook client gibi offline modda da calissin
seçeneğidir.
Bu yukarida acikladigim bolumler standart ayarlar. Yani her plug-in step’i tanimladigimizda mutlaka
bakmamiz gereken ayarlar. Ekranda bir de farkli ayarlar var onlara da bakalim.
Event Handler: Bu kodun calismaya baslayacagi class’in seçildiği yerdir. Cok değişik bir hareket
yapmadiginiz surece zaten plugin registration tool otomatik bir sekilde “Execute” metodunu görecek
ve orayi seçecektir.
Name: Sistem bu step icin otomatik bir atamakta ama değiştirmek isterseniz buradan yapabilirsiniz.
59 | P a g e
Dynamics CRM 2015 Kodlama Mimarisi v1.0 - Baris KANLICA
Run in User’s Context: Belki dokunmaniz gereken noktalardan biri olabilir. Bu kodu hangi kullanici
yetkileriyle calistirmak istiyorsaniz onu seçebilirsiniz. Standartta ayari “Calling User” yani hangi
kullanici bu işlemi yaparsa seçilidir.
ExecutionOrder: eger ayni varlik içinde ve ayni mesaj icin başka bir plug-in daha varsa buraya sira
numaralari vererek hangisini once-sonra calisacagini belirleyebilirsiniz.
Unsecure ve Secure Configuration’larin ne ise yaradigina zaten “Plug-in Yapici Metodlari” basligi
altinda değinmiştim.
Butun gerekli ayarlamalari yaptıktan sonra en allta bulunan “Register New Step” düğmesine
tıklayarak işlemi tamamlıyoruz. Artik plug-in’i test edebilirsiniz.
Plug-in’i Debug Etmek
CRM 2015 icerisinde yazmis olduğumuz bir plug-in’i debug etmenin iki temel yolu bulunmakta. Birinci
yol calisan sisteme visual studio ile attach olarak yapilan, ikinci yol ise plug-in profiller kullanmak.
Profiller icin Microsoft dokümantasyonlarda plug-in performansini ölçmek icin kullaniliyor dese de
aslinda 1. Yöntemden daha saglikli olduğunu söyleyebilirim. Ozellikle Online sistemler icin başka
çareniz de yok zaten.
Servis’lere Attach olarak Debug Etme
Plug-in’i sisteme kaydettikten sonra visual studio ile nereye attach olacagimizi seçmemiz gerekmekte.
Kayit Ayari
Servis
online
w3wp.exe
offline
Microsoft.Crm.Application.Hoster.exe
asynchronous olarak kaydedilmis plug- CrmAsyncService.exe
in’ler (ya da custom workflow’lar)
sandbox (isolation mode)
Microsoft.Crm.Sandbox.WorkerProcess.exe
Online : CRM Web arabirimini
Offline : Outllok Client gibi offline yapidaki yazilimlar
Kendimize uygun olan secimi yaptıktan sonra geriye bir tek breakpoint’i seçip attach olmak kaliyor.
Visual Studio’yu acip “Attach to Process..” diyoruz.
60 | P a g e
Dynamics CRM 2015 Kodlama Mimarisi v1.0 - Baris KANLICA
Sonra asagidaki ekran goruntusu gelecek ve ilgili servisi seçeceğiz.
Asagidaki ekran goruntusunde de goruldugu uzere visual studio ilgili yerde devreye girecek ve bizim
kodu debug etmemizi saglayacaktir.
61 | P a g e
Dynamics CRM 2015 Kodlama Mimarisi v1.0 - Baris KANLICA
Islem bu kadar basit sadece dikkat etmeniz gereken noktalar bulunmakta;
1. Eger disk’e yaz adimini seçerek plug-in’i sisteme kayit ettiyseniz bu plug-in’in debug modda
yeni bir versiyonunu ayni dizine kopyalamak icin plug-in üzerinde calistigi servisi yeniden
baslatmaniz gerekir.
2. Plug-in üzerinde değişiklikler yaptiginizda her seferinde registration tool’u ile güncelleme
islemini yapin.
3. Eger plug-in’i bu sekilde test edip butun işlemleri bitirdiyseniz onu veritabanina kaydetmenizi
tavsiye ederim. Disk olarak birakmaniz pek önerilen bir yöntem değildir.
4. Her ne olursa olsun .pdb uzantili dosyalari assembly klasörü içerisinde birakmayin.
5. Sandbox içindeki bir plug-in’i debug etmek istiyorsaniz asagidaki registery ayarinin 1
(DWORD) değeri tasidigindan emin olun :
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\MSCRM\SandboxDebugPlugins
Plug-in Profiller’i kullanarak Debug etme
Bu yöntemi kullanmak birçok bakimdan daha avantajli nedeni ise CRM size kullanicinin yaptigi
hareketi simule ediyor ve böylece siz bunun üzerinden debug ediyorsunuz. Ayrica ciktilari baskalarina
gönderme opsiyonu da bulunmakta. En değerli ozelligi ise plug-in’i derleyip derleyip CRM’e atmak
gibi bir derdiniz yok. Yani bir hata mi yakaladiginiz ya da kodun bir yerini mi değiştirmek istiyorsunuz
tek yapmaniz gereken degisikligi yapip uygulamaya yeniden bağlanmak ayni kullanici hareketi tekrar
simule edilecek ve siz de yaptiginiz degisikligin etkilerini göreceksiniz.
Simdi sirasiyla bu işlemi nasil yapacagimiza bakalim. Oncelikle “Plug-in Registration Tool”’u aciyoruz
ve işlem yapmak istediğimiz organizasyonu seçiyoruz.
Tool üzerindeki “Install Profiller” düğmesine tikliyoruz. Boylece “Plug-in Profiller”’da listemizde
gozukmeye basliyor.
Daha sonra test etmek istediğimiz step’i seçiyoruz ve yine toolbar’da yer alan “Start Profilling”
düğmesine basıyoruz. Karsimiza asagidaki gibi “Profiler Settings” ekrani cikiyor.
62 | P a g e
Dynamics CRM 2015 Kodlama Mimarisi v1.0 - Baris KANLICA
Bu adimda iki secenekten birini seçmeniz lazim.
 Exception: Microsoft, Exception yöntemini önermekte gordugunuz gibi. Bunun anlami ise su
kullanici ya da siz plug-in’i tetikledeginizde plug-in calisacak ve sistem size bir hata mesaji
gösterecek. Bu hata mesaji içerisinde plug-i debug etmemize yarayacak bilgiler yer alacak. Bu
bilgileri almak icin cikan hata penceresinde “Download Log File” düğmesine basmaniz
gerekmekte.

Persist to Entity: Eger ikinci adimi seçerseniz bu sefer butun bilgiler CRM içerisinde bir Entity
içerisine yazılacak.
Iki adimdan birini seçip plug-in’i tetikleyecek işlemi yaptıktan sonra “Plug-in Registration Tool”
içerisinde “Debug” düğmesine tiklamaniz gerekmekte.
63 | P a g e
Dynamics CRM 2015 Kodlama Mimarisi v1.0 - Baris KANLICA
Karsiniza asagidaki gibi bir ekran gelecek. Bu ekran bir önceki seçtiğiniz adima gore iki işlemden birini
yapmaniz gerekmekte;
Eger “Exception” adimini seçtiyseniz “…”’ya basarak kaydetmiş olduğunuz hata dosyasinin yerini
gösterin. Eger “Persist To Entity” adimini seçtiyseniz asagi doğru duran ok düğmesine basiniz.
Karsiniza soyle bir ekran cikacak;
64 | P a g e
Dynamics CRM 2015 Kodlama Mimarisi v1.0 - Baris KANLICA
Bu ekrandan kaydettiğiniz profile log’unu seçebilirsiniz.
Sonra sirasiyla .dll dosyanizi sisteme gösterin ve debug etmek istediğiniz Plug-in’i secin. Visual
Studio’yu acin ve “PluginRegistration.exe” uygulamasina attach olun. Start Execution dugmesina
basin ve breakpoint koyduğunuz yerde bekleyin. Bir sure sonra Visual Studio’a beklediğimiz yere
gelecek ve bizim debug etmemizi sağlayacak.
65 | P a g e
Dynamics CRM 2015 Kodlama Mimarisi v1.0 - Baris KANLICA
Eger plug-in’de değişiklik yapmaniz gerekiyorsa değişiklikleri yapin ama CRM’e atmayin ilk once biraz
önceki adimlari uygulayarak kodu tekrar test edin. Artik koddan eminseniz CRM içerisine
gönderebilirsiniz güncellediğiniz .dll’i.
Bu arada belirtmeliyim ki asagida “plug-in traces” bolumunden de debug etmeden kodun nasil
calistigini izleyebilirsiniz.
Burada icin TracingService ile yazacaginiz mesajlar görüntülenecektir.
66 | P a g e

Benzer belgeler