AVIS – pomocná funkce pro překonání výpadku dostupnosti Integračních služeb

Abychom ulehčili práci vývojářům třetích stran, kteří používají prostředí .Net, vytvořili jsme následující pomocnou metodu, která vám pomůže sjednotit a zjednodušit webové volání „Integračních služeb”. Metoda umožňuje opakovaně volat potřebnou část vašeho kódu a překonat tak bez většího úsilí krátkodobý výpadek dostupnosti Integračních služeb.

Kód je napsán v jazyku C#. Vývojáři, kteří používají jiné jazyky nebo jiné technologie, si mohou podobnou metodu napsat sami. Kód vzorové metody obsahuje komentáře, podle kterých je možné se zorientovat a funkci si přizpůsobit nebo napsat vlastní.

  1. Následující metodu zkopírujte a vložte ji na vhodné místo ve vašem projektu.


        //Metoda pro automatické opakované volání Integračních služeb v případě jejich výpadku
        //repeatSection - labda výraz nebo delegát, který je potřeba provést
        //remainingAdditionalAttempts  - počet opakování volání v případě, že služby nejsou dostupné
        //sleepSeconds - délka intervalu v sekundách, po který se čeká mezi jednotlivými pokusy
        public static void CallSafeAVIS(
            Action repeatSection, 
            int remainingAdditionalAttempts = 3, 
            int sleepSeconds = 25)
        {
            try
            {
                //vyvoláme akci
                repeatSection.Invoke();
            }
            catch (System.ServiceModel.CommunicationException err)
            {
                //při běhu, při pádu nebo v době restartu budeme sledovat výjimky 
                //typu System.ServiceModel.CommunicationException
                //a jeho potomky
                Type exceptionContractType = null;
                bool isTerminalFault = true;
                bool isApplicationException = true;
                string exceptionDescription = null;
                string exceptionSource = null;
                int exceptionNumber = 0;

                var errType = err.GetType();
                if (!errType.IsGenericType) { }
                else if (!(errType.BaseType == typeof(System.ServiceModel.FaultException))) { }
                else if (errType.GenericTypeArguments.Length != 1) { }
                else if (err.GetType().GenericTypeArguments[0].Name != "ExceptionContract") { }
                else
                {
                    //tady máme chybu, kterou vyhodily Integrační služby, 
                    //takže zjistíme, jestli jde o chybu, která vynutila restart služby
                    //v případě, že je vlastnost IsTerminalFault = true, došlo právě k restartu služby
                    exceptionContractType = err.GetType().GenericTypeArguments[0];
                    isTerminalFault = exceptionContractType
                                                 .GetProperty("IsTerminalFault")
                                                 .GetValue(((dynamic)err).Detail);
                    isApplicationException = exceptionContractType
                                                 .GetProperty("IsApplicationException")
                                                 .GetValue(((dynamic)err).Detail);
                    exceptionDescription = exceptionContractType
                                                 .GetProperty("Description")
                                                 .GetValue(((dynamic)err).Detail);
                    exceptionSource = exceptionContractType
                                                 .GetProperty("Source")
                                                 .GetValue(((dynamic)err).Detail);
                    exceptionNumber = exceptionContractType
                                                 .GetProperty("Number")
                                                 .GetValue(((dynamic)err).Detail);
                }

                if (isTerminalFault && remainingAdditionalAttempts > 0)
                {
                    //služby nejsou dočasně dostupné, zbývají ještě nějaké pokusy
                    //počkat a zkusit to znovu
                    System.Threading.Thread.Sleep(new TimeSpan(0, 0, sleepSeconds));
                    CallSafeAVIS(repeatSection, --remainingAdditionalAttempts, sleepSeconds);
                    return;
                }
                else if (isTerminalFault)
                {
                    //služby nejsou dočasně dostupné
                    //došla trpělivost, oznámit chybu
                    throw;
                }
                else if (isApplicationException)
                {
                    //očekávaná aplikační chyba, víme, co se děje, a uživatel ví, co s tím udělat
                    throw new ApplicationException(exceptionDescription);
                }
                else
                {
                    //neočekávaná chyba 
                    throw new ApplicationException(
                        string.Format("Chyba číslo {0} v {1}:\r\n{2}",
                        exceptionNumber,
                        exceptionSource,
                        exceptionDescription));
                }
            }
            catch (Exception err)
            {
                //ostatní typy výjimek předáme volajícímu
                throw err;
            }
        }
  1. Příklady použití pomocné metody CallSafeAVIS v praxi:

        public DokladyAVIS.DokladContract NacistDoklad(string id)
        {
            DokladyAVIS.DokladContract result = null;
            CallSafeAVIS(() =>
            {
                using (var client = new DokladyAVIS.DokladyClient())
                {
                    result = client.NacistDoklad(id);
                }
            });
            return result;
        }

        public void AktualizovatDoklad(DokladyAVIS.DokladContract doklad)
        {
            CallSafeAVIS(() =>
            {
                using (var client = new DokladyAVIS.DokladyClient())
                {
                    client.AktualizovatDoklad(doklad);
                }
            });
        }

        public Guid VlozitNovyDoklad(DokladyAVIS.DokladContract doklad)
        {
            Guid rg = Guid.Empty;
            CallSafeAVIS(() =>
            {
                using (var client = new DokladyAVIS.DokladyClient())
                {
                    rg = client.VlozitNovyDoklad(doklad);
                }
            }); 	
            return rg;
        }



  1. Při volání různých metod v jedné funkci je potřeba, aby každé jedno volání bylo provedeno s pomocí metody CallSafeAVIS:

        public Guid ZapsatNovyDoklad(DokladyAVIS.DokladContract doklad)
        {
            Guid rg = Guid.Empty;
            CallSafeAVIS(() =>
            {
                using (var client = new DokladyAVIS.DokladyClient())
                {
                    rg = client.VlozitNovyDoklad(doklad);
                }
            }); 	

            …
            //doklad načteme, získáme hodnoty vyplněné šablonou
            …

            CallSafeAVIS(() =>
            {
                using (var client = new DokladyAVIS.DokladyClient())
                {
                    doklad = client.NacistDokladRG(rg);
                }
            });

            …
            //provedeme nějaké úpravy a zaktualizujeme
            …

            CallSafeAVIS(() =>
            {
                using (var client = new DokladyAVIS.DokladyClient())
                {
                    client.AktualizovatDoklad(doklad);
                }
            });