[OutSystems]Extensionからthrowする例外

C#で書くExtensionで発生した例外は、OutSystemsのLow-Code部分でどう処理されるのかを確認してみます。
Extensionで色々なパターンで例外を発生させてみて、Action Flowでどの様にHandleされるか。

# 確認環境
Personal Environment(Version 11.12.0 (Build 30002))
Service Studio (Version 11.11.6)
Integration Studio (Version 11.11.5)

# サンプルモジュール
[Forgeコンポーネント](https://www.outsystems.com/forge/component-overview/7086/housesoftsamplereactive)のV1.0.10。
MainFlow > Exception Screen

# 結論
Extensionで発生した例外はAllExceptionsでハンドルする。
ハンドルした例外のExceptionMessageプロパティには、C#例外オブジェクトのmessageが設定される。
Exceptionが入れ子になっている（InnerExceptionが設定されている）場合でも一番上位の例外のmessageしか取れない。
Service CenterのErrorログでStack欄を見れば、Extension内の例外の発生順序は確認できる。

# Extension Actionで直接発生した例外
以下のように、Extension Actionの先頭でいきなり例外をthrowさせた場合。
MyExceptionはテンプレートそのままのシンプルな例外クラス。

``` cs
public void MssExceptionTest1() {
	if (1 == 1)
		throw new MyException("Actionメソッドから直接例外をthrowする");
}
```

Action Flowでは、各種の例外に対してHandle Exceptionを置いて、デバッガでどのパスに来るかを確認しました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/332987/aa82474c-5701-f2d4-692d-78452cd4c17f.png)

以下の通り、AllExceptionsでHandleされ、Extension内で例外オブジェクトに渡したメッセージが、ExceptionMessageプロパティに届いてます。UserExceptionとかではハンドルできないんですね。Extensionで発生した例外について、Action Flowで利用できるのは、せいぜい「例外が発生した事実」と「例外メッセージ」くらい。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/332987/f34dea8b-cb4b-37bc-aebf-34c65ec88ed0.png)

Error Log (Service Center)に出力された、Source=「Extension metho」のログのStack。

```
Actionメソッドから直接例外をthrowする
   at OutSystems.NssExceptionExtension.CssExceptionExtension.MssExceptionTest1()
   at ssAssociateReactive.RssExtensionExceptionExtension.MssExceptionTest1(HeContext heContext)
```

# 別のクラスで発生した例外
Extension Actionから別のclassのメソッドを呼び出し、そこで例外を発生させた場合。

## Extension Actionでは例外処理しない場合
例外を発生させるClass1.method1()を呼び、try-catchによる処理はしない場合。

``` cs
public void MssExceptionTest2() {
	var obj = new Class1();
	obj.method1(); // throw new MyException("他のclassで例外を発生させる");
}
```

この場合もAllExceptions。呼び出したclassで設定した例外メッセージが、ExceptionMessageプロパティに。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/332987/b6ca832c-d9f3-23c6-ac35-ab60a80c14c3.png)

Stack。

```
他のclassで例外を発生させる
   at OutSystems.NssExceptionExtension.Class1.method1()
   at ssAssociateReactive.RssExtensionExceptionExtension.MssExceptionTest2(HeContext heContext)
```

## Extension Actionで例外をリスローした場合
try-catchで例外を発生させる処理を囲み、catchした例外には手を触れずにそのままリスローする場合。

``` cs
public void MssExceptionTest3() {
	try
	{
		var obj = new Class1();
		obj.method1(); // throw new MyException("他のclassで例外を発生させる");
	}
	catch (Exception)
	{
		throw;
	}

}
```

ExceptionMessageは例外をcatchしなかった場合と同じ。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/332987/6637cdbd-6108-42d4-cfc0-0ad6e9dda2a7.png)

Stackを確認すると、２行目にthrow;を行っているメソッドが追加されています。

```
他のclassで例外を発生させる
   at OutSystems.NssExceptionExtension.Class1.method1()
   at OutSystems.NssExceptionExtension.CssExceptionExtension.MssExceptionTest3()
   at ssAssociateReactive.RssExtensionExceptionExtension.MssExceptionTest3(HeContext heContext)
```

## Extension Actionでcatchした例外を詰め直す
C#では、try-catchのcatch内で同じ例外をthrowする場合、「throw;」として、「throw ex;」みたいな書き方をしないようにすすめられています。そのおすすめにあえて反した場合どうなるか。catchした例外を別の例外クラスのオブジェクトにくるんで（InnerExceptionとして渡して）throwしています。

``` cs
public void MssExceptionTest4() {
	try
	{
		var obj = new Class1();
		obj.method1();
	}
	catch (Exception e)
	{
		throw new My2Exception("catchした例外を他の例外にラップしてthrowする", e);
	}
}
```

ExceptionMessageには、InnerException（上記コードの「e」オブジェクト）の例外メッセージではなく、throwした例外オブジェクト自体の例外メッセージが設定されていますね。InnerExceptionはロジックから触ることができないようです（リフレクションとか使えば別でしょうが）。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/332987/4e33561d-1ba6-9ba5-afae-b6b9b692510d.png)

Stackは以下のように2つに別れました。

```
[1] catchした例外を他の例外にラップしてthrowする
   at OutSystems.NssExceptionExtension.CssExceptionExtension.MssExceptionTest4()
   at ssAssociateReactive.RssExtensionExceptionExtension.MssExceptionTest4(HeContext heContext)

[2] 他のclassで例外を発生させる
   at OutSystems.NssExceptionExtension.Class1.method1()
   at OutSystems.NssExceptionExtension.CssExceptionExtension.MssExceptionTest4()
```

# 例外的ケース：DatabaseException
以下のように、C#でDatabaseExceptionをthrowしたら、Action Flowでも同名のDataBaseExceptionでハンドルできました。
これはおそらく、OutSystemsのDatabaseExceptionを.NETにコンパイルするときに、.NETのDatabaseExceptionクラスに解決しているということと思われます。つまり、このようにたまたま対応する例外クラスを見つけられる場合は、その例外クラスをthrowすることでAction Flowの分岐にも使えるかもしれない。ただ、ドキュメントにも特に記載が無いようだし、これをあてにした処理を書くのはおすすめできませんが。

``` cs
public void MssExceptionTest5() {
	if (1 == 1)
		throw new DataBaseException("DatabaseExceptionを直接throwしてみる");
}
```