cub-e.net

just coding...

CRM 2011’de Create/Update/Delete Metodlarına Farklı Bir Bakış

Dynamics CRM'in iş dünyasının parlayan yıldızı olan ve her türlü iş platfomunun temeline yerleştirebilecek bir mimaride olduğunu her fırsatta anlatmaya çalışıyorum. Bu mimari üzerinde bizim kod geliştirmemizi ve sistem ile entegre olmamızı sağlayan web servisleri ve dll'ler vasıtasıyla yazma ve okuma işlemlerimizi gerçekleştirmekteyiz. Bugün sizlere daha önceden eski versiyonlar için de bahsetmiş olduğum oluşturma,  güncelleme ve silme metodlarının yapısını tekrar anlatacağım ama sonunda bunları Process isimli bir class'da birleştirip tek bir yerden yönetilmelerini sağlayacağız.

CRM servis nesnesi bize kayıtlar üzerinde işlem yapma özelliği sağlamaktadır. Servis içinden yapmak istediğimiz harekete uygun metodu çağırmamız gerekmektedir.
  Tabii burada unutulmaması gereken konu servisi çağıran kullanıcının çağrılan metodda işlem yapmaya yetkili olması gerekmektedir.

Create Metodu CRM 2011 içerisinde bir entity içerisinde yeni bir nesne oluşturmamıza olanak tanır. Metod parametre olarak entity türünden bir nesne alır ve yeni oluşturulmuş nesnenin GUID türünden değerini geri döndürür.

Aşağıdaki örnek bu metodun late-bound sınıfla kullanımını göstermektedir.

// Entity nesnesinin yeni bir instance’ini olusturuyoruz
Entity account = new Entity("account");
// Gerekli attribute’lara atama yapiyoruz.
account["name"] = "Örnek Firma";
// Örnek Firma adında bir firma karti olusturuyoruz.
_accountId = ioService.Create(account); 

Aşağıdaki örnek bu metodun early-bound sınıfla kullanımını göstermektedir.

Contact contact = new Contact()
{
    FirstName="Deneme",
    LastName="Kisisi",
    Address1_City="İstanbul",
};
Guid contactGuid =_service.Create(contact);

Update Metodu CRM 2011 içerisinde bir entity içerisinde bir nesneyi güncellememizi sağlar. Metod parametre olarak entity türünden bir nesne alır. Güncellenecek nesnenin id’si mutlaka parametre olarak verilmelidir.

Guid gContact = new Guid("7bE545CCD3-9A3A-E011-BA8B-78E7D1623F9D");
 
Contact contact = new Contact()
{
     ContactId = gContact,
     FirstName="Test",
     LastName="Kisisi",
     Address1_City="Ankara",
};
_service.Update(contact);

Delete Metodu CRM 2011 içerisinde Id’sini verdiğiniz bir nesneyi sistemden silmeye yarar. Metod parametre olarak silinecek nesnenin Id’si yanında bu nesnenin türünü ister.

Guid gContact = "7bE545CCD3-9A3A-E011-BA8B-78E7D1623F9D";
_service.delete("contact", gContact); 

Bir programcı olarak çok standart olan bu işlemleri isterseniz bir class mantığı altında birleştirelim. Bu sayede daha yönetilebilir bir CRUD (Create, Read, Update, Delete) yapısı oluşturabiliriz. Ben bu sınıf için Process adını kullandım ve Process sınıfı içerisinde şu anda Oluşturma, Güncelleme ve Silme işlerimi yapmaktayım. İlerleyen makalelerde Okuma yapılarını incelerken onları da bu sınıfa dâhil ederiz.

Burada öncelikle açıklamalıyım ki bir önceki makalede yer alan Singleton tasarım deseniyle CRM servisini oluşturma yazımdaki class’tan faydalanarak servisi çağırma işlemini gerçekleştirdim. O yazıyı okumak isterseniz buraya tıklayınız

Ek olarak hata olaylarını kontrol etmek için Result isimli bir class kullandım bu class’ın içeriği şu şekilde;

public class Result
    {
        public string Message { get; set; }
        public bool isError { get; set; }
        public Object BusinessObject { get; set; }
 
        public Result(string _Message, bool _isError, Object _BusinessObject)
        {
            Message = _Message;
            isError = _isError;
            BusinessObject = _BusinessObject;
        }
 
        public Result(string _Message, bool _isError)
        {
            Message = _Message;
            isError = _isError;
            BusinessObject = null;
        }
    }

Görüldüğü üzere çok basit bir class bize sonucun başarılı mı başarısız mı olduğunu döndürecek o kadar.

Bunun haricinde bir de Base isimli bir class’ım var ki Process sınıfını aslında bu class’dan türetmekteyim. Şu anda bu class’ı sadece ErrorNumber ve ErrorDetail gibi hata oluştuğunda bilgi almamızı sağlayacak iki property ile kullanmaktayım ama ileride farklı propertyler de eklenecek.

public class Base
{
    public int ErrorCode { get; set; }
    public string ErrorDetail { get; set; }
} 

Bu iki class’ımı açıkladıktan sonra asıl yapıda kullanacağımız metodlara gelelim. İlk önce create metodunu inceleyelim. Hatırlayacağınız üzere CRM servisinde Create metdonun Update metodundan tek farkı id’ye ihtiyaç duymamasıydı. Çünkü bu metod id’yi üretip bize geri döndürecek. Ben de kurguyu buna göre planladım.

public Result Create(Entity EntityForCreate)
       {
            try
            {
                Service service = Service.GetService();
 
                if (service == null)
                    throw new Exception("Service is null");
                if (service != null && service.ErrorCode > 0)
                    throw new Exception(service.ErrorDetail);
 
                Guid EntityID = service.OrganizationService.Create(EntityForCreate);
 
                DetailedLog.CreateLog("Entity Created! Type: " + EntityForCreate.LogicalName + ", ID : " + EntityID, 
                    System.Diagnostics.EventLogEntryType.Information);
 
                return new Result("", false, EntityID);
            }
            catch (Exception ex)
            {
                ErrorCode = 100;
                ErrorDetail = ExceptionHandler.HandleException(ex);
                DetailedLog.CreateLog(System.Reflection.Assembly.GetExecutingAssembly().GetName().Name
                    + " : " + System.Reflection.MethodBase.GetCurrentMethod().ToString()
                    + " : " + ErrorDetail, System.Diagnostics.EventLogEntryType.Error);
 
                return new Result(ErrorDetail, true);
            }
        }

Aslında yapı çok basit servis nesnemi çağırıyorum, servis oluşurken hata var mı diye bakıyorum eğer hata varsa hatayı fırlatıyorum. (bu noktada şunu belirtmeliyim ki catch bölümünde yer alan DetailLog ve ExceptionHandler benim daha önceden yazmış olduğum hata yönetimi ile ilgili class’lar catch bölümünü siz de istediğiniz gibi düzenleyebilirsiniz) Eğer hata yok ise OrganizationService metodumu kullanarak nesnemi oluşturuyorum. Bu noktada da bir hata yoksa servis bana oluşturduğu nesnenin id’sini döndürüyor yok eğer hata varsa zaten kod catch bloğuna düşüyor. İşte bu kadar.

Şimdi gelin diğer metodlara bir göz atalım;

public Result Update(Entity EntityForUpdate, Guid EntityID)
        {
            try
            {
                Service service = Service.GetService();
 
                if (service == null)
                    throw new Exception("Service is null");
                if (service != null && service.ErrorCode > 0)
                    throw new Exception(service.ErrorDetail);
 
                service.OrganizationService.Update(EntityForUpdate);
 
                DetailedLog.CreateLog("Entity Updated! Type: " + EntityForUpdate.LogicalName + ", ID : " + EntityID,
                    System.Diagnostics.EventLogEntryType.Information);
 
                return new Result("", false);
            }
            catch (Exception ex)
            {
                ErrorCode = 100;
                ErrorDetail = ExceptionHandler.HandleException(ex);
                DetailedLog.CreateLog(System.Reflection.Assembly.GetExecutingAssembly().GetName().Name
                    + " : " + System.Reflection.MethodBase.GetCurrentMethod().ToString()
                    + " : " + ErrorDetail, System.Diagnostics.EventLogEntryType.Error);
 
                return new Result(ErrorDetail, true);
            }
        }

Update metodu neredeyse Create metodunun aynısı tek fark daha önce de ifade ettiğim gibi güncellenecek nesnenin id’sini alması. Bunu almalı ki neyi güncellediğini bilebilsin. Aslında burada şöyle bir mantıksal kargaşa var update metodu ek olarak id’yi almamakta zaten sizin ona update edilmesi için vereceğiniz entity’nin Id alanına vermelisiniz. Benim burada ek olarak almamdaki amaç onu loglamak için.

Bir de Delete metoduna göz atalım;

public Result Delete(string EntityName, Guid EntityID)
        {
            try
            {
                Service service = Service.GetService();
 
                if (service == null)
                    throw new Exception("Service is null");
                if (service != null && service.ErrorCode > 0)
                    throw new Exception(service.ErrorDetail);
 
                service.OrganizationService.Delete(EntityName, EntityID);
 
                DetailedLog.CreateLog("Entity Created! Type: " + EntityName + ", ID : " + EntityID,
                    System.Diagnostics.EventLogEntryType.Information);
 
                return new Result("", false);
            }
            catch (Exception ex)
            {
                ErrorCode = 100;
                ErrorDetail = ExceptionHandler.HandleException(ex);
                DetailedLog.CreateLog(System.Reflection.Assembly.GetExecutingAssembly().GetName().Name
                    + " : " + System.Reflection.MethodBase.GetCurrentMethod().ToString()
                    + " : " + ErrorDetail, System.Diagnostics.EventLogEntryType.Error);
 
                return new Result(ErrorDetail, true, null);
            }
        }

Delete metodu entity’nin adını ve id’sini almakta. Bu iki veriyi de servise vermekteyiz.

Class’ın tamamına bakacak olursak;

public class Process : Base
    {
        public Result Create(Entity EntityForCreate)
        {
            try
            {
                Service service = Service.GetService();
 
                if (service == null)
                    throw new Exception("Service is null");
                if (service != null && service.ErrorCode > 0)
                    throw new Exception(service.ErrorDetail);
 
                Guid EntityID = service.OrganizationService.Create(EntityForCreate);
 
                DetailedLog.CreateLog("Entity Created! Type: " + EntityForCreate.LogicalName + ", ID : " + EntityID, 
                    System.Diagnostics.EventLogEntryType.Information);
 
                return new Result("", false, EntityID);
            }
            catch (Exception ex)
            {
                ErrorCode = 100;
                ErrorDetail = ExceptionHandler.HandleException(ex);
                DetailedLog.CreateLog(System.Reflection.Assembly.GetExecutingAssembly().GetName().Name
                    + " : " + System.Reflection.MethodBase.GetCurrentMethod().ToString()
                    + " : " + ErrorDetail, System.Diagnostics.EventLogEntryType.Error);
 
                return new Result(ErrorDetail, true);
            }
        }
 
        public Result Update(Entity EntityForUpdate, Guid EntityID)
        {
            try
            {
                Service service = Service.GetService();
 
                if (service == null)
                    throw new Exception("Service is null");
                if (service != null && service.ErrorCode > 0)
                    throw new Exception(service.ErrorDetail);
                
                service.OrganizationService.Update(EntityForUpdate);
 
                DetailedLog.CreateLog("Entity Updated! Type: " + EntityForUpdate.LogicalName + ", ID : " + EntityID,
                    System.Diagnostics.EventLogEntryType.Information);
 
                return new Result("", false);
            }
            catch (Exception ex)
            {
                ErrorCode = 100;
                ErrorDetail = ExceptionHandler.HandleException(ex);
                DetailedLog.CreateLog(System.Reflection.Assembly.GetExecutingAssembly().GetName().Name
                    + " : " + System.Reflection.MethodBase.GetCurrentMethod().ToString()
                    + " : " + ErrorDetail, System.Diagnostics.EventLogEntryType.Error);
 
                return new Result(ErrorDetail, true);
            }
        }
 
        public Result Delete(string EntityName, Guid EntityID)
        {
            try
            {
                Service service = Service.GetService();
 
                if (service == null)
                    throw new Exception("Service is null");
                if (service != null && service.ErrorCode > 0)
                    throw new Exception(service.ErrorDetail);
 
                service.OrganizationService.Delete(EntityName, EntityID);
 
                DetailedLog.CreateLog("Entity Created! Type: " + EntityName + ", ID : " + EntityID,
                    System.Diagnostics.EventLogEntryType.Information);
 
                return new Result("", false);
            }
            catch (Exception ex)
            {
                ErrorCode = 100;
                ErrorDetail = ExceptionHandler.HandleException(ex);
                DetailedLog.CreateLog(System.Reflection.Assembly.GetExecutingAssembly().GetName().Name
                    + " : " + System.Reflection.MethodBase.GetCurrentMethod().ToString()
                    + " : " + ErrorDetail, System.Diagnostics.EventLogEntryType.Error);
 
                return new Result(ErrorDetail, true, null);
            }
        }
    }

Class’ımızı bu şekilde oluşturduktan sonra gelin onu bir konsol uygulaması yardımıyla çağıralım ve iş başında görelim.

static void Main(string[] args)
        {
            try
            {
                Entity lead = new Entity("lead");
                lead.Attributes["subject"] = "Fuardan Gelenler";
                lead.Attributes["firstname"] = "Barış";
                lead.Attributes["lastname"] = "KANLICA";
                lead.Attributes["companyname"] = "Omerd Business Solutions";
 
                Process process = new Process();
                Result result = process.Create(lead);
                if (result.isError)
                    throw new Exception(result.Message);
 
                Console.WriteLine("Lead created : " + result.BusinessObject.ToString());
                Console.ReadLine();
            }
            catch (Exception ex)
            {
                Console.WriteLine("Error : " + ex.Message);
                Console.ReadLine();
            }
        }

 

Gördüğünüz üzere Entity nesnemi çağıyor ve ona lead adını veriyorum. İçerisi istediğim bilgiler ile dolduruyor ve Process class’ımın içindeki Create metodu vasıtasıyla CRM içerisinde oluşmasını sağlıyorum. İşte hepsi bu kadar :) 

Bütün bu işlemler sonunda tek bir noktadan yönetilebilir bir CRUD mimarisi yavaş yavaş ortaya çıkmakta. Sıra Okuma işlemlerinin detaylarına inmekte. Onu da artık başka bir makalede inceleyeceğiz.

Microsoft Dynamics CRM 2013 'de FetchXML ile Aggregate(Toplama Sorguları) İşlemleri

Bir önceki makalede FetchXML ile neler yapabileceğimize değindik. (Buraya tıklayarak o makaleye ulaşabilirsiniz) Eğer istersek FetchXML ile grouping ve aggregate işlemlerini de yapabilmekteyiz. FetchXML ile aşağıdaki işlemler desteklenmektedir;

·         sum

·         avg

·         min

·         max

·         count(*)

·         count(attribute name)

 

Aggregate yapabileceğimiz sorgu örneği standart olarak bildiğimiz fetchXML kurgusundan farklı değil burada sadece attribute kısmında aggregate ile ilgili keyword yer almakta. Aggregate=true diyerek bu şekilde bir sorgulama yapacağımızı sisteme söylüyoruz. Sonrasında ise sırasıyla hangi alan üzerinden işlem yapılacağını, alias yani etiketin ne olacağını ve aggregate türümüzün ne olacağını sisteme söylüyoruz.

<fetch distinct='false' mapping='logical' aggregate='true'>

  <entity name='opportunity'>

    <attribute name='name' alias='opportunity_count' aggregate='count'/>

  </entity>

</fetch>

Bu şekilde oluşturduğumuz bir sorgu ile aslında şunu demek istiyoruz : “select count('name') as opportunity_count from opportunity” yani standart bir count işleminden fazlası değil yaptığımız.

 

Bu fetchXML’i çalıştırmak için ise aşağıdaki gibi bir kod hazırladım;

 

string opportunity_count = @" <fetch distinct='false' mapping='logical' aggregate='true'>

                                                <entity name='opportunity'>

                                                    <attribute name='name' alias='opportunity_count' aggregate='count'/>

                                                </entity>

                                              </fetch>";

 

                EntityCollection opportunity_count_result = ServiseBaglan().RetrieveMultiple(new FetchExpression(opportunity_count));

 

                foreach (var c in opportunity_count_result.Entities)

                {

                    Int32 aggregate2 = (Int32)((AliasedValue)c["opportunity_count"]).Value;

                    System.Console.WriteLine("Count of all opportunities: " + aggregate2);

                }

 

Burada dikkat edilmesi gereken nokta geriye EntityCollection döndürmesi. Bir önceki makalemden hatırlarsanız normalde bu tarz sorgulama ile biz geriye account, contact gibi sitem içerisindeki nesnelerden oluşmuş bir dizi alıyoruz. FetchXML yapısı gereği tek bir metod kullanıyoruz ve bu metod sonucunda yukarıdaki örnekte olduğu gibi tek bir satır dahi dönecek olsa bile yine sonucu bir collection içerisinde almaktayız. Bu nedenle mecbur foreach içerisinde dönüyoruz ya da collection’un 0. üyesini alıyoruz. Ben örneğimde foreach ile collection içinde döndüm ve gördüğünüz gibi değeri aldım.

 

Yine bir önceki makalemde ileri düzey sorgular bölümünde anlattığım gibi eğer nesne üzerinden direkt bir değer döndürmeyeceksek sonuç her zaman AliasedValue olarak gelmekte. işte bu nedenden dolayı şu şekilde bir convert işlemi yapıyoruz;

 

Int32 aggregate2 = (Int32)((AliasedValue)c["opportunity_count"]).Value

 

Bu işlem sonucunda bu koddan aşağıdaki gibi çıktımızı alabiliyoruz.

 

 

Eğer distinct yapmak istersek de fetchXML içerisine yerleştireceğimiz bir distinct=”true” ifadesiyle ile sisteme bunu beliretebiliriz.

Aşağıda ise distinct yapılmış bir kod örneği bulunmakta;

 

<fetch distinct='false' mapping='logical' aggregate='true'>

  <entity name='opportunity'>

    <attribute name='name' alias='opportunity_distcount' aggregate='countcolumn' distinct='true'/>

  </entity>

</fetch>

 

Bu FetxhXML’i de aşağıdaki kodla çalıştırabiliriz.

 

                string opportunity_distcount = @"

                                           <fetch distinct='false' mapping='logical' aggregate='true'>

                                               <entity name='opportunity'>

                                                  <attribute name='name' alias='opportunity_distcount' aggregate='countcolumn' distinct='true'/>

                                               </entity>

                                           </fetch>";

 

                EntityCollection opportunity_distcount_result = ServiseBaglan().RetrieveMultiple(new FetchExpression(opportunity_distcount));

 

                foreach (var c in opportunity_distcount_result.Entities)

                {

                    Int32 aggregate4 = (Int32)((AliasedValue)c["opportunity_distcount"]).Value;

                    System.Console.WriteLine("Distinct name count of all opportunities: " + aggregate4);

                }

 

                Console.ReadLine();

 

Bu kod sonucunda da aşağıdaki gibi bir cevap alabiliriz sistemden;


 

Gelin olayı biraz renklendirelim. Bu sefer de bir sorgu içerisinde birden fazla aggregate seçeneğinin cevabını isteyelim. Böyle bir işlem için standart fetchXML’in içerisinde sırasıyla hangi özelliklerin bize geri döneceğini belirtiyoruz tabii unutulmaması gereken nokta ise hepsi için ayrı birer alias olması gerektiğidir.

 

<fetch distinct='false' mapping='logical' aggregate='true'>

  <entity name='opportunity'>

    <attribute name='opportunityid' alias='opportunity_count' aggregate='count'/>

    <attribute name='estimatedvalue' alias='estimatedvalue_sum' aggregate='sum'/>

    <attribute name='estimatedvalue' alias='estimatedvalue_avg' aggregate='avg'/>

  </entity>

</fetch>

 

Bu sorgu için ise aşağıdaki kodu hazırladım;

 

                string estimatedvalue_avg2 = @"

                                    <fetch distinct='false' mapping='logical' aggregate='true'>

                                        <entity name='opportunity'>

                                           <attribute name='opportunityid' alias='opportunity_count' aggregate='count'/>

                                           <attribute name='estimatedvalue' alias='estimatedvalue_sum' aggregate='sum'/>

                                           <attribute name='estimatedvalue' alias='estimatedvalue_avg' aggregate='avg'/>

                                        </entity>

                                    </fetch>";

 

                EntityCollection estimatedvalue_avg2_result = ServiseBaglan().RetrieveMultiple(new FetchExpression(estimatedvalue_avg2));

 

                foreach (var c in estimatedvalue_avg2_result.Entities)

                {

                    Int32 aggregate8a = (Int32)((AliasedValue)c["opportunity_count"]).Value;

                    System.Console.WriteLine("Count of all opportunities: " + aggregate8a);

                    decimal aggregate8b = ((Money)((AliasedValue)c["estimatedvalue_sum"]).Value).Value;

                    System.Console.WriteLine("Sum of estimated value of all opportunities: " + aggregate8b);

                    decimal aggregate8c = ((Money)((AliasedValue)c["estimatedvalue_avg"]).Value).Value;

                    System.Console.WriteLine("Average of estimated value of all opportunities: " + aggregate8c);

 

                }

 

                Console.ReadLine();

 

İşte bu şekilde aynı anda count, sum ve avg işlemlerini yapabilmekteyim. Sonuç ise aşağıdaki gibi;

 

 

Bu iki makalede FetchXML’ler üzerinde konuştuk. Umarım faydalı olmuştur.

Dynamics CRM Seminer Dizisi

Dynamics CRM 2013 versiyonu artık yayında bildiğiniz üzere, hatta 2013 üzerinde birçok proje live olmaya bile başladı bile. Peki siz Dynamics CRM nedir biliyor musunuz? Microsoft'un en çok para yatırdığı platformlardan biri olan Dynamics ailesini ne kadar tanıyorsunuz? Dynamics CRM nasıl bir yazılım platformu olarak kullanılır biliyor musunuz? işte bütün bu sorulara cevap verecek üstüne 2011 ve 2013 veriyonlarını öğreneceğiniz güzel bir web seminerleri dizisi başlatmaktayım. Aşağıda detayları bulabilirsiniz.

19 Aralık Perşembe Saat 10:00 : Dynamics CRM Nedir? Ne işe Yarar?

   Seminere giriş linkimiz : https://www.livemeeting.com/cc/mvp/join?id=MVP4025099&role=attend&pw=hq.%27SG%28c3

23 Aralık Pazartesi Saat 10:00 : Dynamics CRM 2011 ve 2013 Versiyonlarındaki Farklılıklar.

   Seminere giriş linkimiz : https://www.livemeeting.com/cc/mvp/join?id=MVP4025099&role=attend&pw=p%60dG4%40ChR 

27 Aralık Cuma Saat 10:00 : Dynamics CRM 2013 ile Yazılım Geliştirme.

Facebook event adresine bu linkten ulaşabilirsiniz : https://www.facebook.com/events/432791460180088/

 

 

Microsoft Dynamics CRM 2011 Scripting Cookbook

Bugün sizlere bir kitap tanıtacağım. Umarım sizler için faydalı olur.

"Microsoft Dynamics CRM 2011 Scripting Cookbook" okuyucuya özelleştirme mimarisi, en basit konulardan başlayarak özel alanların ve türlerin oluşturulması, formlar ile çalışma, ileri düzeyde script yazma konuları ve kodu debug etme, yeni formlar ve ribbonlar dizayn etme, script kütüphanlerinin kullanımı ve dış veri kaynakları ile entegrasyon dahil olmak üzere özelleştirme çerçevisinde her konuyu örnekleriyle açıklayacak anlatmaktadır.



Kitabı bitirdiğinizde kendi özelleştirmenizi nasıl yapacağınızı kavramış ve bunları artık nasıl bir çözüm(solution) haline getirebileceğiniz anlatılmaktadır.

Bu kitap iş analizcileri ve kod geliştiricileri için gerçekten yeterli bir kaynak. Konular gerçekten güzel bir mantık sırasında anlatılmış ve açıklanmış. Örnekler de gayet açıklayıcı ve doyurucu bir şekilde konuları pekiştirmiş.

Dynamics CRM konusunda piyasada gerçekten önemli bir yere geçecek faydalı bir eser olmuş. Nicolae Tarla'yı tebrik etmek gerekli.

Kitabın kağıt haricinde her türlü cihaz için dijital baskıları da mevcut.

Kitaba amazon.com üzerinden bu adresten ulaşabilirsiniz : http://www.amazon.com/Microsoft-Dynamics-2011-Scripting-Cookbook/dp/1849688826/ref=tmm_pap_title_0

Ayrıca yayınevi üzerinden de ulaşabilirsiniz : http://www.packtpub.com/microsoft-dynamics-crm-2011-scripting-cookbook/book

 

Dynamics CRM Web Servisinin Çağrılmasında Singleton Tasarım Desenin Kullanılması

Aslında bu makalenin büyük bir kısmını 2009 yılında yazdım ama hatırlayamadığım bir nedenden dolayı o dönemde yarım kaldı ve unutuldu gitti. Şimdi fırsat buldum ve makaleyi tamamladım. (O zamanlar CRM 4.0 vardı şimdi kodları 2011 ve 2011'den sonraki versiyonlar göre uyarladım)

Bu makalemizde, programımızın yaşam döngüsü boyunca sadece bir Dynamics CRM Web Servisi bağlantısının hafızada yer almasını garanti altına almamızı sağlayacak olan Singleton tasarım desenini tanıyacağız ve bu iş için gerekli olan kodu geliştireceğiz.

Bildiğiniz üzere Dynamics CRM 2011 diğer CRM sürümleri gibi bir web uygulaması ve CRM üzerinde kendi yazılımlarımızı geliştirmek istiyorsak onun web servislerini kullanmamız gerekmektedir. Bu durumda IIS'in performansını da düşünmek zorundayız ve uygulamalarımızı buna göre geliştirmeliyiz. Uygulamamızda her işlem sırasında CRM web servisini çağırmak hem uygulamamızın hem de IIS'in gereksiz yere şişmesine neden olmaktadır. Ayrıca CRM servisi IOrganizationService türünden bir nesne ve bizim temel amacımız bu nesnenin hafızanda birden fazla kere oluşturulmasını ve oluşturma zahmetine girilmesini engellemek. Çünkü ben bu örnekte kodların içerisine gömmüş olsam dahi gerçek dünyada projelerde bizler servise bağlanılacak kullanıcı adı ve şifre gibi bilgileri .xml dosyaları gibi dosyalarda tutup buradan okumaktayız. Bir application life cycle içerisinde bu bilgileri okumak için çok sık I/O yaptırmak çok doğru bir işlem değil.

İşte bu durumda uygulamamız çalıştığı sürece açık kalacak ve bizim komutlarımızı icra edecek bir web servisi nesnesine ihtiyacımız olacaktır. Bunu Singleton Tasarım Deseniyle (Singleton Design Pattern) bunu sağlayabiliriz.

Singleton Tasarım Desenine geçmeden önce Tasarım Desenleri'nin ne olduğunu bilmeyenler için açıklayayım. Yazılım Mühendisliğinde içerinde ele alınan Tasarım Deseni (Design Pattern) kavramı, yazılım geliştirilirken sık meydana gelen sorunlara genel ve tekrar kullanılabilir çözümler bütünü olarak tanımlanabilir. Tasarım desenleri size direkt olarak kod üretmez sadece belirli bir problemin çözümüne mimari düzeyde sınıf ve method tasarımlarınızı oluşturmanızı sağlar. Herhangi bir kod parçacığı içermediği için programlama dilinden bağımsızdırlar. Kısaca Design Pattern koda çevrilebilecek tamamlanmış bir tasarım değildir, sorunun nasıl çözüleceğine dair bir modeldir. Tasarım desenleri test edilmiş, onaylanmış modeller sağlayarak yazılım geliştirme sürecini hızlandırabilirler. Tasarım Desenleri 3 kola ayrılırlar.

  • Creational Patterns (Yaratımsal Desenler) : nesnelerin ve sınıfların oluşturulması ile ilgili tasarım desenlerini içerir
  • Behavioral Patterns (Davranışlar Desenler) : sınıfların yapısal özelliklerinin belirlenmesi ile ilgili tasarım desenlerini içerir
  • Structural Patterns (Yapısal Desenler) : nesnelerin davranışsal özelliklerini ve bu davranışlara göre durumlarını yöneten tasarım desenlerini içerir

 

Yazılım geliştiriciler bazen programlarında özel olarak tasarım desenlerini kullanmasalarda aslında onlarda benzer yapılar kurarlar. Zaten tasarım desenlerine baktıkları zaman, "ya zaten ben bunu kullanıyordum.." gibi cümleleri de duymak mümkündür. Eminim benim burada anlatmaya çalışacağım Singleton Tasarım Desenini ve bu desendeki gibi CRM Web Servisine bağlanmayı yapmış arkadaşlar mutlaka vardır.

Şimdi "Creatinal Patterns" grubunda bulunan "Singleton" desenini açıklamaya başlayabiliriz. Singleton deseni bir programın yaşam süresince belirli bir nesneden sadece bir örneğinin olmasını sağlar. Bir uygulamada, tek bir yerden bu nesneye ulaşımın olması isteniyorsa bu desen kullanılabilir. Hatırlayacağınız üzere bir sınıftan yeni bir nesne oluşturmak için varsayılan yapıcı metodu(default constructor) çağırmak gerekir. C# dilinde bu işlemi new sözcüğünü kullarak yapmaktayız.

SınıfAdı olustrulacakNesne = new SınıfAdı();  //Normalde olması gerekn çağırma şekli

Bu şekilde yeni bir nesne oluşturmak için new anahtar sözcüğünün temsil ettiği yapıcı metoduna dışarıdan erişimin olması gerekir. Yani yapıcı metodun public olarak bildirilmiş olması gerekir. Ancak "Singleton" desenine göre belirli bir anda sadece bir nesne olabileceği için new anahtar sözcüğünün ilgili sınıf için yasaklanması gerekir yani yapıcı metodun protected ya da private olarak bildirilmesi gerekir. Eğer bir metodun varsayılan yapıcı metodu(default constructor- parametresiz yapıcı metot) public olarak bildirilmemişse ilgili sınıf türünden herhangi bir nesnenin sınıfın dışında tanımlanması mümkün değildir. Ancak bizim isteğimiz yalnızca bir nesnenin yaratılması olduğuna göre ilgili sınıfın içinde bir yerde nesnenin oluşturulması gerekir. Bunu elbette statik bir özellik(property) ya da statik bir metotla yapacağız. Bu statik metot sınıfın kendi içinde yaratılan nesneyi geri dönüş değeri olarak bize gönderecektir. Peki, bu nesne nerede ve ne zaman yaratılacaktır? Bu nesne statik metodun ya da özelliğin içinde yaratılıp yine sınıfın private olan elemanına atanır. Tekil olarak yaratılan bu nesne her istendiğinde eğer nesne zaten yaratılmışsa bu private olan elemanın referasına geri dönmek ya da nesneyi yaratıp bu private değişkene atamak gerekmektedir.

Ama bizim yapacağımız şu şekilde olacaktır:
SınıfAdı olusturulacakNesne = SınıfAdı.MetodAdı(); //Standart bir static class çağırma şekli

Temel Uml görüntümüz ise şöyle olacaktır:

Ama ben bu yapıya ek birkaç şey daha ekleyerek aşağıdaki şekilde bir sınıf hazırladım.

Temelde ihtiyacım olan property’leri kod bloğunun üst kısmında aşağıda görüldüğü gibi oluşturuyorum.

private static Service _serviceInstance;
private IOrganizationService _organizationService;
private Guid _controlID;
private static object _lockObject = new object();

public IOrganizationService OrganizationService
{
    get { return _organizationService; }
}

public Guid ControlID
{
    get { return _controlID; }
}

Burada Service türünden oluşan _serviceInstance isimli property aslında bütün işi yapan arkadaş. Biz onun dolu ya da boş olduğuna yani null olup olmamasına bakarak hareket edeceğiz. IOrganizationService daha önce de ifade ettiğim gibi Dynamics CRM’in servis nesnesi. Amacımız onun oluşturulma aşamasının tekrar tekrar çağrılmasını engellemek. ControlID sistemde oluşan instance’ların aynı id’ye sahip olup olmadıklarına bakarak onların kaç kere oluşturulduklarını gözlemlediğimiz değişken. lockObject ise tamamen ilerisi için düşünülmüş ve multithread mantıkta sıkıntı yaşamamızı engellemek için konmuş bir değişken.

 

Temel ihtiyaçlar bölümünden sonra asıl işimi yapacak olan kod öbeğini doğru yere yerleştirme işlemi bulunmakta. Aşağıdaki blok benim her zaman için bir kere çalıştırmak isteyeceğim blok işte bu bloğu Singleton tasarım deseni içerisinde private olarak ayarlanmış Constructor metodumun içine yerleştiriyorum.

ClientCredentials clntCredentials = new ClientCredentials();
clntCredentials.Windows.ClientCredential = new System.Net.NetworkCredential("kullanıcı adı", "şifre", "domain");
Uri orgUri = new Uri("http://crm url/Organizasyon Adı/XRMServices/2011/Organization.svc");
OrganizationServiceProxy orgService = new OrganizationServiceProxy(orgUri, null, clntCredentials, null);
_organizationService = (IOrganizationService)orgService;

Daha sonra static olarak çağrılacak ve mevcut servisimin hafıza daha önceden oluşturulup oluşturulmadığını kontrol edecek yapıya geliyor sıra. Onun için de kodun üst kısmında tanımlamış olduğum property’ler vasıtasıyla kontrollerimi yapıyorum.

        public static Service GetService()
        {
            try
            {
                if (_serviceInstance == null)
                {
                    lock (_lockObject)
                    {
                        if (_serviceInstance == null)
                            _serviceInstance = new Service(Guid.NewGuid()); 
                    }
                }

                return _serviceInstance;
            }

 

Burada şunu belirtmeliyim ki lock bölümüne aslında normal şartlar altında ihtiyacım bulunmamakta o bölüm ileride bu sistemi multithread olarak kullanırsam servisinin ayrı instance’lar tarafından tekrar oluşturulmasını engellemek için konan bir kod bloğu.

Ve hepsini bir araya topladığımızda aşağıdaki kod bloğu karşımıza çıkmakta.

    class Service
    {
        private static Service _serviceInstance;
        private IOrganizationService _organizationService;
        private Guid _controlID;
        private static object _lockObject = new object();

        public IOrganizationService OrganizationService
        {
            get { return _organizationService; }
        }

        public Guid ControlID
        {
            get { return _controlID; }
        }
        
        private Service(Guid controlID)
        {
            ClientCredentials clntCredentials = new ClientCredentials();
            clntCredentials.Windows.ClientCredential = new System.Net.NetworkCredential("kullanıcı adı", "şifre", "domain");
            Uri orgUri = new Uri("http://crm url/Organizasyon Adı/XRMServices/2011/Organization.svc");
            OrganizationServiceProxy orgService = new OrganizationServiceProxy(orgUri, null, clntCredentials, null);
            _organizationService = (IOrganizationService)orgService;
            _controlID = controlID;
        }

        public static Service GetService()
        {
            try
            {
                if (_serviceInstance == null)
                {
                    lock (_lockObject)
                    {
                        if (_serviceInstance == null)
                            _serviceInstance = new Service(Guid.NewGuid()); 
                    }
                }

                return _serviceInstance;
            }
            catch (Exception ex)
            {
                string ErrorDetail = ExceptionHandler.HandleException(ex);
                DetailedLog.CreateLog(System.Reflection.Assembly.GetExecutingAssembly().GetName().Name
                    + " : " + System.Reflection.MethodBase.GetCurrentMethod().ToString()
                    + " : " + ErrorDetail, System.Diagnostics.EventLogEntryType.Error);

                return null;
            }
        }
    } 

İşte bu kadar herşey hazırlandı. Şimdi bu kodu çağıracak olan kodu hazırlamaya geldi sıra. Bunun için de bir konsol uygulaması oluşturuyorum ve aşağıdaki kodları ekliyorum.

            Service service1 = Service.GetService();
            Console.WriteLine(service1.ControlID);

            Service service2 = Service.GetService();
            Console.WriteLine(service2.ControlID);

            Entity lead = new Entity("lead");
            lead.Attributes["subject"] = "Fuardan Gelenler";
            lead.Attributes["firstname"] = "Barış";
            lead.Attributes["lastname"] = "KANLICA";
            lead.Attributes["companyname"] = "Omerd Business Solutions";

            service1.OrganizationService.Create(lead);

            Console.WriteLine("Lead created!!");
            Console.ReadLine();

Burada yaptığımız temel işlem Service türünden oluşturduğumuz nesnenin içindeki CRM’in OrganizationService nesnesine ulaşmak. Aşağıdaki ekran çıktısında da göreceğiniz üzere servis sadece bir kere oluşmakta. (Nereden mi biliyorum bakın iki service nesnesi de aynı ControlID değerini Guid olarak döndürmekte)

Eğer isterseniz başka classlar içerisinde de service nesnelerinin çağırabilirsiniz her seferinde tek bir oluşturma işlemi olacak ve sistem kaynaklarını daha verimli kullanmış olacağız.

Seminer Duyurusu : Microsoft Dynamics CRM Nedir?

Bir seminer vakti daha geldi. Yarın sizlere Dynamics CRM'in aşağıda sıraladığım 4(3 modül+1 genişleme) temel özelliğinin nasıl işlediğini anlatacağım. Seminer 23.11.2013 saat 10:00'da olacaktır.

Dynamics CRM 2011 kendinden önce gelen sürümlerde de olduğu gibi 3 temel modül üzerine oturmuş bulanmaktadır.

Sales Automation : Bütün satış süreçlerinizi en temel ihtiyaçlarınızdan başlayarak karmaşık yatay ve dikey satış stratejilerine varana kadar kolay ve hızlı bir biçimde yönetmenizi sağlar. Bu bölümde müşteri kavramı üzerine oturtulmuş 360 derecelik görünüm müşterinizin sizinle olan bütün temas noktalarını göz önüne serecek ve doğru zamanda doğru hamleyi yapmanız için size yol gösterecektir. 
Daha detaylı bilgiye şu linkten ulaşabilirsiniz : http://crm.dynamics.com/en-us/sales 
Video için ise bu linke bakabilirsiniz : http://uscrmdynamics.cloudapp.net/demos/Dynamics-CRM-2011-Driving-Sales-Productivity/CRM-2011-Driving-Sales-Productivity.html

Marketing : Potansiyel ve mevcut müşterilerinizi elde tutmak için hazırlanmış bu modülde pazarlama kampanyalarınızı, fuarları, pazarlama aktivitelerinizi yönetebilir bunları karşılaştırabilirsiniz. Böylece size en faydalı sonucu üreten pazarlama sürecinizi bulup bunun üzerine yoğunlaşabilirsiniz. Pazarda ürünüzün konumunu ve rakiplerle olan durumlarınızı da gözlemleyip buna göre bir sonraki hamlenizi planlayabilirsniz.

Daha detaylı bilgiye şu linkten ulaşabilirsiniz : http://crm.dynamics.com/en-us/marketing
Video için ise bu linke bakabilirsiniz : http://uscrmdynamics.cloudapp.net/demos/Dynamics-CRM-2011-Maximizing-Marketing-Impact/CRM-2011-Maximizing-Marketing-Impact.html

Customer Service : Müşterilerinize verdiğiniz servis ve hizmetleri kolayca yönetip ölçeklendirebileceğiniz bu bölüm Dynamics CRM içerisindeki birlikte çalışma özelliğini de ön plana çıkarmaktadır. Sahada gezen yada yerleşik servis hizmeti veren bütün firmaların ihtiyaçlarını karşılayacak şekilde dizayn edilmiştir. Böylece hızlı ve seri bir şekilde müşterinize hizmet sunmayı kolaylaştırabilirsiniz. 
Daha detaylı bilgiye şu linkten ulaşabilirsiniz : http://crm.dynamics.com/en-us/customer-service
Video için ise bu linke bakabilirsiniz : http://uscrmdynamics.cloudapp.net/demos/Dynamics-CRM-2011-Optimize-Customer-Experience/CRM-2011-Optimize-Customer-Experience.html

Bu bölümler haricinde bir de CRM Extended özelliğinden söz etmek gerekir. Dynamics CRM'i diğer yazılımlardan ayıran en büyük özelliği genişleyebilir bir mimariye sahip olmasıdır. Kısacası bizler CRM üzerinde iş gereksinimlerinize uygun nesneleri ya da alanları açıp bunu CRM içinde gelen bir parçaymış gibi kullanmanızı sağlayabiliyoruz. Bu özellik sayesinde CRM sadece yukarıda bahsettiğimiz işlevleri yerine getirmenin yanısıra sizin tam işinize uygun bir şekilde şekil değiştirebilmektedir. Bu özellik sayesinde Dynamics CRM'in kullanıldığı bütün firmalarda benzersiz bir hale bürünmekte ve o firmanın ihtiyaçlarına cevap verecek bir hale gelmektedir.
Extending özelliği hakkında daha fazla bilgiye bu linkten ulaşabilirsiniz : http://crm.dynamics.com/en-us/extended-crm

İşte bu özellikleri sayesinde Dynamics CRM firmanızın ihtiyaçlarına göre şekillenir ve işinizi yönetmede size yardımcı olur. Dynamics CRM 2011 en iyi iş platformudur !!!

Bahçeşehir Üniversitesi Dynamics CRM Semineri

15 Mayıs Çarşamba günü saat 10:30 - 12:30 arasında Bahçeşehir Üniversitesinde "CRM, XRM Kavramları ve Dynamics CRM" konulu bir seminer vereceğim. İçerik ise şu şekilde olacak; Dünya’da uzun yıllardır konuşulan CRM “Müşteri İlişkileri Yönetimi” kavramı müşterimizin neleri sevdiğini, neye para harcadığını ve neleri yapmaktan hoşlandığını kısacası bize müşterimizin kim olduğu gösteren temeller üzerine oturmuştur. XRM sadece müşteri değil onun haricinde dış dünyada yönettiğimiz varlıkların bize kim/ne olduğunu  göstermektedir. İşte bu seminerde Microsoft Dynamics CRM’in bu iki kavramın ortasına nasıl oturduğunu ve programatik olarak nasıl geliştirilebileceği anlatılacaktır.

Microsoft Dynamics CRM, adındaki CRM ibarisi nedeniyle sadece Müşteri İlişkileri departmanlarını ilgilendiren bir yazılım olarak anlaşılmaktadır. Aslında Dynamics CRM bir platfomdur ve üzerinde sizin şekillendirdiğimiz bir çok altyapıya destek verebilir. Örneğin aynı Dynamics CRM Ağaoğlu'nda gayrimenkul satış platformu olarak kullanılabilirken Levent'teki Sapphire İstanbul'daki evlerin yönetiminin merkezine oturtulmuştur. Yine aynı Dynamics CRM IKEA'nın Online Satış sisteminin yönetiminde kullanılırken Öztürkler için ise B2B Satış Portalı olarak da kullanılabilmektedir. Ele alınan örneklerde görüldüğü üzere Dynamics CRM tam bir iş platformudur ve esnek mimarisi sayesinde her türlü iş uygulamasının temelini oluşturabilme yeteneğine sahiptir.

Dynamics CRM ile İnşaat ve Gayrimenkul Satış Yönetimi

Yarın Microsoft'ta yapılacak olan etkinliğimize herkesi bekleriz. Özellikle Dynamics CRM'in bir XRM platformu olarak farklı sorunlara nasıl çözüm olduğunu görmek isteyen kişiler için çok önemli bir etkinlik olacak.
Yaptığımız çözüm genel hatları ile şu özellikleri kapsamakta; İnşaat firmalarının gayrimenkul havuzlarını ayrıntılı segmentasyon kriterleri ile birlikte tanımlamaları ve bu segmentler doğrultusunda filtreleyerek raporlayabildikleri, satış işlemlerini tüm aşamaları ve sözleşme, senet gibi finansal süreçlerini de tam entegre bir şekilde kulanabildikleri inşaat şirketleri için özelleştirilmiş tüm süreçlerini içeren konsolide bir CRM yazılımı olan Gayrımenkul satış platformunu bu etkinliğimizde detaylı olarak inceleyebilir, konunun uzmanlarından detaylı bilgi edinebilirsiniz.

Etkinliğe kayıt için lütfen linke tıklayınız : https://msevents.microsoft.com/CUI/EventDetail.aspx?EventID=1032547495&culture=tr-TR