しばらくハマったのと、疑問があったのでメモしておきます。
環境
PostgreSQL 12.2, compiled by Visual C++ build 1914, 64-bit
EntityFramework6.Npgsql v6.4.3
作業の流れ
①あるテーブルempに列stを追加。Char(1)で、特定の文字を入れてステータスを管理する。"S"とか"T"とかが入る。また、空文字""は「指定なし」の意味となる。
②DBに①の列を追加。
③PostgreSQLは空文字とNullを区別するので、空文字以外にNullの状態まで管理するとバグの原因になると思い、NOT NULL制約を付与。
④既存のテーブルに列を追加するので、デフォルト値として空文字''を指定。
ALTER TABLE emp ADD COLUMN st char(1) NOT NULL DEFAULT '';
⑤これに併せて、エンティティクラスempにも、以下のプロパティを追加。
<Required>
<StringLength(1)>
Public Property st As String
⑥ところが、実際にEFを使っての更新プログラムにて、
rec.st = ""
の行を追加したところ、DbContext.SaveChagesでエラーに。
どうやらRequired属性を指定した為に引っかかっている模様。Required属性は、空文字を「未入力」として扱うということでしょう。DBのNOT NULLとそのまま対応付けられるわけではないので、注意が必要そうです。
⑦以下のようにすることで、空文字を許容するようになるとのこと。
<Required(AllowEmptyStrings:=True)>
<StringLength(1)>
Public Property st As String
AllowEmptyStringsをTrueにして再度動かしてみると、空文字を指定してもエラーが出なくなった。
DBも正しく空文字が設定されており、問題なし。
懸念事項
ただ、上記のマイクロソフトのページに書かれている次の文言が気になる。
When you set AllowEmptyStrings to true for a data field, Dynamic Data does not perform validation and transforms the empty string to a null value. This value is then passed to the database.
If the database does not allow null values, it raises an error. To avoid this error, you must also set the ConvertEmptyStringToNull to false.
適当に翻訳すると
データフィールドのAllowEmptyStringsをtrueに設定すると、Dynamic Dataは検証を実行せず、空文字列をnull値に変換します。この値はその後データベースへと渡されます。
もしデータベースがnull値を許容しない場合、エラーを発生させます。このエラーを回避する為には、併せてConvertEmptyStringToNullをfalseに設定する必要があります。
今回、フィールドstは、DB側でNOT NULL制約を付与しているので、上記の説明の通りだと空文字を設定した時に値がNullに変換され、DB更新時にNOT NULL制約にひっかかってエラーが発生するはず。
しかしエラーは発生しておらず、データベースにもnullではなく空文字が設定されている。
念のためConvertEmptyStringToNull:=Trueを指定してみても、結果は同様。
つまり、ConvertEmptyStringToNullは動作していないように見える。
一体どういうことになっているのかわかる方がいたらコメントで教えて頂けると助かります。