- 題名: DatasetからRecordsetへの変換
- 日時: 2014/03/19 17:26:47
- ID: 32198
- この記事の返信元:
- (なし)
- この記事への返信:
- [32200] Re[1]: DatasetからRecordsetへの変換2014/03/19 18:48:49
- [32201] Re[1]: DatasetからRecordsetへの変換2014/03/19 18:56:21
- ツリーを表示
■No32198に返信(さくらさんの記事)
> Dataset(System.Data.Dataset)に入っているデータを
> ADODB.Recordsetに移すことは可能でしょうか?
DataTable → Recordset ではなく、
DataSet → Recordset なのですか?
複数のテーブルのリレーション構造を、Recordset で表現するのは難しいです。
ある程度までは、MSDataShape プロバイダーで表現できますが…。
とりあえず下記では、DataTable に対して ToRecordset メソッドを追加してみました。
Module Module1
#Region "DataTable から Recordset への変換"
<System.Runtime.CompilerServices.Extension()> _
Public Function ToRecordset(tbl As DataTable) As ADODB.Recordset
Dim rs As New ADODB.Recordset
Dim fields As New ArrayList()
For Each col As DataColumn In tbl.Columns
Dim n = col.ColumnName
fields.Add(n)
Dim t = Convert(col.DataType)
Dim a = ADODB.FieldAttributeEnum.adFldUpdatable
If col.ReadOnly Then a = a And Not ADODB.FieldAttributeEnum.adFldUpdatable
If col.AllowDBNull Then a = a Or ADODB.FieldAttributeEnum.adFldIsNullable
If a = 0 Then a = ADODB.FieldAttributeEnum.adFldUnspecified
rs.Fields.Append(n, t, 0, a)
Next
Dim cols = fields.ToArray()
rs.Open()
For Each row As DataRow In tbl.Rows
If row.RowState = DataRowState.Deleted Then Continue For
rs.AddNew(cols, row.ItemArray)
Next
If Not rs.EOF Then rs.MoveFirst()
Return rs
End Function
Private Function Convert(t As Type) As ADODB.DataTypeEnum
Select Case t
Case GetType(DateTime) : Return ADODB.DataTypeEnum.adDBTime
Case GetType(Decimal) : Return ADODB.DataTypeEnum.adDecimal
Case GetType(Boolean) : Return ADODB.DataTypeEnum.adBoolean
Case GetType(Integer) : Return ADODB.DataTypeEnum.adInteger
Case GetType(Single) : Return ADODB.DataTypeEnum.adSingle
Case GetType(Double) : Return ADODB.DataTypeEnum.adDouble
Case GetType(Byte()) : Return ADODB.DataTypeEnum.adBinary
Case GetType(String) : Return ADODB.DataTypeEnum.adBSTR
Case GetType(Short) : Return ADODB.DataTypeEnum.adSmallInt
Case GetType(Byte) : Return ADODB.DataTypeEnum.adTinyInt
Case GetType(Long) : Return ADODB.DataTypeEnum.adBigInt
Case Else : Return ADODB.DataTypeEnum.adVarWChar
End Select
End Function
#End Region
End Module
■No32201に追記(魔界の仮面弁士の記事)
> DataTable.WriteXml で保存して、それを
> Workbooks.OpenXML "C:\TEMP\TEST.XML" , , xlXmlLoadImportToList
> で読み込ませてみるとか。
さらに別案。VBA 側で
Set rs = CreateObject("ADODB.Recordset")
rs.Open "C:\TEMP\SAMPLE.XML"
で読み取れるタイプの XML ファイルを .NET 側で生成してみました。
これは、
「rs.Save "C:\TEMP\SAMPLE.XML", adPersistXML」
で出力されるタイプのファイル形式です。
(本来は編集状態なども管理できる形式ですが、ここでは細かい情報を端折っています)
参考資料:
http://support.microsoft.com/KB/316337
'--------------------------------------------------
Imports <xmlns:rs='urn:schemas-microsoft-com:rowset'>
Imports <xmlns:z='#RowsetSchema'>
Imports <xmlns:s='uuid:BDC6E3F0-6DA3-11d1-A2A3-00AA00C14882'>
Imports <xmlns:dt='uuid:C2F41010-65B3-11d1-A29F-00AA00C14882'>
Module Module1
Public Sub PersistXML(ByVal tbl As DataTable, filePath As String)
Dim doc =
<xml
xmlns:s='uuid:BDC6E3F0-6DA3-11d1-A2A3-00AA00C14882'
xmlns:dt='uuid:C2F41010-65B3-11d1-A29F-00AA00C14882'
xmlns:rs='urn:schemas-microsoft-com:rowset'
xmlns:z='#RowsetSchema'>
<s:Schema id='RowsetSchema'>
<s:ElementType name="row" content="eltOnly" rs:updatable="true">
<%= tbl.Columns.OfType(Of DataColumn)().
Select(Function(c, i) GetSchema(c, i + 1)) %>
</s:ElementType>
</s:Schema>
<rs:data>
<rs:insert>
<%= From row In tbl.AsEnumerable()
Where (row.RowState <> DataRowState.Deleted)
Select <z:row
<%=
From col As DataColumn In tbl.Columns
Select New XAttribute(col.ColumnName, row(col))
%>/>
%>
</rs:insert>
</rs:data>
</xml>
doc.Save(filePath)
End Sub
Private Function GetSchema(col As DataColumn, idx As Integer) As XElement
Dim tname As String
' dt:type 属性値を決定しているところ。面倒ならすべて string でも可。
Select Case col.DataType
Case GetType(Date) : tname = "dateTime"
Case GetType(String) : tname = "string"
Case GetType(Integer) : tname = "int"
'Case GetType(Decimal) : tname = "number" '※
Case Else : tname = "float"
End Select
'※ dt:type="number" 型も使えるが、その場合は rs:dbtype 属性が必要
Dim elm = <s:AttributeType
name=<%= col.ColumnName %>
rs:number=<%= idx %>
rs:write="true">
<s:datatype
dt:type=<%= tname %>
dt:maxLength=<%= col.MaxLength %>
rs:maybenull=<%= col.AllowDBNull %>
/>
</s:AttributeType>
Return elm
End Function
End Module
■No32202に返信(さくらさんの記事)
> 試すのが楽しみです。
> 進んで出勤したい気分なんて久々です(笑)
失礼しました。
うっかり、VB2008 という箇所を読み落としていました。
そのままだとコンパイルエラーとなりますので、適宜修正をお願いします。
No32200 & No32204
> Case GetType(DateTime)
.NET 4 の System.Type には 比較演算子が実装されていますが、
.NET 3.5 以下の System.Type には、比較演算子が実装されていません。
そのため VB2008 では、 先述の「Select Case Type値」構文は
コンパイルエラーとなってしまいます。
型の .FullName による比較で代用するか、もしくは
If 文を用いた表現などに置き換えてみてください。
No32204
> Dim doc =
> <xml
この部分では、行継続文字(行末の「空白+アンダーバー」)を省略しています。
VB2008 の場合は、上記箇所でエラーとなるため、
Dim doc = _
<xml
のように記述するか、もしくは
Dim doc = <xml
のように、改行せずに記述するようにしてください。
また、<s:ElementType> 直下に置いた
> <%= tbl.Columns.OfType(Of DataColumn)().
> Select(Function(c, i) GetSchema(c, i + 1)) %>
と書かれている Linq 式の記述に対しても、
<%= tbl.Columns.OfType(Of DataColumn)(). _
Select(Function(c, i) GetSchema(c, i + 1)) %>
のように、行末に「 _」の行継続文字を補完する必要があります。
<rs:insert> 直下の Linq 構文も同様です。
分類:[.NET]
VB2008にて開発しています。
Dataset(System.Data.Dataset)に入っているデータを
ADODB.Recordsetに移すことは可能でしょうか?
逆のRecordsetの内容をDatasetに入れる方法なら見つかるのですが
その反対が見つかりません。
目的としては、DBからDatasetへデータを取得するvb.netのDLLがあり
それを根幹の部分は変えずにExcelVBAで使えるようにしたいのです。
Excel側ではRecordsetのように1行ずつ読み込んで処理したり
フィールド名を指定して値を読み込んだりといった
ことがしたいです。
ですので、Recordsetに変換できるのなら一番ありがたいですが、
無理であれば他のExcelVBAで扱えるデータ形式に変換するのでもかまいません。
ちなみに、今まで試した方法としては、
dataset.writexmlでXMLの文字列にしてExcelに渡し、
それをExcel側でMSXML2.DOMDocumentオブジェクトに入れるところまでは
成功したのですが、どうも使い勝手が悪いようで、他の方法を探しています。
アドバイスいただけますと助かります。