解決したいこと
ClickOnceなどで、Webサービス(asmx)をサービス参照した場合に、改行コードが自動的にCRLFからLFに変わるという問題が発生します。
しかし、そのままでは次のような問題が出てきます。
- 複数行テキストボックスで改行が反映されない
- ファイル出力時に改行が反映されない
原因
詳細は検索いただければと思いますが、SOAPの仕様も踏まえ、クライアントまでは正しくたどり着いているが、自動生成されるプロキシクラスでは、既定でCRが消失するという動きとなるようです。
Webメソッドのstring型の戻り値に含まれる改行コードが変換されてしまう
解決方法
WCFを使用しない.NET 2.0までの参照方法であるWeb参照なら、SoapHttpClientProtocolを継承していることもあり、Partialクラスを使用して、スマートに解決できます。
しかし、サービス参照を使用した場合のスマートな解決方法が見つかりませんでした(Partialすべきクラスがわかりませんでした)ので、とりあえずの解決方法を記載します。
もしもっとスマートな解決法があれば、ぜひ教えていただけると助かります!
Webサービス(asmx)をWeb参照した場合(.NET 2.0。.NET 2.0以外では非推奨)
WebMethod中の改行コード(CRLFがLFになっちゃう)
具体的なコードを記載いただいている方がいらっしゃいましたので、上記ページより引用します。
SoapHttpClientProtocol.GetReaderForMessageをオーバーライドすればいい、という情報を得て、
.NET Reflectorで中身を確認したところ、XmlTextReaderのインスタンスをアップキャストして返してるのがわかったので、XmlTextReader.Normalizationをfalseにすることで解決。
コードにしたらこういう感じ。
自動生成のコードには手を入れたくないので、パーシャルクラス様様です。
public partial class HogeService
{
protected override XmlReader GetReaderForMessage(SoapClientMessage message, int bufferSize)
{
XmlTextReader reader = (XmlTextReader)base.GetReaderForMessage(message, bufferSize);
reader.Normalization = false;
return reader;
}
}
Webサービス(asmx)をサービス参照した場合(.NET 3.5~)
単純に、Webサービスから取得直後に処理するのみという方法です。
データの処理をクライアント側の使用箇所ごとにで行うため、修正箇所も増え、Document-View(参考1、参考2)のアーキテクチャ的にも、本来は望ましくない暫定的な方法です。
Public Class SoapNormalization
Public Shared Sub EditDataSet(inV As DataSet)
For Each item As DataTable In inV.Tables
EditDataTable(item)
Next
End Sub
Public Shared Sub EditDataTable(inV As DataTable)
' 速度を考慮するなら、String列の判定は1回のみ行う
inV.AsEnumerable.ToList.ForEach(
Sub(row)
For colIndex = 0 To inV.Columns.Count - 1
If inV.Columns(colIndex).DataType Is GetType(String) Then
' 文字列のみ処理する
row(colIndex) = NormalizeString(row(colIndex))
End If
Next
End Sub)
End Sub
Private Shared Function NormalizeString(v As Object) As Object
If IsDBNull(v) Then Return v
If IsNothing(v) Then Return v
Dim ev = $"{v}"
ev = ev.Replace(vbLf, vbCrLf)
Return ev
End Function
End Class
<WebMethod()>
Public Function GetDataSet() As DataSet
Dim ds = New DataSet("dummy")
Dim dt = New DataTable
dt.Columns.Add("c1", GetType(String))
dt.Columns.Add("c2", GetType(Integer))
dt.Columns.Add("c3", GetType(Decimal))
dt.Columns.Add("c4", GetType(Date))
dt.Rows.Add($"Hello World{vbNewLine}2行目{vbNewLine}3行目{vbNewLine}", 55, 678.9, Now.AddMonths(10))
dt.Rows.Add($"Hello World", 1, 2.3, DBNull.Value)
dt.Rows.Add(DBNull.Value, DBNull.Value, DBNull.Value, DBNull.Value)
dt.TableName = "t1"
ds.Tables.Add(dt)
Return ds
End Function
Dim sv = New WebService1.WebService1SoapClient
Dim ds = sv.GetDataSet
'改行コード対策
SoapNormalization.EditDataSet(ds)
Dim dt = ds.Tables(0)
TextBox1.DataBindings.Add("Text", dt, "c1")