cub-e.net

just coding...

CRM Plugini Yaziyoruz :)

23 Eylul Carsamba günü saat 20:30 - 21:30 arasında bir webcast verecegim. Internet uzerinden yapilacak olan bu yayina herkes katilabilir. Link daha sonra paylasilacaktir.

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-in’ler (ya da custom workflow’lar)

CrmAsyncService.exe

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.

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 On-Premises 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.

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.

 

Message Name

Ownership Type

Message Availability

Entity Supported Deployment

Assign

User-owned entities only

Server

Server

Create

User-owned and organization-owned entities

Both

Server

Delete

User-owned and organization-owned entities

Both

Server

GrantAccess

User-owned entities only

Server

Server

ModifyAccess

User-owned entities only

Server

Server

Retrieve

User-owned and organization-owned entities

Both

Server

RetrieveMultiple

User-owned and organization-owned 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 organization-owned entities

Both

Server

SetStateDynamicEntity

User-owned and organization-owned entities

Both

Server

Update

User-owned and organization-owned 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 plug-in ü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 plug-in 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.

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(Pre-Event) 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 plug-in’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.

 

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 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 non-public (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;

}

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 plug-in’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(IOrganizationServiceFactory));

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 post-event 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(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;

            }

        }

    }

}

Plug-in’ler Icerisindeki Hatalari Yakalamak

Bu makalede plug-in hata yakalama mekanizmasina deginecegim. 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");

                            }

                        }

                    }

 

Message-entity support for plug-ins for Custom Entities in CRM 2013

Message Name Primary Entity Secondary Entity Message Availability
Assign new_customentity NULL Server
Create new_customentity NULL Both
Delete new_customentity NULL Both
GrantAccess new_customentity NULL Server
ModifyAccess new_customentity NULL Server
Retrieve new_customentity NULL Both
RetrieveMultiple new_customentity NULL Both
RetrievePrincipalAccess new_customentity NULL Both
RetrieveSharedPrincipalsAndAccess new_customentity NULL Both
RevokeAccess new_customentity NULL Server
SetState new_customentity NULL Both
SetStateDynamicEntity new_customentity NULL Both
Update new_customentity NULL Both

Message-entity support for plug-ins in CRM 2013

Message Name Primary Entity Secondary Entity Message Availability
AddItem Campaign NULL Both
AddItem CampaignActivity NULL Both
AddListMembers List NULL Server
AddMember List NULL Server
AddMembers Team NULL Server
AddPrivileges Role NULL Server
AddProductToKit Product NULL Both
AddRecurrence RecurringAppointmentMaster NULL Both
AddToQueue QueueItem NULL Both
AddUserToRecordTeam TeamTemplate NULL Server
Assign Account NULL Server
Assign Annotation NULL Server
Assign Appointment NULL Server
Assign Campaign NULL Server
Assign CampaignActivity NULL Server
Assign CampaignResponse NULL Server
Assign Connection NULL Server
Assign Contact NULL Server
Assign Contract NULL Server
Assign CustomerOpportunityRole NULL Server
Assign CustomerRelationship NULL Server
Assign Email NULL Server
Assign Fax NULL Server
Assign Goal NULL Server
Assign GoalRollupQuery NULL Server
Assign Incident NULL Server
Assign IncidentResolution NULL Server
Assign Invoice NULL Server
Assign Lead NULL Server
Assign Letter NULL Server
Assign List NULL Server
Assign msdyn_PostAlbum NULL Server
Assign msdyn_wallsavedqueryusersettings NULL Server
Assign Opportunity NULL Server
Assign OpportunityClose NULL Server
Assign OrderClose NULL Server
Assign PhoneCall NULL Server
Assign ProcessSession NULL Server
Assign Queue NULL Server
Assign Quote NULL Server
Assign QuoteClose NULL Server
Assign RecurringAppointmentMaster NULL Server
Assign SalesOrder NULL Server
Assign ServiceAppointment NULL Server
Assign SharePointDocumentLocation NULL Server
Assign SharePointSite NULL Server
Assign Task NULL Server
Assign Template NULL Server
Assign UserForm NULL Server
Assign UserQuery NULL Server
Assign UserQueryVisualization NULL Server
AssignUserRoles Role NULL Server
Associate NULL NULL Both
BackgroundSend Email NULL Both
Book Appointment NULL Server
Book RecurringAppointmentMaster NULL Server
Book ServiceAppointment NULL Server
Cancel Contract NULL Both
Cancel SalesOrder NULL Both
CheckIncoming Email NULL Both
CheckPromote Email NULL Both
Clone Contract NULL Both
Close Incident NULL Both
Close Quote NULL Both
CopyDynamicListToStatic List NULL Server
CopySystemForm SystemForm NULL Server
Create Account NULL Both
Create ActivityMimeAttachment NULL Both
Create Annotation NULL Both
Create Appointment NULL Both
Create BusinessUnit NULL Both
Create BusinessUnitNewsArticle NULL Both
Create Calendar NULL Both
Create Campaign NULL Both
Create CampaignActivity NULL Both
Create CampaignResponse NULL Both
Create Competitor NULL Both
Create Connection NULL Both
Create ConnectionRole NULL Both
Create ConnectionRoleObjectTypeCode NULL Both
Create Contact NULL Both
Create Contract NULL Both
Create ContractDetail NULL Both
Create ContractTemplate NULL Both
Create CustomerAddress NULL Both
Create CustomerOpportunityRole NULL Both
Create CustomerRelationship NULL Both
Create Discount NULL Both
Create DiscountType NULL Both
Create Email NULL Both
Create Equipment NULL Both
Create ExchangeSyncIdMapping NULL Both
Create Fax NULL Both
Create FieldPermission NULL Both
Create FieldSecurityProfile NULL Both
Create Goal NULL Both
Create GoalRollupQuery NULL Both
Create Incident NULL Both
Create IncidentResolution NULL Both
Create Invoice NULL Both
Create InvoiceDetail NULL Both
Create KbArticle NULL Both
Create KbArticleComment NULL Both
Create KbArticleTemplate NULL Both
Create Lead NULL Both
Create Letter NULL Both
Create List NULL Both
Create Metric NULL Both
Create msdyn_PostAlbum NULL Both
Create msdyn_PostConfig NULL Both
Create msdyn_PostRuleConfig NULL Both
Create msdyn_wallsavedquery NULL Both
Create msdyn_wallsavedqueryusersettings NULL Both
Create Opportunity NULL Both
Create OpportunityClose NULL Both
Create OpportunityProduct NULL Both
Create OrderClose NULL Both
Create PhoneCall NULL Both
Create Post NULL Both
Create PostComment NULL Both
Create PostFollow NULL Both
Create PriceLevel NULL Both
Create PrincipalObjectAttributeAccess NULL Both
Create ProcessSession NULL Both
Create Product NULL Both
Create ProductPriceLevel NULL Both
Create Queue NULL Both
Create QueueItem NULL Both
Create Quote NULL Both
Create QuoteClose NULL Both
Create QuoteDetail NULL Both
Create RecurrenceRule NULL Both
Create RecurringAppointmentMaster NULL Both
Create Role NULL Both
Create RollupField NULL Both
Create SalesLiterature NULL Both
Create SalesLiteratureItem NULL Both
Create SalesOrder NULL Both
Create SalesOrderDetail NULL Both
Create Service NULL Both
Create ServiceAppointment NULL Both
Create SharePointDocumentLocation NULL Both
Create SharePointSite NULL Both
Create Site NULL Both
Create Subject NULL Both
Create SystemUser NULL Both
Create Task NULL Both
Create Team NULL Both
Create Template NULL Both
Create Territory NULL Both
Create TraceLog NULL Both
Create TransactionCurrency NULL Both
Create UoM NULL Both
Create UoMSchedule NULL Both
Create UserQuery NULL Both
Create WebResource NULL Both
CreateException Appointment NULL Both
CreateInstance RecurringAppointmentMaster NULL Server
Delete Account NULL Both
Delete ActivityMimeAttachment NULL Both
Delete Annotation NULL Both
Delete Appointment NULL Both
Delete BusinessUnit NULL Both
Delete BusinessUnitNewsArticle NULL Both
Delete Calendar NULL Both
Delete Campaign NULL Both
Delete CampaignActivity NULL Both
Delete CampaignResponse NULL Both
Delete Competitor NULL Both
Delete Connection NULL Both
Delete ConnectionRole NULL Both
Delete ConnectionRoleObjectTypeCode NULL Both
Delete Contact NULL Both
Delete Contract NULL Both
Delete ContractDetail NULL Both
Delete ContractTemplate NULL Both
Delete CustomerAddress NULL Both
Delete CustomerOpportunityRole NULL Both
Delete CustomerRelationship NULL Both
Delete Discount NULL Both
Delete DiscountType NULL Both
Delete Email NULL Both
Delete Equipment NULL Both
Delete ExchangeSyncIdMapping NULL Both
Delete Fax NULL Both
Delete FieldPermission NULL Both
Delete FieldSecurityProfile NULL Both
Delete Goal NULL Both
Delete GoalRollupQuery NULL Both
Delete Incident NULL Both
Delete IncidentResolution NULL Both
Delete Invoice NULL Both
Delete InvoiceDetail NULL Both
Delete KbArticle NULL Both
Delete KbArticleComment NULL Both
Delete KbArticleTemplate NULL Both
Delete Lead NULL Both
Delete Letter NULL Both
Delete List NULL Both
Delete Metric NULL Both
Delete msdyn_PostAlbum NULL Both
Delete msdyn_PostConfig NULL Both
Delete msdyn_PostRuleConfig NULL Both
Delete msdyn_wallsavedquery NULL Both
Delete msdyn_wallsavedqueryusersettings NULL Both
Delete Opportunity NULL Both
Delete OpportunityClose NULL Both
Delete OpportunityProduct NULL Both
Delete OrderClose NULL Both
Delete PhoneCall NULL Both
Delete Post NULL Both
Delete PostComment NULL Both
Delete PostFollow NULL Both
Delete PostLike NULL Both
Delete PriceLevel NULL Both
Delete PrincipalObjectAttributeAccess NULL Both
Delete ProcessSession NULL Both
Delete Product NULL Both
Delete ProductPriceLevel NULL Both
Delete Publisher NULL Both
Delete PublisherAddress NULL Both
Delete Queue NULL Both
Delete QueueItem NULL Both
Delete Quote NULL Both
Delete QuoteClose NULL Both
Delete QuoteDetail NULL Both
Delete RecurrenceRule NULL Both
Delete RecurringAppointmentMaster NULL Both
Delete Role NULL Both
Delete RollupField NULL Both
Delete SalesLiterature NULL Both
Delete SalesLiteratureItem NULL Both
Delete SalesOrder NULL Both
Delete SalesOrderDetail NULL Both
Delete Service NULL Both
Delete ServiceAppointment NULL Both
Delete SharePointDocumentLocation NULL Both
Delete SharePointSite NULL Both
Delete Site NULL Both
Delete Solution NULL Both
Delete Subject NULL Both
Delete Task NULL Both
Delete Team NULL Both
Delete Template NULL Both
Delete Territory NULL Both
Delete TransactionCurrency NULL Both
Delete UoM NULL Both
Delete UoMSchedule NULL Both
Delete UserForm NULL Both
Delete UserQuery NULL Both
Delete UserQueryVisualization NULL Both
Delete WebResource NULL Both
DeleteOpenInstances RecurringAppointmentMaster NULL Both
DeliverIncoming Email NULL Server
DeliverPromote Email NULL Both
DetachFromQueue Email NULL Both
Disassociate NULL NULL Both
Execute NULL NULL Both
ExecuteById SavedQuery NULL Both
ExecuteById UserQuery NULL Both
Export NULL NULL Server
ExportAll NULL NULL Server
ExportCompressed NULL NULL Server
ExportCompressedAll NULL NULL Server
GrantAccess Account NULL Server
GrantAccess Annotation NULL Server
GrantAccess Appointment NULL Server
GrantAccess Campaign NULL Server
GrantAccess CampaignActivity NULL Server
GrantAccess CampaignResponse NULL Server
GrantAccess Connection NULL Server
GrantAccess Contact NULL Server
GrantAccess Contract NULL Server
GrantAccess CustomerOpportunityRole NULL Server
GrantAccess CustomerRelationship NULL Server
GrantAccess Email NULL Server
GrantAccess Fax NULL Server
GrantAccess Goal NULL Server
GrantAccess GoalRollupQuery NULL Server
GrantAccess Incident NULL Server
GrantAccess IncidentResolution NULL Server
GrantAccess Invoice NULL Server
GrantAccess Lead NULL Server
GrantAccess Letter NULL Server
GrantAccess List NULL Server
GrantAccess msdyn_PostAlbum NULL Server
GrantAccess msdyn_wallsavedqueryusersettings NULL Server
GrantAccess Opportunity NULL Server
GrantAccess OpportunityClose NULL Server
GrantAccess OrderClose NULL Server
GrantAccess PhoneCall NULL Server
GrantAccess ProcessSession NULL Server
GrantAccess Queue NULL Server
GrantAccess Quote NULL Server
GrantAccess QuoteClose NULL Server
GrantAccess RecurringAppointmentMaster NULL Server
GrantAccess SalesOrder NULL Server
GrantAccess ServiceAppointment NULL Server
GrantAccess SharePointDocumentLocation NULL Server
GrantAccess SharePointSite NULL Server
GrantAccess Task NULL Server
GrantAccess Template NULL Server
GrantAccess UserForm NULL Server
GrantAccess UserQuery NULL Server
GrantAccess UserQueryVisualization NULL Server
Handle Appointment NULL Both
Handle CampaignActivity NULL Both
Handle CampaignResponse NULL Both
Handle Email NULL Both
Handle Fax NULL Both
Handle Incident NULL Both
Handle Letter NULL Both
Handle PhoneCall NULL Both
Handle RecurringAppointmentMaster NULL Both
Handle ServiceAppointment NULL Both
Handle Task NULL Both
Import NULL NULL Server
ImportAll NULL NULL Server
ImportCompressedAll NULL NULL Server
ImportCompressedWithProgress NULL NULL Server
ImportWithProgress NULL NULL Server
LockInvoicePricing Invoice NULL Server
LockSalesOrderPricing SalesOrder NULL Server
Lose Opportunity NULL Both
Merge Account NULL Server
Merge Contact NULL Server
Merge Lead NULL Server
ModifyAccess Account NULL Server
ModifyAccess Annotation NULL Server
ModifyAccess Appointment NULL Server
ModifyAccess Campaign NULL Server
ModifyAccess CampaignActivity NULL Server
ModifyAccess CampaignResponse NULL Server
ModifyAccess Connection NULL Server
ModifyAccess Contact NULL Server
ModifyAccess Contract NULL Server
ModifyAccess CustomerOpportunityRole NULL Server
ModifyAccess CustomerRelationship NULL Server
ModifyAccess Email NULL Server
ModifyAccess Fax NULL Server
ModifyAccess Goal NULL Server
ModifyAccess GoalRollupQuery NULL Server
ModifyAccess Incident NULL Server
ModifyAccess IncidentResolution NULL Server
ModifyAccess Invoice NULL Server
ModifyAccess Lead NULL Server
ModifyAccess Letter NULL Server
ModifyAccess List NULL Server
ModifyAccess msdyn_PostAlbum NULL Server
ModifyAccess msdyn_wallsavedqueryusersettings NULL Server
ModifyAccess Opportunity NULL Server
ModifyAccess OpportunityClose NULL Server
ModifyAccess OrderClose NULL Server
ModifyAccess PhoneCall NULL Server
ModifyAccess ProcessSession NULL Server
ModifyAccess Queue NULL Server
ModifyAccess Quote NULL Server
ModifyAccess QuoteClose NULL Server
ModifyAccess RecurringAppointmentMaster NULL Server
ModifyAccess SalesOrder NULL Server
ModifyAccess ServiceAppointment NULL Server
ModifyAccess SharePointDocumentLocation NULL Server
ModifyAccess SharePointSite NULL Server
ModifyAccess Task NULL Server
ModifyAccess Template NULL Server
ModifyAccess UserForm NULL Server
ModifyAccess UserQuery NULL Server
ModifyAccess UserQueryVisualization NULL Server
Publish NULL NULL Server
PublishAll NULL NULL Server
QualifyLead Lead NULL Both
Recalculate Goal NULL Server
RemoveItem Campaign NULL Both
RemoveItem CampaignActivity NULL Both
RemoveMember List NULL Server
RemoveMembers Team NULL Server
RemovePrivilege Role NULL Server
RemoveProductFromKit NULL NULL Both
RemoveRelated Invoice Contact Both
RemoveRelated Lead Contact Both
RemoveRelated Lead Account Both
RemoveRelated Opportunity Account Both
RemoveRelated Opportunity Contact Both
RemoveRelated Opportunity Competitor Both
RemoveRelated Product Competitor Both
RemoveRelated Product Lead Both
RemoveRelated Quote Contact Both
RemoveRelated SalesLiterature Competitor Both
RemoveRelated SalesLiterature Product Both
RemoveRelated SalesOrder Contact Both
RemoveUserFromRecordTeam TeamTemplate NULL Server
RemoveUserRoles Role NULL Server
ReplacePrivileges Role NULL Server
Reschedule Appointment NULL Server
Reschedule RecurringAppointmentMaster NULL Server
Reschedule ServiceAppointment NULL Server
Retrieve Account NULL Both
Retrieve ActivityMimeAttachment NULL Both
Retrieve ActivityPointer NULL Both
Retrieve Annotation NULL Both
Retrieve Appointment NULL Both
Retrieve BusinessUnitNewsArticle NULL Both
Retrieve Calendar NULL Both
Retrieve Campaign NULL Both
Retrieve CampaignActivity NULL Both
Retrieve CampaignResponse NULL Both
Retrieve Competitor NULL Both
Retrieve Connection NULL Both
Retrieve ConnectionRole NULL Both
Retrieve ConnectionRoleObjectTypeCode NULL Both
Retrieve Contact NULL Both
Retrieve Contract NULL Both
Retrieve ContractDetail NULL Both
Retrieve ContractTemplate NULL Both
Retrieve CustomerAddress NULL Both
Retrieve CustomerOpportunityRole NULL Both
Retrieve CustomerRelationship NULL Both
Retrieve Dependency NULL Both
Retrieve Discount NULL Both
Retrieve DiscountType NULL Both
Retrieve Email NULL Both
Retrieve Equipment NULL Both
Retrieve ExchangeSyncIdMapping NULL Both
Retrieve Fax NULL Both
Retrieve Goal NULL Both
Retrieve GoalRollupQuery NULL Both
Retrieve Incident NULL Both
Retrieve IncidentResolution NULL Both
Retrieve InvalidDependency NULL Both
Retrieve Invoice NULL Both
Retrieve InvoiceDetail NULL Both
Retrieve KbArticle NULL Both
Retrieve KbArticleComment NULL Both
Retrieve KbArticleTemplate NULL Both
Retrieve Lead NULL Both
Retrieve LeadAddress NULL Both
Retrieve Letter NULL Both
Retrieve List NULL Both
Retrieve Metric NULL Both
Retrieve msdyn_PostAlbum NULL Both
Retrieve msdyn_PostConfig NULL Both
Retrieve msdyn_PostRuleConfig NULL Both
Retrieve msdyn_wallsavedquery NULL Both
Retrieve msdyn_wallsavedqueryusersettings NULL Both
Retrieve