Sizlere bugün Dynamics CRM içerisindeki Plug-in Mimarisinden söz etmek istiyorum. Plug-in’ler IPlugin arayüzünden türetilmiş kod parçacıklarıdır ve CRM’in içerisinde belli bir sıra içerisinde çalışırlar. Kabaca tarif edersek bunlar birer .dll dosyalarıdır ve CRM’e bu dosyaya ne zaman bakması gerektiğini biz söyleriz. Plug-in’ler olay bazlı olarak çalışırlar. Yani herhangi bir kayıt oluşturulduğunda, güncellendiğinde, silindiğinde vb.. olaylar oldukça biz ilgili ayarı yapmışsak çalışırlar.
Plug-in’lerin en güzel yani pre ve post olarak çalışmaya ayarlanabilmeleridir. Plug-in’ler olay bazlı çalışırlar demiştim iste bu olay olmadan önceki kaydın son hali üzerinden ve olay olduktan sonraki hali üzerinden işlem yapmanıza olanak sağlarlar.
Plug-in’lerin çalışması için Microsoft.Xrm.Sdk.dll ve Microsoft.Crm.Sdk.Proxy.dll dosyalarının referanslara eklenmiş olması gerekmektedir. Tam yeri gelmişken bahsedeyim eğer siz üçüncü parti bir .dll kullanıyorsanız (yani kendi yazdığınız sınıfların olduğu ya da diğer uygulamalardan aldığınız) bu .dll’lerin ilgili serverin assembly klasöründe olduğundan emin olun yoksa plug-in çalışmayabilir.
public class MyPlugin: IPlugin
{
public void Execute(IServiceProvider serviceProvider)
{}
}
Detaya inecek olursak IPlugin arayüzünden türetilmiş bir sınıf içerisinde Execute metodu yer almalıdır. Bu metod parametre olarak IServiceProvider arayuzunden türetilmiş bir bilgi yığınını içerir. Yani CRM kod içerisinde yapacağımız işlemlerde bize CRM içerisinde olan olaylardan bize bilgi taşır ki biz de bu bilgileri kodun içerisinde kullanalım. Ne gibi veriler içinde taşımakta derseniz çok fazla detay verebilirim mesela su anda hangi kullanıcının işlem yaptığı, taşıdığı nesnenin turunu, eğer pre-plugin ise değerlerin değişmeden önceki halini vb… bir çok veri içermekte.
Simdi sırasıyla gelen veri yığınlarını inceleyelim.
Plug-in Execution Context
Çalışma zamanında oluşan veriler bu yapı içerisinde yer almaktadır. Bunlara kodun çalışma hiyerarşisi ve entity bilgileri de dahildir.
IPluginExecutionContext context = (IPluginExecutionContext)
serviceProvider.GetService(typeof(IPluginExecutionContext));
Bir olay olduğunda kayıt edilmiş bir plug-in’e bu veriler aktarılır aslında o anda çalışan bütün plug-in’lere bu veriler aktarılır ama execution pipeline denen sıralamaya uyarak aktarılır önce 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 sözü edilen kodların sonsuz döngüye girmemeleri için sistem içerisinde Depth denen bir anahtar yer almaktadır. Varsayılanda bu bir plugin’i arda arda 8 kere çalıştırır ve durdurur. Böylece sistemin bir kod yanlışlığı ile çökmesi engellenmiştir. Bu değer değiştirebilir bir değerdir.
Sistemin çalışması da aslında şu mantığa dayanmakta;
Resim-1
Yani Event Execution Pipeline’a bir mesaj girdiğinde Pre-Event -> Platform İşlemleri (Yani CRM’in kendi iç işleyişi) -> Post-Event seklinde islenmekte. Bu döngü senkron ve asenkron yapılar için böyle ilerlemekte.
Organization Servise Erişme
CRM içerisinde işlem yapabilmek her zaman bir servis nesnesine ihtiyaç duymaktayız. İşte kullanıcının hareketi neticesinde acilmiş bu servisi bize kullanmamız için geçirmekte serviceProvider nesnesi.
IOrganizationServiceFactory serviceFactory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
IOrganizationService service = serviceFactory.CreateOrganizationService(context.UserId);
Notification Servise Erişme
Senkron olarak işaretlenmiş plug-in’ler Microsoft Azure Service Bus’a veri mesaj gönderilirler. 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 yapılan hareketin yani su anda tetiklenmiş olayın bilgisini ve su anda üzerinde işlem görülen entity’nin bilgisini içerir. Bu bilgiye erişmek için “Target” nesnesine bakmamız gerekmektedir ve bu nesneyi alıp Entity class’ina çevirebiliriz. Input nesneleri Request message yapısındadır.
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 şekilde OutputParameters da Response message içerir. Ama sunu unutmayın ki senkron post-event ve asenkron plug-in’ler OutputParameters türünden nesneler içerirler.
Pre ve Post Entity Imajlari
Bu konuyu okurken sakin İngilizce Images kelimesinin resim anlamıyla karıştırmayın buradaki anlamı verinin o anki görüntüsü seklinde ifade etmek daha doğru olur. Aslında tam İngilizce tabiriyle snapshot. PreEntityImages ve PostEntityImages verileri sistem tarafından size gönderilir ama siz özellikle beklediğiniz alanları plug-in’in kayit işlemi sırasında sisteme söyleyebilirsiniz.
Burada tabii ki bir mantık çerçevesi olduğunu da unutmayın Create anında bir nesnenin preImage’i olamayacağı gibi Delete işleminden sonra da bir postImage beklemeyin.
Simdi bu bilgileri verdikten sonra bütün bunları birleştirerek bir plug-in temel görüntüsüne bakalım.
Açıklamalar 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(IOrganizationServiceFactory));
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;
}
}
}
}
Bu konuyla ilgili sorularınızı alt kısımda bulunan yorumlar alanını kullanarak sorabilirsiniz.
Referanslar