[OutSystems]Structure/Entity/Recordの属性は、ExtensionのC#でどう表現されるか

OutSystemsの複合データ型であるStructure/Entity/RecordをExtension (C#) に渡したら、データの型がどうなるかを確認してみた。

# 環境
Personal Environment(Version 11.17.0 (Build 36291))
Service Studio(Version 11.53.13)
Integration Studio(Version 11.11.5)

# サンプルモジュール
[Forgeコンポーネント: HousesoftSampleExtension](https://www.outsystems.com/forge/component-overview/13723/housesoftsampleextension)のV1.0.3。

- Demoモジュール > MainFlow > AttributeInDotNet Screen
- サンプルAction (Extension): RetrieveAttributeInformation

# 確認結果のまとめ

- 渡した値がStructure/Recordの場合、属性はFieldとして定義されており、TypeオブジェクトのGetFieldsで取得できる。名前は「ss<属性名>」
- 渡した値がEntityの場合、属性はPropertyとして定義されており、TypeオブジェクトのGetPropertiesで取得できる。名前は「ss<属性名>」
    - 渡した値がジョインしたEntityの場合、渡した値の直下にはFieldとしてジョインされた各Entityが含まれる。名前は「ssEN<Entity名>」

# 確認方法
1. 入力変数（object型、呼び出すときにBuilt-In FunctionのToObjectで変換して渡す）のTypeオブジェクトを取得
1. Typeオブジェクトを経由して入力変数のFieldを列挙して名前・型を取得して出力変数のリストに追加
1. Typeオブジェクトを経由して入力変数のPropertyを列挙して名前・型を取得して出力変数のリストに追加

``` cs
public void MssRetrieveAttributeInformation(object ssInput, out RLAttributeInfomationRecordList ssAttributeInformationList) {
	ssAttributeInformationList = new RLAttributeInfomationRecordList();
	var type = ssInput.GetType();
	var fields = type.GetFields();
	foreach (var field in fields)
	{
		var temp = new RCAttributeInfomationRecord();
		temp.ssSTAttributeInfomation = new STAttributeInfomationStructure()
		{
			ssName = field.Name,
			ssDataType = field.FieldType.FullName,
			ssMemberType = "Field"
		};
		ssAttributeInformationList.Add(temp);
	}
	var props = type.GetProperties();
	foreach (var prop in props)
	{
		var temp = new RCAttributeInfomationRecord();
		temp.ssSTAttributeInfomation = new STAttributeInfomationStructure()
		{
			ssName = prop.Name,
			ssDataType = prop.PropertyType.FullName,
			ssMemberType = "Property"
		};
		ssAttributeInformationList.Add(temp);
	}
}
```

# データの種類ごとの確認結果
以前書いた[ExtensionのC#コードに渡ってくるデータの型について](https://qiita.com/jyunji_watanabe/items/624aeba2e1293ae0f7e8)　と同じ種類のデータを使って確認した。各種類のデータに対して属性情報を抜き出してみた結果は以下の通り（リスト系のデータに対するテストは除外した）。

## Structure：シンプル
### 入力データの定義
Text型・Integer型のAttributeを持つStructure SampleStructureを使ってテスト。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/332987/fa0d441c-cf04-8a29-b27d-03290590950b.png)

### 結果
各属性は、C#では、Structureに対応するClassのFieldとして定義される（TypeオブジェクトのGetFieldsで取得できる）。
Field名：ss<属性名>
型：対応するビルトインデータ型（TextならSystem.Stringなど）

## Structure：子Structure有
### 入力データの定義
こちらは、同じStructureであっても、Attributeに別のStructure型のものを持つ入れ子形式。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/332987/8afec505-1890-1469-8dd1-8c88fb19481f.png)

### 結果
各属性はFieldとして定義される。
Field名：ss<属性名>
型：OutSystemsのBasic Data Typeであれば、対応するビルトインデータ型（Date TimeならSystem.DateTime）。Structureについては、「ss\<Structureを定義したモジュール名\>.ST\<Structure名\>Structure」。

## Structure：Extensionに定義
### 入力データの定義
シンプルなStructureをExtension内に定義した場合のテスト。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/332987/1321c854-9e56-1d41-7ea2-e7254cc1c4f9.png)

### 結果
各属性はFieldとして定義される。
Field名：ss<属性名>
型：対応するビルトインデータ型（TextならSystem.Stringなど）

## Entity：単独・１レコード
### 入力データの定義
Extensionに渡る値が、StructureとEntityでは扱いが異なる。
以下のようにService Studioで定義したEntity型の1レコードを直接Extensionに渡した場合。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/332987/ed2f5933-833f-5bc9-084b-ac94017fb5cb.png)

### 結果
Entityの場合、属性はFieldとPropertyの両方に定義される。つまり値の本体はprivateのFieldとして名前に「_」（アンダースコア）をつけて定義され、Class外には直接Fieldを公開せず、Propertyを経由して操作させる、というやり方。ここではPropertyの方を見ておく。

こういった定義をC#のコードで見るには、外部DB用のExtensionを作成し、そのソースコードをIntegration Studioで生成するとよい。

Property名：ss<属性名>
型：対応するビルトインデータ型（TextならSystem.Stringなど）

## Entity：ジョイン・１レコード
### 入力データの定義
こちらは、複数のEntityをAggregateでジョインし、結果のList.Currentを渡した場合。
以下のように2 EntityをジョインするAggregateを定義し、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/332987/47987303-8528-bb2f-52d7-ee832147a200.png)
ExtensionのActionにはその結果の1レコード、List.Currentを渡している。
```
ToObject(GetSampleEmployees.List.Current)
```

### 結果
ここではジョインした結果を渡しているため、入力変数の直下には、ジョインした各Entity型がぶら下がる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/332987/e95be5f7-68c5-624c-f755-5b11d2217354.png)
この場合、Entity型に対応する属性はFieldとして定義されている。
Field名：ss<Entity名>
型：「ss\<モジュール名\>.EN\<Entity名\>EntityRecord」

## Record：Data Type Editor
### 入力データの定義
Service Studio上でData TypeとしてRecordを選択すると、その場でRecordの型を定義できる。その形式の入力を使ったテスト。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/332987/38c25ceb-b3f3-f389-a6f5-0057c1e1ffa1.png)
以下のようにText型の属性のみを持つRecordを定義した。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/332987/63f16667-4c29-4763-9309-02324755b741.png)

### 結果
各属性はFieldとして定義される。
Field名：ss<属性名>
型：対応するビルトインデータ型（TextならSystem.Stringなど）