UiPathActivityCreatorを利用してカスタムアクティビティを作ってみた（実践編）「UiPath, PostgreSQL接続」

# はじめに
先日投稿した、UiPathActivityCreatorを利用してカスタムアクティビティを作成するの、実践編になります。
「UiPathActivityCreatorとは」「インストール方法」は、前回の記事を御覧ください。

https://qiita.com/Y_Hakiai/items/a1535c58426100c46c68

今回は、実際にUiPathActivityCreatorを利用してのカスタムアクティビティの作成及び、
UiPath Studioでの利用までをやっていきます。

# 今回作成するカスタムアクティビティ
今回はサンプルとして、PostgreSQLに接続、SELECT文を実行、結果を取得するアクティビティを作成したいと思います。
イメージとしてはこんな感じです。
![172705.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2638718/4930099e-1b9b-2215-5cdd-1028b764f00c.png)
※PostgreSQLへ接続する手段として、Npgsqlを使用します。

# 環境・バージョン情報
* Visual Studio 2022
* UiPath Studio 2022.4.4 Community License
* Npgsql v6.0.6

# カスタムアクティビティ実装
1 .UiPathActivityCreatorを使って、プロジェクトを作成します。
　作り方の詳細は、前回の記事を御覧ください。

https://qiita.com/Y_Hakiai/items/a1535c58426100c46c68#4-1-2-%E5%88%9D%E3%82%81%E3%81%A6%E3%81%AE%E4%BD%BF%E3%81%84%E6%96%B9

今回は、以下の様の形でプロジェクトを作成しました。
* プロジェクト名：PostgreSQLConnection
![WS000000.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2638718/85f01781-7c5d-d978-0ef3-18aa1fc05bda.jpeg)

2 .PostgreSQLへ接続する為、Npgsqlを導入します。
　PostgreSQLConnection.Activitiesを右クリックし、NuGetパッケージ管理を押下
![WS000001.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2638718/c4122305-ddca-4204-6118-7301fc05bd21.jpeg)
参照タブにて、'npgsql'と入力して検索を行い、Npgsqlをクリックし、インストールを押下
![WS000002.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2638718/979b4d3c-92a9-4ae3-1d13-b35fb628819f.jpeg)
変更のプレビューが出てくる場合があるので、OKを押下
![WS000003.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2638718/7b4d46cd-575e-749c-3fd6-5237862aaf5d.jpeg)
以下のように表示されたらインストール完了
![WS000004.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2638718/efa0bf58-5872-6f7b-f0b9-6b63b8e0e92f.jpeg)

3 .アクティビティの定義を実施します。
　こちらも詳細は、前回の記事に記載されています。
　今回はこんな形にしました。
アクティビティ定義
![WS000005.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2638718/a508716d-b84d-7a93-1f7a-83bfa5cc0ba9.jpeg)
プロパティ定義
![WS000006.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2638718/cba31237-2c82-3d34-a963-8cc166a62974.jpeg)
こんな感じで、自動でソースコードが生成されました。
![WS000007.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2638718/e0b54651-9dc9-ec06-c1da-35a265eef087.jpeg)
```c#:SELECT.cs（一部抜粋）
#region Protected Methods

        protected override void CacheMetadata(CodeActivityMetadata metadata)
        {
            if (Server == null) metadata.AddValidationError(string.Format(Resources.ValidationValue_Error, nameof(Server)));
            if (Port == null) metadata.AddValidationError(string.Format(Resources.ValidationValue_Error, nameof(Port)));
            if (UserId == null) metadata.AddValidationError(string.Format(Resources.ValidationValue_Error, nameof(UserId)));
            if (DbName == null) metadata.AddValidationError(string.Format(Resources.ValidationValue_Error, nameof(DbName)));
            if (Password == null) metadata.AddValidationError(string.Format(Resources.ValidationValue_Error, nameof(Password)));
            if (Enlist == null) metadata.AddValidationError(string.Format(Resources.ValidationValue_Error, nameof(Enlist)));

            base.CacheMetadata(metadata);
        }

        protected override async Task<Action<AsyncCodeActivityContext>> ExecuteAsync(AsyncCodeActivityContext context, CancellationToken cancellationToken)
        {
            // Inputs
            var server = Server.Get(context);
            var port = Port.Get(context);
            var userid = UserId.Get(context);
            var dbname = DbName.Get(context);
            var password = Password.Get(context);
            var enlist = Enlist.Get(context);
    
            ///////////////////////////
            // Add execution logic HERE
            ///////////////////////////

            // Outputs
            return (ctx) => {
                Result.Set(ctx, null);
            };
        }

#endregion
```

4 .'Add execution logic HERE'の箇所に処理を実装します。
変更を加えた箇所だけピックアップして記載します。

:::note warn
* クエリ文は、直接コードに実装しました。
本来は、INPUTパラメータで渡して、動的に変更出来るようにするのがベストです。
今回はカスタムアクティビティを実装して使うのが目的なので割愛しました。
* DB接続はトランザクションをはるべきですが、今回割愛してます。
業務で実際に使う場合等は、トランザクションはるようにして下さい。
:::
```diff_c_sharp:SELECT.cs（参照部分）
using System;
using System.Activities;
using System.Threading;
using System.Threading.Tasks;
using System.Data;
using PostgreSQLConnection.Activities.Properties;
using UiPath.Shared.Activities;
using UiPath.Shared.Activities.Localization;
+ using Npgsql;
```
```diff_c_sharp:SELECT.cs（実処理部分）
protected override async Task<Action<AsyncCodeActivityContext>> ExecuteAsync(AsyncCodeActivityContext context, CancellationToken cancellationToken)
        {
            // Inputs
            var server = Server.Get(context);
            var port = Port.Get(context);
            var userid = UserId.Get(context);
            var dbname = DbName.Get(context);
            var password = Password.Get(context);
            var enlist = Enlist.Get(context);

            // 接続文字列の生成
+           string connectStr = $"Server={server};Port={port};User ID={userid};Database={dbname};Password={password};Enlist={enlist}";
+
+           // 返却用変数
+           DataTable table = new DataTable();
+
+            // DB接続
+            using (NpgsqlConnection connection = new NpgsqlConnection(connectStr)) { 
+               
+               // DB接続開始
+               connection.Open();
+
+                // 実行クエリ
+               string queryStr = "SELECT * FROM test";
+
+                // 実行準備
+               NpgsqlCommand command = new NpgsqlCommand(queryStr, connection);
+               NpgsqlDataAdapter adapter = new NpgsqlDataAdapter(command);
+
+                
+               // 実行
+               adapter.Fill(table);
+
+               // DB接続クローズ
+               connection.Close();
+           }

            // Outputs
            return (ctx) => {
+               Result.Set(ctx, table);
-               Result.Set(ctx, null);
            };
        }
```
5 .作成したアクティビティをパッケージ化して使えるようにします。
PostgreSQLConnection.Activitiesを右クリックし、発行を押下
![WS000009.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2638718/737abf72-1c5d-feff-ccf6-366dbb91bb08.jpeg)
今回は、ローカルフォルダに発行するため、フォルダを選択し、次へを押下
![WS000010.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2638718/9e695ac3-f0e5-dc64-245f-850d3b0483e4.jpeg)
パッケージを作成するフォルダを入力し、完了を押下
![WS000012.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2638718/c55c8d3b-c4c4-9554-04b0-10621fbe45b9.jpeg)
公開準備が完了しました。と表示されるので、発行を押下
![WS000013.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2638718/66681b5f-f17a-d3ad-3841-ab35d45cec78.jpeg)
ビルドが正常に終了した場合、成功した旨のメッセージが表示されます
ここまでくれば、VisualStudioでの作業は終了です
![WS000014.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2638718/431acdef-6a5e-f8f7-e53c-896b7483d3f4.jpeg)
![WS000015.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2638718/7ab07dc5-ddf7-0f11-6381-114f571564b5.jpeg)

6 .UiPathStudioで作成したパッケージを取り込みます。
設定＞パッケージソースを管理＞ユーザー定義のパッケージソースに、先程パッケージを出力した先のフォルダを指定
![WS000018.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2638718/c400d822-633c-bb35-6e53-b004f583c908.jpeg)

ユーザー定義のパッケージソースに追加されます
![WS000019.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2638718/3f47dfb1-7d2b-cb5c-70e7-93aaaa6ed754.jpeg)

# ロボット作成
1 .実際に作成したパッケージでプロセスを作成します。
　プロセスの作成方法は、通常のロボットと同じで大丈夫です。

2 .作成したプロセスにパッケージを追加します。
パッケージ管理を押下
![WS000020.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2638718/9ef33229-5aab-3d0c-a273-5b842d3755ce.jpeg)

ローカルパッケージ（先程、ユーザー定義にて定義した名前）を選択し、
作成したパッケージを選択、インストールを押下し、最後に保存を押下します
![WS000021.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2638718/87a1f08b-2bf7-a172-979c-36b6c2c28289.jpeg)

インストールが実行され、アクティビティ一覧に表示されるようになります
![WS000022.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2638718/fe38becc-c076-5bac-76d7-6fb328845ffc.jpeg)

2 .実際に作成したアクティビティを使用します。
通常のアクティビティ同様、Input、Outputに変数または固定値を入力します
※Input、OutputはUiPathActivityCreatorでのアクティビティ作成時に設定したものになります
今回はInputは予め用意したローカルDB接続情報（固定値）、Outputはretという変数に設定するようにしました
![WS000024.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2638718/ec510d90-2d16-0410-2547-b0bf5c46ce29.jpeg)

# 動作確認
ブレークポイントを設定して、デバックで実行してみます。
Output変数、retの中にSELECTの実行結果が入っていることが確認できました
![WS000025.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2638718/28743542-6ff1-ba16-cc07-ab9d41e02f79.jpeg)

ちなみに、テスト用のDBの中身はこうなってます。
![WS000026.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2638718/4a9a6447-e962-35e6-94d9-c4e5ab477405.jpeg)

# まとめ
今回は実践編ということで、実際にUiPathActivityCreatorを使って、カスタムアクティビティ作成、利用をしてみました。
C#での実装の内容を変えれば他にも色々使えそうだと思いました。
ただ、以下点が気になりました。
* Input、Output定義を途中で変更したい場合、修正ウィンドウみたいなものが見当たらない
（途中で変数の定義漏れに気づいたのですが、やり方がわかりませんでした…）
* アクティビティ定義、Input、Output定義のウィンドウで行の並べ替えが出来ない
（あったら嬉しいなぁと思いました）

とは言え、従来より格段にカスタムアクティビティ作れるようになっているので、
拡張機能としてはかなり便利だと思います。

今後も.Net6対応等アップデートされていくみたいなので、個人的にも楽しみです！

# 参考資料
* [C#(.NET系)からPostgreSQLへ接続(TransactionScopeや分散トランザクションについて)](https://qiita.com/mimitaro/items/fde451da2f722fe12072#transactionscope%E3%81%AB%E3%82%88%E3%82%8B%E3%83%88%E3%83%A9%E3%83%B3%E3%82%B6%E3%82%AF%E3%82%B7%E3%83%A7%E3%83%B3%E7%AE%A1%E7%90%86)
* [UiPathActivityCreatorを利用してカスタムアクティビティを作ってみた（作り方編）](https://qiita.com/Y_Hakiai/items/a1535c58426100c46c68)