Dynamics CRM 2015 Plug-in Mimarisi

Transkript

Dynamics CRM 2015 Plug-in Mimarisi
Dynamics CRM 2015
Plug-in Mimarisi
Baris KANLICA
Icerik
Yazar Hakkinda ........................................................................................................................................ 2
E-Kitap Hakkinda ..................................................................................................................................... 2
Dynamics CRM 2015 Plug-in Mimarisi..................................................................................................... 3
Plug-in Execution Context ................................................................................................................... 3
Organization Servise Erisme ................................................................................................................ 4
Notification Servise Erisme.................................................................................................................. 4
Input ve Output Parametreleri ............................................................................................................ 4
Pre ve Post Entity Imajlari ................................................................................................................... 5
Execute Metodu ...................................................................................................................................... 6
Plug-in’ler Icerisindeki Hatalari Yakalamak ............................................................................................. 7
Plug-in Yapici Metodlari ........................................................................................................................ 10
Hadi Plug-in Yazalim .............................................................................................................................. 11
Veritabanina gitmeden kayitlari değiştirmek .................................................................................... 11
Update aninda update edilmemiş değerlere ulaşmak ...................................................................... 13
Uzerinde calistigim nesnenin id’si nerede? ....................................................................................... 16
Plug-in’ler arasinda bilgi paylasimi .................................................................................................... 16
Plug-in Registration Tool’u Kullanmak .................................................................................................. 18
Plug-in’i Debug Etmek ........................................................................................................................... 24
Servis’lere Attach olarak Debug Etme ............................................................................................... 24
Plug-in Profiller’i kullanarak Debug etme.......................................................................................... 26
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 ‘te Plug-in konusunu ele aldigim yazilarin biraraya toplanmis halidir.
Ilereyen zamanlarda diger yazilarimi da 2015 versiyonuna guncelleyerek daha genis bir e-kitap
olusturmayi amaclamaktayim.
Umarim hepinize faydali olur.
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
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;
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
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
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;
}
}
}
}
Execute Metodu
InputParameters ve Output Parameters konularindan bahsederken Request ve Response konularina
değinmeden geçtim konu havada kalmasin diye ayri bir baslik altinda incelemenin daha doğru
olacagini duşundum.
CRM Servisi içerisindeki Execute Metodu Request ve Response yani Talep ve Yanit seklinde
calismaktadir. Sistem üzerinde yapacaginiz butun hareketleri bu sekilde yaopabilirsiniz 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.
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
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.");
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)
{
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)
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;
}
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.
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));
// 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.
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.
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.
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());
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.
}
}
}
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.
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.
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.
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
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.
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.
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.
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.
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.
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.
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;
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.
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.

Benzer belgeler