cub-e.net

just coding...

Dynamics CRM 2016 upgrade sonrasi kullanicilarin kayitlari gorememesi problemi

Dynamics CRM'de bir yazilim ve bug'lari oluyor elbette. Karsilastigim bir bug ve cozumunu de sizinle paylasmak istiyorum.

CRM 2016'ya eger onceki veriyondan upgrade ederek gectiyseniz bazi kullanicilarin sisteme giris yaptiktan sonra hicbir kaydi goruntuleyemedigini gorebilirsiniz. Bu sorun upgrade islemi sirasinda yetki gruplarinda kaynaklanan bir hatadan olusmaktadir ve kullanicinin sisteme girmesine izin vermesine ragmen kayitlara erisimini engellemektedir.

Cozum ise bu sorunu yasayan kullanicilarin guvenlik rollerini kaldirip yeniden vermek. Konuyla ilgili Microsoft'ta case acilmis durumda ilk guncellemede duzelecegini umuyorum.

Bu sekilde sorun cozulmekte. Sizlerin de karsilastigi sorunlar varsa lutfen bildirin ki burada yayinlayarak herkese faydali olmaya calisalim.


Dynamics CRM 2016 - Upsert

CRM yazilimcilari cok asina olmasa da bu terim DBA’ler icin yabanci bir terim degil. Upsert aslinda bizim bildigimiz Update ve Insert islemlerinin bir metodda birlestirilmis hali. Isin ozu ise siz sisteme bir nesne gonderdiginizde ilk once bu var mi diye kontrol ediyor eger varsa update ediyor yoksa yenisini olusturuyor.

            using (var service = new OrganizationService(crmConnection))

            {

                // Use alternate key (accountnumber) field to identify an account record

                var account = new Entity("account")

                {

                    KeyAttributes = new KeyAttributeCollection

                    {

                        {"accountnumber", "MWNS-123" }

                    }

                };

 

                account["name"] = "Microsoft Turkey";

                account["creditlimit"] = new Money(200000);

 

                var request = new UpsertRequest() { Target = account };

                var response = (UpsertResponse)service.Execute(request);

            }

Kod calistiginda MWNS-123 kodlu kaydi arayacak varsa update yoksa insert edecek, iste bu kadar basit.

Bu arada sunu belirtmekte fayda var Upsert islemi icin ozel bir mesaj bulunmamakta yani islem Update olursa Update mesaji, Create olursa create mesaji calismakta. Plugin yazarken lutfen bunu goz onunde bulundurun.

Daha detayli bilgiyi asagidaki adreslerde bulabilrsiniz.

Dynamics CRM 2016 - Alternatif Anahtarlar (Alternate Keys)

Dynamics CRM 2016 icerisindeki guzel ozelliklerden biri de artik bir kaydi tanimlamak icin Guid disinda alternative bir anahtar da tanimlayabiliyor olmamiz. Bu sayede CRM’i baska sistemlerle entegre etmek daha da kolay hale gelmekte. Excel’den veri aktarimi yapmak ya da baska bir uygulamayla entegre ederken diger uygulamanin tanimlayicilarini CRM icersinde tanimlayabilmek bize buyuk esneklik kazandirmakta.

CRM arabiriminde Ayarlar(Settings)->Ozellestirmeler(Customization)->Sistemi Ozellestir(Customize System) adimlarini takip ederek herhangi bir nesne(entity)yi actigimizda anahtar tanimlama ekranina ulasabilmekteyiz.

Bu ekran vasitasiyla tanimlayicilari ekran goruntusunde gosterildigi gibi tanimlayabiliriz.

Burada onemli olan nokta ise gereksinimlerinize gore bir ya da birden fazla alani birlestirerek bir anahtar olusturabiliyorsunuz.

Bir alternative anahtar olusturdugunuzda arka tarafta CRM veritabani uzerinde sorgu performansini artirmak amaciyla bir bir sistem ise calismakta ve bir index olusturulmaktadir. Sistemdeki kayit sayiniza bagli olarak bu islem biraz zaman alabilir.

Bu arada String, Integer, Decimal alanlari bu islem icin kullanabilirsiniz. Olusturdugunuz key’i Update ve Upsert (yani Update or Insert) isleminde kullanabilirsiniz.

            using (var service = new OrganizationService(crmConnection))

            {

                // Use alternate key (accountnumber) field to identify an account record

                Entity account = new Entity("account", "accountnumber", "MWNS-123");

 

                // Set new credit limit;

                account["creditlimit"] = new Money(120000);

 

                // Entity reference using alternate key (emailaddress1) on contact entity

                account["primarycontactid"] = new EntityReference("contact", "emailaddress1", "test@test.com");

 

                UpdateRequest request = new UpdateRequest() { Target = account };

                UpdateResponse response = (UpdateResponse)service.Execute(request);

            }

Bu kullanim seklinin disinda bir de Entity ve Entity Reference siniflari icerisinde KeyAttributes olarak da erisme ve kullanma hakkina sahibiz.

            using (var service = new OrganizationService(crmConnection))

            {

                // Use alternate key (accountnumber) field to identify an account record

                Entity account = new Entity("account")

                {

                    KeyAttributes = new KeyAttributeCollection

                    {

                        {"accountnumber", "MWNS-123" }

                    }

                };

 

                // Set new credit limit;

                account["creditlimit"] = new Money(100000);

 

                // Entity reference using alternate key (emailaddress1) on contact entity

                account["primarycontactid"] = new EntityReference("contact")

                {

                    KeyAttributes = new KeyAttributeCollection

                    {

                        {"emailaddress1", "test@test.com"}

                    }

                };

 

                UpdateRequest request = new UpdateRequest() { Target = account };

                UpdateResponse response = (UpdateResponse)service.Execute(request);

            }

Eger CRM icerisinde talep ettigiginiz key bulunmaz ise asagidaki hatayi alabilirsiniz.

A record with the specified key values does not exist in account entity

Bu linklerden daha detayli bilgi alabilirsiniz.

Dynamics CRM ve Application Insights Entegrasyonu

Bircok Microsft Azure servisini Dynamics CRM ile entegre edebilmekteyiz. Bunlardan en guzel orneklerden birisi Application Insights ve Dynamics CRM birlikteligi. Bu makalede bu Application Insights ile Dynamics CRM'i nasil konusturacagimizi gorecegiz. Oncelikle Application Insights'in ne olduguyla baslayalim isterseniz. 
Visual Studio Application Insight bir telemetri servisidir yani bir sistemi detayli olarak incelemek sitediginizde ona veri gonderdginiz ve bu verileri detaylari bir sekilde grafikler ve tablolar halinde izlediginiz sonucunda rapolar alarak sisteminizin durumu hakkinda bilgiler edindiginiz bir hizmettir. Kisacasi size sisteminizin saglikli calisip calismadigi hakkinda olceklenebilir bilgiler vermektedir.
Dynamics CRM ise bir web tabanli uygulama oldugundan IIS uzerinde olusan web sayfasi goruntuleme taleplerini bunlarin suresini ve bu taleplerin kimden geldigini, hangi sayfalara bakildigini detayli olarak Application Insights'a aktarabiliriz.
Hadi baslayalim.

Azure icinde Application Insights kaynagi olusturalim

Azure hesabiniz yoksa bu adresten olusturabilirsiniz. Sisteme giris yaptiktan sonra Browse'a tikladiktan sonra Application Insights'i secenegini ile olusturma penceresini aciyoruz burada tek dikkat etmeniz gereken nokta Application Type seceneginde Asp.Net'i secmeniz gerekmekte. Gerekli bilgileri doldurdukran sonra Create dugmesiyle kendi Application Insights'inizi olusturabilirsiniz.



Bu noktadan sonra ekrandaki grafiklere tiklayarak her bir grafik bolumunun icinde o bolume nasil veri atabileceginize dair ornekler yer almakta.


JavaScript ile CRM icerisinden veri gonderelim

Dynamics CRM'i acip Ayarlar(Settings)>Ozellestirme(Customization)>Customize the System(Sistemi Ozellestir) bolumunden yeni Web Resource(Web Kaynagi) ekliyoruz.


Ben asagidaki kod parcasini kullaniyorum bu kod parcasi ile CRM icerisinden kullaniciyi ve islem yapilan entity'i(nesne) parametre olarak gonderiyorum.

var UserName;
var Alias;
var serverUrl;

function Getinfo() {
    var context;
    var UserID;
    var ODataPath;
    var selectFields = "$select=DomainName,FullName";

    context = Xrm.Page.context;
    serverUrl = context.getClientUrl();
    UserID = context.getUserId();
    ODataPath = serverUrl + "/XRMServices/2011/OrganizationData.svc";

    var retrieveUserReq = new XMLHttpRequest();
    retrieveUserReq.open("GET", ODataPath + "/SystemUserSet(guid'" + UserID + "')?" + selectFields, false);
    retrieveUserReq.setRequestHeader("Accept", "application/json");
    retrieveUserReq.setRequestHeader("Content-Type", "application/json; charset=utf-8");
    retrieveUserReq.onreadystatechange = function () {
        retrieveUserReqCallBack(this);
    };
    retrieveUserReq.send();
}

function retrieveUserReqCallBack(retrieveUserReq) {
    if (retrieveUserReq.readyState == 4 /* complete */) {
        if (retrieveUserReq.status == 200) {
            var retrievedUser = JSON.parse(retrieveUserReq.responseText).d;
            if (retrievedUser.FullName != null) {
                UserName = retrievedUser.FullName;
                Alias = retrievedUser.DomainName;
            }
        }
    }
}

function captureTelemetry() {
    var appInsights = window.appInsights || function (config) {
        function s(config) { t[config] = function () { var i = arguments; t.queue.push(function () { t[config].apply(t, i) }) } } var t = { config: config }, r = document, f = window, e = "script", o = r.createElement(e), i, u; for (o.src = config.url || "//az416426.vo.msecnd.net/scripts/a/ai.0.js", r.getElementsByTagName(e)[0].parentNode.appendChild(o), t.cookie = r.cookie, t.queue = [], i = ["Event", "Exception", "Metric", "PageView", "Trace"]; i.length;) s("track" + i.pop()); return config.disableExceptionTracking || (i = "onerror", s("_" + i), u = f[i], f[i] = function (config, r, f, e, o) { var s = u && u(config, r, f, e, o); return s !== !0 && t["_" + i](config, r, f, e, o), s }), t
    }({
        instrumentationKey: "Burayi degistirin"
    });

    window.appInsights = appInsights;
    appInsights.trackPageView(Xrm.Page.data.entity.getEntityName(), serverUrl, { User: Xrm.Page.context.getUserName(), DomainName: Alias });
    appInsights.trackEvent(Xrm.Page.data.entity.getEntityName());
}

function startPoint() {
    Getinfo();
    captureTelemetry();
}

Kodun icerisindeki "instrumentationKey: "Burayi degistirin"" bolumundeki instrumentationKey "Essentials" altinda bulabilirsiniz. 

Ben yukaridaki kodu Account(Firma) ve Contact(Contact) entity'lerinin onLoad event'lerine yerlestirdim.


Bunu yapmak icin form uzerindeki uc noktaya tikiiyoruz ve form duzenleyicisini aciyoruz. Form duzenleyicisi uzerinden Form Ozelliklerini aciyoruz ve biraz once eklemis oldugumuz js web kaynagini buradan seciyoruz ve ekliyoruz.


Yapmis oldugumuz degisiklikleri kaydedip formu yayinliyoruz,
Artik form her acildiginda  Application Insights'e veriler gidecektir. Benim demo sistemimden elde ettigim ekran goruntuleri asagidaki gibidir. AppInsights'a ilk girdigimde asagida goruldugu gibi bir ekran bizi karsilamakta. Sirasiyla bu kutucuklara tiklafdigimda detayli bilgileri almaktayim.


Request ve Response zamanlarini;

Kullanicinin sayfalari ne zaman hangi cihazdan cagirdigi ve yuklenme surelerini;


hangi formlarin ne kadar cagrildigini;


ve metadata bilgilerine;


yukarida goruldugu gibi ulasabilmekteyim. Bunun haricinde formlarda olusan hatalari ve kullanici detaylarini(geolocation gibi) da almaktayim. 

Application Insights hakkinda daha fazla bilgiye buradan ulasabilirsiniz.

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;

}

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");

                            }

                        }

                    }

 

Execute Metodu

Dynamics CRM plug-in mimarisi isimli makalede InputParameters ve Output Parameters konularindan bahsederken Request ve Response konularina değinmeden geçtim konu havada kalmasin diye ayri bir baslik altinda incelemenin daha doğru olacagini duşundum. (Ilgili makaleye buradan ulasabilirsiniz)

CRM Servisi içerisindeki Execute Metodu Request ve Response yani Talep ve Yanit seklinde calismaktadir. Sistem üzerinde yapacaginiz butun hareketleri bu sekilde yaopabilirsiniz ki buna ayri metodlari bulunan Create, Update, Delete bile dahildir.

Konu aslinda basit olduğundan çokça açıklanacak bir tarafi da yok gibi ama calisma yapisina bakacak olursak siz bir talepte bulunursunuz o da size yanit verir diye kisaca özetleyebiliriz.



Execute metodunun alabileceği Request’lere makalenin sonunda yer vereceğim. Ama oncelikle sunu da belirteyim ki Request nesnesi opsiyonel parametrelere de sahiptir. Bu parametreler sunlardir.


Parameter

Description

Messages

SolutionUniqueName

Islemin yapilacagi Cozumun Adi

AddPrivilegesRoleRequest

CreateRequest

DeleteRequest

MakeAvailableToOrganizationTemplateRequest

UpdateRequest

SuppressDuplicateDetection

Eslenen kayitlar bulunsun mu bulunmasin mi

CreateRequest

UpdateRequest

 

Kullanima dair ornek kod ise su sekilde;

Account target = new Account();

target.Name = "Fabrikam";

CreateRequest req = new CreateRequest();

req.Target = target;

req["SuppressDuplicateDetection"] = true;

req["SolutionUniqueName"] = "MySolutionName";

CreateResponse response = (CreateResponse)_service.Execute(req);


Eger isterseniz Execute Metodunu asenkron olarka da calistirabilirsiniz. Bu ekranda donmalari ve kullanicilarin ekrandan işlem bitmeden cikmalarini önleyecektir. Bunun için yapmaniz gereken ExecuteAsyncRequest mesajini geçmek olacaktır.

ExecuteMultipleRequest ile de toplu daha aktarimlarinda kullanabileceğiniz mesajdir.

xRM Mesajlarinin tamamina bu adresten ulaşabilirsiniz : https://msdn.microsoft.com/en-us/library/gg334698.aspx

CRM mesajlarina da bu adresten ulaşabilirsiniz : https://msdn.microsoft.com/en-us/library/gg309482.aspx

Dynamics CRM'e saatinizden erisin :)

Akilli saatler de akilli telefonlar kadar hizli bir sekilde hayatimiza girdi. Su anda sektördeki buyuk firmalar ve hatta kickstarter grisimleri bile akilli saat projeleriyle karsimiza cikmakta. Ben de bir Pebble kullanicisiyim ve ozellikle 1 hafta dayanan pili ile diğer akilli saatlerden ayrilmakta.

Bugun Dynamics CRM ile akilli saatlerin nasil birleştirileceğini inceleyeceğiz. Akilli saatler genel itibariyle telefon üzerindeki notificationlari gösterebilme yeteneğine sahipler. Telefon üzerinde notification oluşturmak için ben bugun Pushover sistemini kullanacagim.

Soyle bir senaryomuz olsun; sistem üzerinde oluşturduğumuz bir is akisi ile CRM uzerinde servis talepleri oluştuğu anda push notification isimli bir entity üzerinde bir kayit oluşturuyoruz. Bu kayit varsayilanda gönderilmeyi bekliyor durumunda olacak. Uygulamamiz bu kayitlari okuyacak ve Push Notification’in servisine gönderecek. Boylece notification telefonumuza orada da saatimize gelecek.

Sizin oluşturduğunuz sistemlerde farkli is akislari ile yine push notification isimli entity’e kayit oluşturabilir böylece degisik senaryolar içinde bu sistemi kullanabilirsiniz. Ornegin sistem üzerinde değeri 50.000 tl üzerinde bir firsat oluşturulduğunda bu sistemi tetikleyebilirsiniz.

Sisteme uye olmak için telefonunuza Pushover uygulamasini indirmeniz gerekmekte. Daha sonrasinda ise https://pushover.net sitesinden kendinize bir hesap oluşturuyorsunuz.

Bu sistem üzerinden notification gönderebilmek için 2 bilgiye ihtiyamiz bulunmakta

1.       Sizin User Key’iniz. Her bir kullanicinin bir key’i var sistemde.



2.       Create New Application linkine tıklayarak olusturacaginiz Application Key’i.



Bu sekilde elde ettiğimiz key’ler ile sistem üzerinden notification gönderebilir hale geldik. Bu bilgilerin CRM’de saklanacagi en uygun yer bence Kullanici Kartiydi ve bende bu nedenle kullanici nesnesi üzerine PushOverToken ve PushOverUserKey isimli iki alan actim ve bilgileri buraya girdim.



Daha sonra Push Notification isimli aktivite turunde bir entity oluşturdum ve asagidaki alanlari actim. Buradaki önemli nokta böyle bir entity’i nasil kullanacagimiz. Yazinin basinda da belirttiğim gibi cok farkli senaryolar için bu nesne üzerinde kayitlar oluşturabilirsiniz.


Olusturdugum alanlari incelersek;

Subject : Ekran görüntülenecek baslik bilgisi

To : Mesajlarin kimlere gönderileceği bilgisi

Regarding : Kaydin bagli bulunduğu ana entity. Burada bu bilgiyi telefon üzerinden kullanici bu kayda ulaşmak isterse tek tiklama ile bu bilgiye ulassin diye tutuyorum.

Status Reason : Varsayilanda “Waiting To Send” yani bekliyor konumunda oluşturuluyor kayitlar. Gondeme işlemi tamamlandiktan sonra bu alani “Sent” yani gönderildi olarak değiştiriyorum.

Description : Detayli bilgi vermek için bu alani kullaniyorum.

 

static string URL = "https://xxx.contoso.com";

        private static IOrganizationService ServiseBaglan()

        {

            try

            {

                Uri organizationUri = new Uri(String.Format("{0}/XRMServices/2011/Organization.svc", URL));

                ClientCredentials clientcred = new ClientCredentials();

                clientcred.UserName.UserName = "kullanici adi";

                clientcred.UserName.Password = "sifre";

                OrganizationServiceProxy _serviceproxy = new OrganizationServiceProxy(organizationUri, null, clientcred, null);

                IOrganizationService _service = (IOrganizationService)_serviceproxy;

                return _service; ;

            }

            catch (Exception ex)

            {

                Console.WriteLine(ex.Message);

                return null;

            }

        }

 

        private static void changeStatus(IOrganizationService service, Guid Id)

        {

            // Create the Request Object

            SetStateRequest state = new SetStateRequest();

 

            // Set the Request Object's Properties

            state.State = new OptionSetValue(0);

            state.Status = new OptionSetValue(100000000);

 

            // Point the Request to the case whose state is being changed

            state.EntityMoniker = new EntityReference("new_pushnotification", Id);

 

            // Execute the Request

            SetStateResponse stateSet = (SetStateResponse)service.Execute(state);

        }

        static void Main(string[] args)

        {

            try

            {

                string fetchXml = @"<fetch mapping='logical'>

                                 <entity name='new_pushnotification'>

                                    <attribute name='activityid' />

                                    <attribute name='subject' />

                                    <attribute name='description' /> 

                                     <filter type='and'>

                                        <condition attribute='statuscode' operator='eq' value='1'/>

                                     </filter>

                                    <link-entity name='activityparty' from='activityid' to='activityid' alias='ac'>

                                        <link-entity name='systemuser' from='systemuserid' to='partyid' alias='ad'>

                                            <attribute name='new_pushovertoken' />

                                            <attribute name='new_pushoveruserkey' />

                                        </link-entity>

                                    </link-entity>

                                 </entity>

                             </fetch>";

 

                IOrganizationService service = ServiseBaglan();

               

                if (service != null)

                {

                    Console.WriteLine("Servise Baglanildi");

 

                    EntityCollection entCollection = (EntityCollection)service.RetrieveMultiple(new FetchExpression(fetchXml));

                    foreach (var item in entCollection.Entities)

                    {

                        Console.WriteLine("Aktarilacak kayitlar bulundu");

 

                        if (item.Contains("ad.new_pushovertoken") && item.Contains("ad.new_pushoveruserkey") && item.Contains("subject") && item.Contains("description"))

                        {

                            string PushOverToken = ((AliasedValue)item["ad.new_pushovertoken"]).Value.ToString();

                            string PushOverUserKey = ((AliasedValue)item["ad.new_pushoveruserkey"]).Value.ToString();

                            string subject = item["subject"].ToString();

                            string description = item["description"].ToString();

                            Guid activityid = new Guid(item["activityid"].ToString());

                            string url = String.Format("{0}/main.aspx?etn=new_pushnotification&pagetype=entityrecord&id=%7B{1}%7D", URL, activityid.ToString());

 

                            dynamic Parameters = new NameValueCollection() {

                        {

                            "token",

                            PushOverToken

                        },

                        {

                            "user",

                            PushOverUserKey

                        },

                        {

                            "message",

                            description

                        },

                        {

                            "url",

                            url

                        },

                        {

                            "url_title",

                            "Open Record"

                        },

                        {

                            "title",

                            subject

                        }

                        };

 

                            WebClient client = new WebClient();

                            Console.WriteLine("Kayitlar gonderiliyor");

                            client.UploadValues("https://api.pushover.net/1/messages.json", Parameters);

                            Console.WriteLine("CRM guncelleniyor");

                            changeStatus(service, activityid);

                        }

                    }

                }

 

                Console.WriteLine("Tamamlandi");

                Console.ReadLine();

}

            catch (Exception ex)

            {

                Console.WriteLine(ex.Message);

                return null;

            }

        }

 

Kodda da görebileceğiniz uzere Ilk olarak CRM Servisine baglaniyoruz. Ben IFD enabled bir organizasyon kullandigim için benim servis baglantim bu sekilde Office 365 ya da on premise sistemlerde farkli sekillerde CRM servise bağlanma yöntemleri mevcut. Bu konuda sorun yasarsaniz benimle iletişime geçebilirsiniz.

 

Daha sonra ise fetchXML sorgumuz ile sistem üzerinde bekleyen notification’larin bilgisini aliyoruz ama bu işlemi yaparken ayni zamanda systemuser nesnesine de dallanarak ihtiyacim olan pushovertoken ve pushoveruserkey bilgilerini de aliyorum. Boylece her bir kullanici için teker teker veri sorgulayarak servisi meşgul etmiyorum.

 

@"<fetch mapping='logical'>

                                 <entity name='new_pushnotification'>

                                    <attribute name='activityid' />

                                    <attribute name='subject' />

                                    <attribute name='description' /> 

                                     <filter type='and'>

                                        <condition attribute='statuscode' operator='eq' value='1'/>

                                     </filter>

                                    <link-entity name='activityparty' from='activityid' to='activityid' alias='ac'>

                                        <link-entity name='systemuser' from='systemuserid' to='partyid' alias='ad'>

                                            <attribute name='new_pushovertoken' />

                                            <attribute name='new_pushoveruserkey' />

                                        </link-entity>

                                    </link-entity>

                                 </entity>

                             </fetch>";

 

 

Verileri aldiktan sonra ise bunlari istediğim gibi düzenleyip string veriler haline getiriyorum. Sonunda ise asagidaki bolum ile servise Push Notification servisine gönderiyorum;

 

                            dynamic Parameters = new NameValueCollection() {

                        {

                            "token",

                            PushOverToken

                        },

                        {

                            "user",

                            PushOverUserKey

                        },

                        {

                            "message",

                            description

                        },

                        {

                            "url",

                            url

                        },

                        {

                            "url_title",

                            "Open Record"

                        },

                        {

                            "title",

                            subject

                        }

                        };

 

                            WebClient client = new WebClient();

                            Console.WriteLine("Kayitlar gonderiliyor");

                            client.UploadValues("https://api.pushover.net/1/messages.json", Parameters);

                            Console.WriteLine("CRM guncelleniyor");

                            changeStatus(service, activityid);

 

 

Burada dikkat edilmesi gerekn nokta URL kisminda gerçekten sistemdeki nesnenin acilacagi URL’i buraya getiriyorum ki telefondan kullanici direkt kayda ulassin.

Gonderme işleminden sonra ise kayitlari gönderildi diye güncelliyorum ki tekrar tekrar ayni kayitlari göndermeyelim.

 

Sonuc olarak once telefonda sonra ise saatimde gelen verilere ulaşıyorum :)

PushOver'in ana ekraninda gelen notification.




Notification'i acinca detaylara ulasiyorum. 



Open Record isimli linke tiklayinca da direkt ilgili kayda ulasiyorum.



Ve sonuc Pebble ekraninda CRM'den gelen Notification'i goruyorum :) iste mutlu son...