suborchestration-in-azure-durable-functions

SubOrchestration in Azure Durable functions

“The Developer voice in .NET” seriyamızda Azure funksiyaları öyrənməyi davam etdirərək, bu dəfə Azure Durable funksiyalarda suborchestration haqqında danışacağıq. Bu seriya məqalələrimizdə indiyə kimi yazdığımız postları aşağıdakı sıra ilə oxuya bilərsiniz:

1. APACHE AXIS SERVISININ .NET CORE-DA ISTIFADƏSI

2 N-LAYERED ARXITEKTURADA ISTISNA HALLARININ EMALI

3 N-LAYERED ARCHITECTURE WITH AZURE FUNCTIONS. PRAKTIKADA N-LAYERED ARXITEKTURA

4 PRAKTIKADA AZURE DURABLE FUNKSIYALAR VƏ CHAINING PATTERNI


PS: Məqaləni tam anlamaq və tələblərlə tanış olmaq üçün Praktikada Azure durable funksiyalar və chaining patterni məqaləmizi oxumaq lazımdır.

                Azure Durable funksiyalarda SubOrchestration nədir?

Əvvəkli məqaləmizdə Orchestrator haqqında məlumat vermişdim. Bu bölmədə isə SubOrchestrator haqqında danışacağıq. SubOrchestrator Azure funksiyalarda  bir neçə activity-ni özündə birləşdirən arxitektur yanaşma formasıdır. Bu yanaşma formasında activity-lər müxtəlif patternlər (chaining,fan-in fan-out və.s) əsasında bir-biriylə əlaqələnərək vahid bir kompleks problemi həll etməyə yönəlir. SubOrchestratorlar adətən Orchastratorların daxilində, iş bölgüsü zamanı ortaya çıxır. Özündə bir neçə activity-ni əlaqələndirir və bununla da əsas orchestrator üçün altmodul rolunu oynayır.

       Bizim tapşırığımızda, faylların köçürülməsi üçün ayrıca bir suborchestrator ayırmışıq.

        ..................................        
           if(!getItemsResponse.DocumentListResponse.IsFolderEmpty())
                    {
                        context.SetCustomStatus("Got source folder's documents. Preparing to move...");
                        var rsp = new MoveFilesRequest
                        {
                            Request = getItemsResponse.DocumentListResponse,
                            MoveFolderData = request
                        };
                        var _output = await context.CallSubOrchestratorAsyncstring>>("HV_MoveFiles", rsp);
                        output.AddRange(_output);
                    }
                    else
                    {
                        string message = "Folder Empty. Nothing to move!";
                        context.SetCustomStatus(message);
                        output.Add(message);
                    }
.....................................

       Bu SubOrchestrator əgər köçürtmə prosesini başlatmaq mümkündürsə və köçürtmə üçün nəzərdə tutulan qovluq boş deyilsə, ozaman işə salınır.

       SubOrchestratorlar kontekstin( burada IdurableOrchestrationContextCallSubOrchestratorAsync metodu vasitəsilə işə düşür. Generic hissədə orchestratorun hansı tipdə nəticə qaydaracağı göstərilir. Argument hissəsində isə hansı funksiyanın işə salınacağı və hansı parameterlə işləyəcəyi bildirilir. Nümunədə HV_MoveFiles funksiyası MoveFilesRequest tipində arqument qəbul edərək işə düşür.

                     var rsp = new MoveFilesRequest
                        {
                            Request = getItemsResponse.DocumentListResponse,
                            MoveFolderData = request
                        };
                        var _output = await context.CallSubOrchestratorAsyncstring>>("HV_MoveFiles", rsp);

       SubOrchestratorlar adi activity-lərdən fərqli olaraq, OrchestratorTrigger atributu ilə mark olunur və özü də ayrıca bir altkontekst olduğuna görə IdurableOrchestrationContext tipində paramter qəbul edir. Bu parametr əsas orchestrator olub funksiya içərisində activity-ləri işə salmağımıza imkan yaradır.

          [FunctionName("HV_MoveFiles")]
        public  async Task < List < string > > MoveFiles([OrchestrationTrigger] IDurableOrchestrationContext context)

Durable funksiyalar keçə məqaləmizdə qeyd etdiyimiz kimi, limitli sayda (burada 1 arqument) arqument qəbul edirlər. Əgər subOrchestratora parametr verilibsə ozaman həmin paramteri durable kontekstin Getİnput() metodu vasitəsilə alırıq.

  var request = context.GetInput();

Daha sonra aldığımız arqumentdə (qovluq) faylların sayi qədər dövr təşkil edərək əsas qovluqda alt qovluqları identiikasiya edirik. Əgər qovluq içərisində qovluq varsa ozaman həmin qovluğun adına görə identifikatorunu almalıyıq. Bununçün servisə sorğunu activity ilə veririk.

 if (request.GetItemInIndex(index).DisplayType == DisplayTypes.Folder)
                    {
                        string relativePath = request.GetDestinationItemPath(index);


                        var subFolderExistsResponse = await context.CallActivityAsync("HV_GetFolderIdByName"new FolderRequest
                        {
                            RelativeFolderPath = relativePath,
                            Id = request.Destination.BaseFolderId,
                            SourceSystem = request.MoveFolderData.SourceSystem
                        });
...............................................

Əgər qovluğun identifikatorunu almaq mümkün olursa digər bir activityni çağıraraq həmin qovluğun içərisindəki faylları alırıq.

if (subFolderExistsResponse.HttpResponse.StatusCode == StatusCodes.Status200OK)
                        {
                            var subResponseBatch = await context.CallActivityAsync("HV_GetItemsInFolder"new GetItemsInFolderRequest
                            {
                                SourceSystem = request.MoveFolderData.SourceSystem
                                ,SourceFolderId = request.GetItemInIndex(index).Id
                                ,FolderName = request.GetItemInIndex(index).Name
                            });
.............................................

Daha sonra bu faylları bir bir source-dan destination-a köçürdürük.

for (int subIndex = 0; subIndex < subResponseBatch.GetItemsCount(); subIndex++)
                                {
                                    var moveDocumentData = new MoveDocumentData
                                    {
                                        SourceSystem = request.MoveFolderData.SourceSystem,
                                        MovedDocumentName = subResponseBatch.GetItemInIndex(subIndex).Name,
                                         DocumentType = subResponseBatch.GetItemInIndex(subIndex).DisplayType,
                                        Document = new Models.HomeVault.MoveDocument.Document
                                        {
                                            Destination = new Destination
                                            {
                                                  BaseFolderId = request.Destination.BaseFolderId
                                                , BaseFolderPath = request.Destination.BaseFolderPath
                                                , RelativeFolderPath = relativePath
                                            },
                                            MoveOptions = request.MoveFolderData.Document.MoveOption,
                                            SourceDocumentId = subResponseBatch.GetItemInIndex(subIndex).Id
                                        }
                                    };


         var moveDocumentOutput = await context.CallActivityAsync("HV_MoveDocument", moveDocumentData);

Yox əgər axtarılan element qovluq yox, fayldırsa ozaman sadəcə köçürtmə əməliyyatı yerinə yetiririk.

 [FunctionName("HV_MoveFiles")]
        public  async Task < List < string > > MoveFiles([OrchestrationTrigger] IDurableOrchestrationContext context)
        {
            var output = new List<string>
            {
                "Move Files successfully started..."
            };
            var request = context.GetInput();
            try
            {
                for (int index = 0; index < request.GetItemsCount(); index++)
                {
                    if (request.GetItemInIndex(index).DisplayType == DisplayTypes.Folder)
                    {
                        string relativePath = request.GetDestinationItemPath(index);


                        var subFolderExistsResponse = await context.CallActivityAsync < GetFolderByIdOutput > ("HV_GetFolderIdByName"new FolderRequest
                        {
                            RelativeFolderPath = relativePath,
                            Id = request.Destination.BaseFolderId,
                            SourceSystem = request.MoveFolderData.SourceSystem
                        });
                        output.AddRange(subFolderExistsResponse.Messages);
                        if (subFolderExistsResponse.HttpResponse.StatusCode == StatusCodes.Status200OK)
                        {
                            var subResponseBatch = await context.CallActivityAsync < DocumentListOutput >("HV_GetItemsInFolder"new GetItemsInFolderRequest
                            {
                                SourceSystem = request.MoveFolderData.SourceSystem
                                ,SourceFolderId = request.GetItemInIndex(index).Id
                                ,FolderName = request.GetItemInIndex(index).Name
                            });


                            output.AddRange(subResponseBatch.Messages);
                            bool isFolderEmpty = true;


                            if (subResponseBatch == null)
                            {
                                string message = "Folder is empty. Nothing to move!";
                                output.Add(message);
                            }
                            else
                            {
                           
                                for (int subIndex = 0; subIndex < subResponseBatch.GetItemsCount(); subIndex++)
                                {
                                    var moveDocumentData = new MoveDocumentData
                                    {
                                        SourceSystem = request.MoveFolderData.SourceSystem,
                                        MovedDocumentName = subResponseBatch.GetItemInIndex(subIndex).Name,
                                         DocumentType = subResponseBatch.GetItemInIndex(subIndex).DisplayType,
                                        Document = new Models.HomeVault.MoveDocument.Document
                                        {
                                            Destination = new Destination
                                            {
                                                  BaseFolderId = request.Destination.BaseFolderId
                                                , BaseFolderPath = request.Destination.BaseFolderPath
                                                , RelativeFolderPath = relativePath
                                            },
                                            MoveOptions = request.MoveFolderData.Document.MoveOption,
                                            SourceDocumentId = subResponseBatch.GetItemInIndex(subIndex).Id
                                        }
                                    };


                                    var moveDocumentOutput = await context.CallActivityAsync("HV_MoveDocument", moveDocumentData);
                                    output.AddRange(moveDocumentOutput.Messages);
                                    if (moveDocumentOutput.IsMoved)
                                    {
                                        string message = $"`{subResponseBatch.GetItemInIndex(subIndex).Name}` moved to `{relativePath}`.";
                                        _logger.LogDebug(message);
                                    }
                                    else
                                    {
                                        string message = $"`Warning : Can't move {subResponseBatch.GetItemInIndex(subIndex).Name}` to `{relativePath}`.";
                                        _logger.LogError(message);
                                        isFolderEmpty = false;
                                    }


                                }
                            }
                            if(isFolderEmpty)
                            {
                                output.Add($"`{request.GetItemInIndex(index).Name}` folder is Empty. Preparing to delete empty folder...");
                               var _output = await context.CallActivityAsync("HV_DeleteDocument"new DeleteDocumentData
                                {
                                      SourceSystem = request.MoveFolderData.SourceSystem
                                    , FolderName = request.GetItemInIndex(index).Name
                                    , Id = request.GetItemInIndex(index).Id
                               });
                                output.AddRange(_output.Messages);
                            }
                        }
                        else
                        {
                           var response = JsonConvert.DeserializeAnonymousType(subFolderExistsResponse.HttpResponse.Value.ToString(), new { Message = "" });
                            string message = $"Warning : {response.Message}. Destination should have the folder before moving files!";
                            _logger.LogError(message);
                        }


                    }
                    else
                        if (request.GetItemInIndex(index).DisplayType == DisplayTypes.Document)
                    {
                        try
                        {
                            var moveDocumentOutput = await context.CallActivityAsync("HV_MoveDocument"new MoveDocumentData
                            {
                                SourceSystem = request.MoveFolderData.SourceSystem,
                                Document = new Models.HomeVault.MoveDocument.Document
                                {
                                    Destination = request.Destination,
                                    MoveOptions = request.MoveFolderData.Document.MoveOption,
                                    SourceDocumentId = request.GetItemInIndex(index).Id
                                }
                                , MovedDocumentName = request.GetItemInIndex(index).Name
                                , DocumentType = request.GetItemInIndex(index).DisplayType
                            });
                            output.AddRange(moveDocumentOutput.Messages);
                            if (moveDocumentOutput.IsMoved)
                            {
                                string message = $"`{request.GetItemInIndex(index).Name}` moved to `{request.Destination.BaseFolderPath}/{request.Destination.RelativeFolderPath}`.";
                                _logger.LogInformation(message);
                            }
                            else
                            {
                                string message = $"`Can't move {request.GetItemInIndex(index).Name}`  to `{request.Destination.BaseFolderPath}/{request.Destination.RelativeFolderPath}`";
                                _logger.LogError(message);
                            }
                        }
                        catch(Exception exp)
                        {
                            output.Add(exp.Message);
                        }
                           
                    }
                }
     
            }
            catch(Exception exp)
            {
                output.Add(exp.Message);
                _logger.LogError(exp.Message);
            }
            return output;
        }

 

 Qovluqdakı elementləri almaq üçün

 [FunctionName("HV_GetItemsInFolder")]
        public async Task GetItemsInFolder([ActivityTrigger] GetItemsInFolderRequest request)
        {
            var output = new DocumentListOutput();
            output.AddMessage($"Trying to get documents from `{request.FolderName}` folder...");
            try
            {
                output.DocumentListResponse = await _moveFolderProvider.GetItemsInFolderAsync(request.SourceSystem, request.SourceFolderId);
                output.AddMessage($"{output.GetItemsCount()} {request.GenerateSuccessOutputMessage()}");
            }
            catch(Exception exp)
            {
                output.AddMessage(request.GenerateExceptionMessage(exp));
            }
            return output;
        }

Qovluğun identifikatorunu almaq üçün isə belə bir activity-ni işə salırıq

      [FunctionName("HV_GetFolderIdByName")]
        public async Task GetFolderIdByName([ActivityTrigger] FolderRequest request)
        {
            var output = new GetFolderByIdOutput();
            try
            {
                output.AddMessage($"Preparing to find folder : `{request.SubFolderName}` inside Destination...");
                output.HttpResponse = await _moveFolderProvider.GetFolderIdByNameAsync(request.Id, request.RelativeFolderPath, request.SourceSystem);
               
                if(output.HttpResponse.StatusCode == StatusCodes.Status200OK)
                {
                    output.AddMessage(request.GenerateSuccessOutputMessage());
                }
                else if(output.HttpResponse.StatusCode == StatusCodes.Status404NotFound)
                {
                    output.AddMessage(request.GenerateExceptionMessage(null));
                }
            }
            catch (Exception exp)
            {
                output.AddMessage(request.GenerateExceptionMessage(exp));
            }
            return output;
        }


Tural

Tural Süleymani

Süleymani Tural Microsoft-un MCSD statuslu mütəxəssisidir, 2008-ci ildən bu yana proqramlaşdırma üzrə tədris aparır

Müəllifin bu dildə ən son postları

Bu yazıları da bəyənə bilərsiniz