Merhaba bu yazımızda her Dynamics Crm geliştiricinin kullandığı XrmToolBox aracı için kendi pluginimizi geliştirip yayınlayacağız. Bu sayede xrmtoolbox kullananlar xrmtoolbox plugin sekmesinden pluginimizi indirip kullanabilecekler. Bu pluginimiz solutionlarımızın asenkron olarak import edilmesine olanak sağlayan basit kodlardan oluşan bir plugin olacak. Async Solution import hakkında detaylı bilgi almak için burdaki başlığı inceleyebilirsiniz. Eğer hiç XrmToolBox kullanmadıysanız https://www.xrmtoolbox.com adresinden bu aracın ne olduğuna dair detaylı bilgi edinebilirsiniz. Yazımız aşağıdaki başlıklardan oluşacak:
- Projenin oluşturulması ve gerekli bağımlılıkların yüklenmesi
- Plugin kodlarının yazılması
- Plugin Test/Debug
- Nuget paketi oluşturma
- XrmToolBox plugin store içerisine ekleme
1.Projenin oluşturulması ve gerekli bağımlılıkların yüklenmesi
Not: Çoğu kaynakda bu işlemi XRMToolbox project template yükliyip projeyi onun üzerinden oluşturarak anlatıyorlar. Visual Studio 2019 versiyonu için bu project template yok. Visual Studio 2017 için olanı ise ben çalıştıramadım. O yüzden bu project template i kullanmadan projemizi oluşturacağız.
Ben Visual Studio 2019 kullandım siz isterseniz 2017 versiyonunu da kullanabilirsiniz. İlk olarak Visual Studio’ da bir tane class library açıyoruz. Framework versiyonunu .NET Framework 4.7.2 seçiyoruz.
Not: Framework versiyonunu daha düşük bir versiyon seçerseniz projeye nuget üzerinden Dynamics Sdk eklenemiyor hata alıyoruz.


Şimdi projemize nuget üzerinden XrmToolBoxPackage ekleyeceğiz. Projemize sağ tıklayıp Manage Nuget Packages diyoruz ve açılan sayfada browse kısmına XrmToolBoxPackage yazıyoruz ve install diyoruz.

Install dedikten sonra verdiği uyarılara OK diyoruz.

XrmToolBoxPackage yüklendi şimdi sıra bu paketi güncellemeye geldi. Aynı sayfada Updates kısmına geliyoruz. Bu aşamaya kadar herşeyi doğru yaptıysak updates kısmında güncellemeleri görüyor olmamız gerekiyor. Bende 6 tane güncelleme görünüyor.

Microsoft.IdentityModel.Clients.ActiveDirectory hariç(nedeni için tıkla) diğerlerini seçip Update diyoruz ve gelen uyarılara OK diyerek güncelliyoruz.
2. Plugin kodlarının yazılması
Pluginimizin doğru çalışabilmesi için 2 ayrı dosya oluşturmamız gerekiyor.
- Plugin Class
- Plugin Controll Class
Plugin class’ımızın içeriği aşağıdaki şekilde olacak.
using System.ComponentModel.Composition;
using XrmToolBox.Extensibility;
using XrmToolBox.Extensibility.Interfaces;
namespace XrmToolBox.AsynchronousSolutionImport
{
[Export(typeof(IXrmToolBoxPlugin)),
ExportMetadata("BackgroundColor", "MediumBlue"),
ExportMetadata("PrimaryFontColor", "White"),
ExportMetadata("SecondaryFontColor", "LightGray"),
ExportMetadata("SmallImageBase64", null),
ExportMetadata("BigImageBase64", null),
ExportMetadata("Name", "Async Solutin Import"),
ExportMetadata("Description", "Dynamics Crm Asynchronous Solution Importy")]
public class AsyncSolutionImportPlugin : PluginBase
{
public override IXrmToolBoxPluginControl GetControl()
{
return new AsyncSolutionImportPluginControl();
}
}
}
Class’ımızın yukarısındaki ExportMetadata ifadeleri pluginimizin özellikleri. Bu kısımları kendi plugininize göre değiştirmelisiniz.
Not: Uygulamanıza icon eklemek istiyorsanız SmallImageBase64 ve BigImageBase64 alanlarına base64 string koyabilirsiniz veya null olarak bırakabilirsiniz. Önemli olan oraya konulacak stringin base64 image olmasıdır. Aksi halde bırakın pluginimizi XrmToolBox bile hata veriyor plugininizi silene kadar açılmıyor .
Class’ımız PluginBase classından türetip GetControl metodunu uygulamalıyız.
GetControl metodundaki ” return new AsyncSolutionImportPluginControl(); ” kısmındaki ” AsyncSolutionImportPluginControl ” isimlendirmesi bir sonraki adımda ekleyeceğimiz UserControl nesnemizin adı. Sizde bu isim farklı olabilir.
Şimdi sıra Plugin Controll Class’ ımızı eklemeye geldi. Projemize sağ tıklayıp Add New ıtem> User Control diyoruz ve isim olarak AsyncSolutionImportPluginControl ismini veriyoruz.
Oluşturduğumuz UserControl’ ı açıp sağ tıklayıp view code(f7) diyoruz. Aşağıdaki gibi bir kod bloğu göreceğiz.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace XrmToolBox.AsynchronousSolutionImport
{
public partial class AsyncSolutionImportPluginControl : UserControl
{
public AsyncSolutionImportPluginControl()
{
InitializeComponent();
}
}
}
Burada AsyncSolutionImportPluginControl isimli partial class “UserControl” classından türemiş. Biz bu UserControl kısmını “PluginControlBase” olarak değiştireceğiz. Classımız aşağıdaki gibi olacak:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using XrmToolBox.Extensibility;
namespace XrmToolBox.AsynchronousSolutionImport
{
public partial class AsyncSolutionImportPluginControl : PluginControlBase
{
public AsyncSolutionImportPluginControl()
{
InitializeComponent();
}
}
}
Tekrar design kısmına gelip bir buton ekleyip pluginimizi XrmToolBox içinde test edelim.

3.Plugin Test/Debug
Butonumuzu eklediğimize göre artık Pluginimizi test edebiliriz. Bunun için iki yol var. Birincisi pluginimizi XrmToolBox içerisine kopyalayarak test etmek. Diğeri ise visual studio içerisinde test etmek. İlk olarak XrmToolBox içerisinde test edelim. Önce projemizi derliyoruz ve projemizin Bin>debug dizinininden projemizin dll dosyasını kopyalıyoruz.

Kopyaladığımız dll dosyasını şu dizine kopyalayacağız: C:\Users\<username>\AppData\Roaming\MscrmTools\XrmToolBox\Plugins
Not: Eğer böyle bir dizin yoksa XrmToolBox aracını bilgisayarınız indirip bir kere çalıştırın.
Şimdi XrmToolBox aracımızı açıyoruz. Plugins sekmesinde oluşturduğumuz plugin otomatik olarak aşağıdaki resimdeki gibi gözükecektir.


Şimdi de diğer yöntem olan Visual Studio içerisinde test edelim. Bunun için projemize sağ tıklayıp Build Events>Post Build Event command line kısmına aşağıdaki kodları yazıyoruz:
IF NOT EXIST Plugins mkdir Plugins
move /Y $(TargetFileName) Plugins
Aynı ekranda Debug>Start External Program kısmında projenizin bin/debug dizinindeki XrmToolBox.exe dosyasını seçiyoruz. Bende bu dizin şu şekilde “C:\Users\fuat\source\repos\XrmToolBox.AsynchronousSolutionImport\XrmToolBox.AsynchronousSolutionImport\bin\Debug\XrmToolBox.exe”
Command Line Arguments kısmına ise “/overridepath:.” yazıyoruz.
Not: Debug dizinindeki XrmToolBox.exe dosyası XrmToolBoxPackage yüklediğimiz zaman geliyor.

Son olarak projeye eklememiz gereken 2 tane dll (Microsoft.Xrm.Tooling.CrmConnectControl.dll, Microsoft.Xrm.Tooling.Ui.Styles.dll) mevcut. Bu 2 dll SDK ile geliyor(SDK yı nasıl indireceğinizi bilmiyorsanız buradaki yazıyı okuyabilirsiniz. Bu iki DLL Sdk içerisinde PackageDeployment klasöründe bulunuyor).
Not: Bu iki dll dosyasını projeye eklemesseniz aşağıdaki gibi bir hata alabilirsiniz.

Dll dosyalarını eklediğimize göre artık uygulamamızı çalıştırabiliriz. Uygulamamızı çalıştırınca XrmToolBox pluginin kendiliğinden açıldığını göreceksiniz. Bu XrmToolBox da birtek bizim pluginimiz mevcut. Artık plugin içerisinde yaptığınız geliştirmeleri gönül rahatlığı ile test edebiliriz.
Not: UserControl nesnemize ToolBox içerisinden OpenFile ekleyip asenkron solution import eden kod bloğunu aşağıda paylaştım. Siz kendi isteğinize göre bu kodları değiştirebilirsiniz.
using Microsoft.Crm.Sdk.Messages;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Messages;
using Microsoft.Xrm.Sdk.Query;
using System;
using System.IO;
using System.Windows.Forms;
using XrmToolBox.Extensibility;
namespace XrmToolBox.AsynchronousSolutionImport
{
public partial class AsyncSolutionImportPluginControl : PluginControlBase
{
public AsyncSolutionImportPluginControl()
{
InitializeComponent();
}
private void AsyncSolutionImportPluginControl_Load(object sender, EventArgs e)
{
ShowInfoNotification("This is a notification that can lead to XrmToolBox repository", new Uri("https://github.com/MscrmTools/XrmToolBox"));
}
private void BtnClose_Click(object sender, EventArgs e)
{
CloseTool();
}
private void GetContacts()
{
WorkAsync(new WorkAsyncInfo
{
Message = "Getting accounts",
Work = (worker, args) =>
{
args.Result = Service.RetrieveMultiple(new QueryExpression("contact")
{
TopCount = 50
});
},
PostWorkCallBack = (args) =>
{
if (args.Error != null)
{
MessageBox.Show(args.Error.ToString(), "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
var result = args.Result as EntityCollection;
if (result != null)
{
MessageBox.Show($"Found {result.Entities.Count} contact");
}
}
});
}
private void BtnGetContacts_Click(object sender, EventArgs e)
{
ExecuteMethod(GetContacts);
}
OpenFileDialog openFileDialog = new OpenFileDialog();
private void BtnSelectSolution_Click(object sender, EventArgs e)
{
openFileDialog.Filter = "Zip Files|*.zip";
openFileDialog.Title = "Please select the solution zip file";
if (openFileDialog.ShowDialog()==DialogResult.OK)
{
string filePath = openFileDialog.FileName;
string fileName = openFileDialog.SafeFileName;
ExecuteAsyncRequest importRequest = PrepareImportRequest(filePath);
RunImportRequest(importRequest);
}
}
private void RunImportRequest(ExecuteAsyncRequest request)
{
WorkAsync(new WorkAsyncInfo
{
Message = "Uploading solution",
Work = (worker, args) =>
{
args.Result = (ExecuteAsyncResponse)Service.Execute(request);
},
PostWorkCallBack = (args) =>
{
if (args.Error != null)
{
MessageBox.Show(args.Error.ToString(), "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
var result = args.Result;
if (result != null)
{
MessageBox.Show("Success");
}
}
});
}
private static ExecuteAsyncRequest PrepareImportRequest(string filePath)
{
ExecuteAsyncRequest request = new ExecuteAsyncRequest
{
Request = new ImportSolutionRequest
{
CustomizationFile = File.ReadAllBytes(filePath),
PublishWorkflows = true,
OverwriteUnmanagedCustomizations = true,
}
};
return request;
}
}}
4.Nuget paketi oluşturma
Öncelikle projemize sağ tıklayıp AssemblyInformation kısmını aşağıdaki gibi dolduruyoruz.

Daha sonra proje dosyamızı metin editörü ile açıp Debug yazan yere Release yazıyoruz.

Şimdi Nuget paketimizin yapılandırma dosyasını oluşturmamız gerekiyor. Bunun için Nuget https://www.nuget.org adresine girip nuget.exe dosyasını indirmemiz gerekiyor.

Nuget.exe dosyasını indirdikten sonra proje dizinimize kopyalıyoruz.

Proje dizinimizde komut satırını açıp “nuget spec XrmToolBox.AsynchronousSolutionImport” kodu çalıştırıyoruz. Bu kod bize uzantısı .nuspec ile biten bir dosya oluşturacak. Bu dosya Nuget paketimizi oluştururken ihtiyacımız olan gerekli yapılandırmaları içerecek.


Bu yapılandırma dosyasını pluginimize göre düzenliyoruz. Ve projemizi Release moduna alıyoruz.

Nuget paketimizi oluşturmak için artık tek yapmamız gereken komut satırına “nuget pack” yazmak.

Ve paketimiz aynı dizinde AsynchronousSolutionImport.1.0.0.1.nupkg ismi ile oluştu.

Oluşturduğumuz paketi nuget.org adresine giriş yapıp yüklemeliyiz.


5.XrmToolBox plugin store içerisine ekleme
https://www.xrmtoolbox.com adresine giriş yapıyoruz. Daha sonra plugins sekmesinden “Add my plugin” diyoruz. Karşımıza gelen sayfadaki kutucuğa pluginimizin nuget id sini yazmamız gerekiyor. “AsynchronousSolutionImport” yazıp submit diyorum.

Artık pluginimizi XrmToolBox pluginlerinin arasına göndermiş olduk. Pluginimiz validasyon işlemlerinden geçtikten sonra XrmToolBox marketinde yayınlanacak.
Not: Nuget marketinde paketimizin yayınlanması için yapılandırma dosyasının detaylı bir şekilde doldurulması gerekmekte.
Plugin Projemizin Github Adresi:
https://github.com/fuattatar/AsynchronousSolutionImport
Yararlanılan Kaynaklar:
- https://www.xrmtoolbox.com/documentation/
- https://www.youtube.com/watch?v=8KdnrRm8jTA
- https://www.c-sharpcorner.com/article/how-to-create-plugins-for-xrmtoolbox/
- https://prashantmayur.wordpress.com/2018/08/01/create-xrm-toolboxplugin-part-1-set-up-visual-studio-and-create-a-basic-plugin/
- https://github.com/MscrmTools/XrmToolBox/wiki/Develop-your-own-custom-plugin-for-XrmToolBox
- https://prashantmayur.wordpress.com/2018/08/01/create-xrm-toolboxplugin-part-1-set-up-visual-studio-and-create-a-basic-plugin/