I am sorry if this question is a repeat. I have a system.object variable where I store results for a select query. I need to output the results to a flat file to further process it. I have the following piece of code that works for couple of seconds and then throws the system invocation error. Can you please suggest any edits to this or if I am doing something wrong:
Public Sub Main()
Dim x As New OleDb.OleDbDataAdapter
Dim dt As New DataTable
Dim str As String = vbNullString
If System.IO.File.Exists("D:\BKP\AD.txt") = False Then
System.IO.File.Create("D:\BKP\AD.txt")
End If
'MessageBox.Show("Hello")
Dim i As Int32
x.Fill(dt, Dts.Variables("User::LDAPResultSet").Value)
i = dt.Rows.Count
For j As Int32 = 0 To i - 1
str = str & Join(dt.Rows.Item(j).ItemArray(), ",") & vbCrLf
Next
Dim objWriter As New System.IO.StreamWriter("D:\BKP\AD.txt")
objWriter.Write(str)
objWriter.Close()
End Sub
End Class
Is there a better way to write this or if there's an alternative code piece I'd like to try that as well. Thank you for your time.
The points from my comment:
You don't need to create the file: the StreamWriter will do that if required, otherwise it will overwrite an existing file.
String.Join(separator, values) - you have the separator as the second argument.
You should also call objWriter.Dispose() when you've finished using it.
But:
A StringBuilder is more efficient for creating a large string.
You can write all the text in one go by using the File.WriteAllText method.
Sub Main()
Dim outputFile As String = "D:\BKP\AD.txt"
Dim x As New OleDb.OleDbDataAdapter
Dim dt As New DataTable
Dim sb As New Text.StringBuilder
x.Fill(dt, Dts.Variables("User::LDAPResultSet").Value)
For j As Integer = 0 To dt.Rows.Count - 1
sb.AppendLine(String.Join(",", dt.Rows.Item(j).ItemArray()))
Next
IO.File.WriteAllText(outputFile, sb.ToString())
End Sub
I guess that you left some lines to do with the OleDbDataAdapter out, but I am not familiar with SSIS.
If you can, use Option Strict On - it would have pointed out the problem with the String.Join for you.
I have done it this way in the past:
https://www.timmitchell.net/post/2015/04/20/using-the-ssis-object-variable-as-a-data-flow-source/
Basically pass the variable into a Script Transformation and then add data to the pipeline. From that point you can use a destination component as normal and avoid creating the output file and delimiting the fields.
#region Namespaces
using System;
using System.Data;
using Microsoft.SqlServer.Dts.Pipeline.Wrapper;
using Microsoft.SqlServer.Dts.Runtime.Wrapper;
#endregion
// Add in the appropriate namespaces
using System.Data;
using System.Data.OleDb;
[Microsoft.SqlServer.Dts.Pipeline.SSISScriptComponentEntryPointAttribute]
public class ScriptMain : UserComponent
{
public override void CreateNewOutputRows()
{
// Set up the DataAdapter to extract the data, and the DataTable object to capture those results
OleDbDataAdapter da = new OleDbDataAdapter();
DataTable dt = new DataTable();
// Extract the data from the object variable into the table
da.Fill(dt, Variables.vResults);
// Since we know the column metadata at design time, we simply need to iterate over each row in
// the DataTable, creating a new row in our Data Flow buffer for each
foreach (DataRow dr in dt.Rows)
{
// Create a new, empty row in the output buffer
SalesOutputBuffer.AddRow();
// Now populate the columns
SalesOutputBuffer.SalesOrderID = int.Parse(dr["SalesOrderID"].ToString());
SalesOutputBuffer.RevisionNumber = int.Parse(dr["RevisionNumber"].ToString());
SalesOutputBuffer.OrderDate = DateTime.Parse(dr["OrderDate"].ToString());
SalesOutputBuffer.ShipDate = DateTime.Parse(dr["ShipDate"].ToString());
SalesOutputBuffer.Status = int.Parse(dr["Status"].ToString());
SalesOutputBuffer.TotalDue = decimal.Parse(dr["TotalDue"].ToString());
}
}
}
This is what worked for me finally:
public override void CreateNewOutputRows()
{
// Set up the DataAdapter to extract the data, and the DataTable object to capture those results
OleDbDataAdapter da = new OleDbDataAdapter();
DataTable dt = new DataTable();
// Extract the data from the object variable into the table
da.Fill(dt, Variables.LDAPResultSet);
// Since we know the column metadata at design time, we simply need to iterate over each row in
// the DataTable, creating a new row in our Data Flow buffer for each
foreach (DataRow dr in dt.Rows)
//'foreach (DataColumn col in dt.Columns)
{
{
// Create a new, empty row in the output buffer
LDAPOutputBuffer.AddRow();
object[] array = dr.ItemArray;
LDAPOutputBuffer.ADENTName = array[1].ToString();
LDAPOutputBuffer.DisplayName = array[3].ToString();
LDAPOutputBuffer.DNName = array[2].ToString();
LDAPOutputBuffer.Mail = array[0].ToString();
}
}
}
}
Related
How we can get a data table raw data into a Single type Array variable?
Like;
Dim price_range1() As Single = {10.4, 9.6, 6.8, 5.6, 4.4}
Dim price_range2() As Single = {5.2, 4.8, 3.4, 2.8, 2.2}
Dim price_range3() As Single = {2.6, 2.4, 1.7, 1.4, 1.1}
I'm already getting all data into the DataGrid. But I need to get those raw data as a variable.
DataGridView Table
Like that kind of variables
Imports MySql.Data.MySqlClient
Public Class showitems
Public commmand As New MySqlCommand
Public adapter As New MySqlDataAdapter
Public datatable As New DataTable
Private Sub showitems_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim var As New ArrayList
If Db_conn() = True Then
Dim sql As String
sql = "SELECT id, range1,range2,range3,range4,range5 FROM `pioltprice` WHERE id = 5 OR id = 6 OR id = 7 "
command.CommandText = sql
command.Connection = conn
adapter.SelectCommand = command
adapter.Fill(datatable)
DataGridView1.DataSource = datatable
Else
MsgBox("Error occurred")
End If
End Sub
End Class
Use MySql DataTable, MySql DataAdapter
You will need to take the values and include them in your array.
If you want to reuse the DataTable that you're already filling, then you could do the following after adapter.Fill(datatable):
Get the Row by its index
Get the ItemArray of the selected DataRow
Skip the first cell in the ItemArray (which is the Id)
Convert the values to a Single
Dim price_range1() As Single
If (datatable.Rows().Count > 0) Then
price_range1 = datatable.Rows().Item(0).ItemArray().Skip(1).Select(Function(cell) Single.Parse(cell)).ToArray()
End If
Dim price_range2() As Single
If (datatable.Rows().Count > 1) Then
price_range2 = datatable.Rows().Item(1).ItemArray().Skip(1).Select(Function(cell) Single.Parse(cell)).ToArray()
End If
Dim price_range3() As Single
If (datatable.Rows().Count > 2) Then
price_range3 = datatable.Rows().Item(2).ItemArray().Skip(1).Select(Function(cell) Single.Parse(cell)).ToArray()
End If
Live Demo: https://dotnetfiddle.net/pdEAkz
I'm fetching data from database as a DataTable and need to convert into CSV string in VB.NET.
Create a generic method with DataTable, CSV Headers, DataTable Columns parameters:
Private Function CSVBuilder(dt As DataTable, headers As List(Of String), columns As List(Of String)) As String
Dim sCSV = New StringBuilder(String.Join(",", headers))
sCSV.Append(Environment.NewLine)
Dim view As New DataView(dt)
Dim tDt As DataTable = view.ToTable(True, columns.ToArray)
For Each row As DataRow In tDt.Rows
'-- Handle comma
sCSV.Append(String.Join(",", (From rw In row.ItemArray Select If(rw.ToString.Trim.Contains(","), String.Format("""{0}""", rw.ToString.Trim), rw.ToString.Trim))))
sCSV.Append(Environment.NewLine)
Next
Return sCSV.ToString
End Function
And then call in your code to get CSV string:
CSVBuilder(dataTable,
New List(Of String) From {"Header Column 1", "Header Column 2", ...},
New List(Of String) From {"DataTableColumn1", "DataTableColumn2", ...})
In response to the comment, since this wouldn't fit in that space:
Private Function CSVBuilder(dt As DataTable) As String
Dim sCSV As New StringBuilder()
'Headers
Dim delimeter As String = ""
For Each col As String In dt.Columns.Select(Func(col) col.ColumnName)
If col.Contains(",") Then col = """" & col & """"
sCSV.Append(delimeter).Append(col)
delimeter = ","
Next
sCSV.AppendLine()
For Each row As DataRow In tDt.Rows
sCSV.AppendLine(String.Join(",", (From rw In row.ItemArray Select If(rw.ToString.Trim.Contains(","), String.Format("""{0}""", rw.ToString.Trim), rw.ToString.Trim))))
Next
Return sCSV.ToString
End Function
Now, I did remove this code:
Dim view As New DataView(dt)
Dim tDt As DataTable = view.ToTable(True, columns.ToArray)
But I wouldn't do this as part of the CSVBuilder() method. If you want to project a specific view of a table, I would do that separately from creating the CSV data. You could make a separate method for it:
Public Function GetProjection(dt As DataTable, columns As IEnumerable(Of String)) As DataTable
Dim view As New DataView(dt)
Return view.ToTable(True, columns.ToArray())
End Function
And then you call them together like this:
Dim dt As DataTable = '.... original table here
Dim columns() As String = '... the columns you want
Dim csv As String = CSVBuilder(GetProjection(dt, columns))
or like this:
Dim dt As DataTable = '.... original table here
Dim columns() As String = '... the columns you want
Dim dt1 = GetProjection(dt, columns)
Dim csv As String = CSVBuilder(dt1)
This is called Currying, and it's a good thing to do.
Finally, I'll repeat my suggestion to think in terms of writing to a stream. Long strings with repeated append operations can cause real problems for the .Net garbage collector. Using StringBuilder can help, but won't fully eliminate these problems. Writing to a Stream, which is often connected to a file on disk, gives you the opportunity to completely eliminate this issue. Plus, it will likely save you work later on.
I am calling a function that returns a data set. However, to generate the data set I have to grab a parameter from another table, which necessitates another data set being created in the function to retrieve that parameter. The issue is that once the first data set is created to get the parameter for the second data set, the function is returning to the calling code, rather than running to the return statement. Is there a way around this?
Example:
Private Sub Form_Load()
Dim dataset as new dataset
dataset = GetList(PathToDB, AccessCode)
End Sub
Function GetList(Path as String, AccessCode as String)
Dim ListConnectionString as String = "..."
Dim Listds as New DataSet
Dim Listcnn as OleDbConnection = New OleDbConnection(ListConnectionString)
Dim ListAdapter As New OleDbDataAdapter
Dim Parameterds As New DataSet
Dim ParameterAdapter As New OleDbDataAdapter
Dim ParameterSelectQuery As String = "..."
Dim ParameterSelectCommand As New OleDbCommand(ParameterSelectQuery, Listcnn)
ParameterAdapter.SelectCommand = ParameterSelectCommand
******** ParameterAdapter.Fill(Parameterds) ********
Dim Parameter As String = Parameterds.Tables(0).Rows(0).Item(0)
Dim ListSelectQuery As String = "...WHERE Value = '" & Parameter & "';"
Dim ListSelectCommand As New OleDbCommand(ListSelectQuery, Listcnn)
ListAdapter.SelectCommand = ListSelectCommand
ListAdapter.Fill(Listds)
Return Modelds
End Function
The code is returning on the line with ********, but should return on the return statement. I've also tried this with separate functions, but it still does the same thing.
I have a datatable where i am trying to do
datatable.Select(Name Like '#%#') but getting error that invalid pattern(expecting result of a table with name col having #Mike#,#Brow#..). Using escape sequense dint for all items dint work fine too. Many suggest to use Linq - but am new to it. How can i do this filter with Linq from this datatable.
This is a sample of what i was trying to do..
Dim dtSamp As Data.DataTable
dtSamp = New Data.DataTable
dtSamp.Columns.Add("Name")
dtSamp.Columns.Add("Marks")
Dim dr As DataRow
dr = dtSamp.NewRow()
dr.Item(0) = "AAA"
dr.Item(1) = "50"
dtSamp.Rows.Add(dr)
dr = dtSamp.NewRow()
dr.Item(0) = "#bbb#"
dr.Item(1) = "60"
dtSamp.Rows.Add(dr)
dr = dtSamp.NewRow()
dr.Item(0) = "ccc"
dr.Item(1) = "44"
dtSamp.Rows.Add(dr)
Dim drResult As DataRow()
drResult = dtSamp.Select("Name Like '#%#'")
Dim dtOutPutTable As Data.DataTable
dtOutPutTable = drResult.CopyToDataTable()
In the dtOutPutTable i was expecting 1 row ie, #bbb# in it.. but the Select function fails.
Generally LINQ queries works on data sources which implement the IEnumerable<T>/ IQueryable<T> Interface. But DataTable does not implement any of these. So we can not directly apply LINQ queries on a DataTable.
But DataTable class has an extension method called AsEnumerable which returns an IEnumerable collection of DataRow. So we can apply the AsEnumerable function on a DataTable and then play with some LINQ on the resulting collection.
var items=(from p in myDataTable.AsEnumerable()
select new { ID= p.Field<int>("ID").
Name=p.Field<string>("Name")
}).ToList();
var filtered=items.Where(x => x.Name.Contains("Mike"));
EDIT : Here is the VB.NET Version ( Disclaimer: I am not a VB.NET guy. but i could build this code without any error)
Dim items = (From p In myDataTable.AsEnumerable()
Select New With {.ID = p.Field(Of Integer)("ID"),
.Name = p.Field(Of String)("Name")}).ToList()
Dim filtered = items.Where(Function(x) x.Name.Contains("Mike")).ToList()
VB
Private Function likes(ByVal dt As DataTable, ByVal column As String, ByVal value As String)
Dim result = dt.Clone()
For Each row As DataRow In From row1 As DataRow In dt.Rows Where (row1(column).Contains(value))
result.ImportRow(row)
Next
Return result
End Function
C#
private DataTable likes(ref DataTable dt, string column, string value)
{
DataTable result = dt.Clone();
foreach (DataRow row in from row1 in dt.Rowswhere (row1(column).Contains(value))) {
result.ImportRow(row);
}
return result;
}
I have the following piece of code that I am using to try and rip a csv file and turn it into a datatable. My problem is that the debugger never makes it to the return statement. Everything is appending to the datatable correctly, so I know that part works. Any idea's on what I can do to trouble shoot this further. Also, if you know of a simpler way to turn a import a csv file to a datatable I'd be very interested in learning about it.
Thanks!
Public Function loadCSVTableII() As DataTable
Dim dt As New DataTable("TableII")
Dim line As String = String.Empty
Dim counter As Integer = 0
Dim reader As New StreamReader(pathTableTwo)
Try
While Not IsNothing(line)
line = reader.ReadLine()
Dim lineSep As String() = line.Split(New Char() {","c})
If Not counter = 0 Then
dt.Rows.Add(lineSep)
counter += 1
Else
For Each value As String In lineSep
dt.Columns.Add(value)
Next
counter += 1
End If
End While
'cursor never gets to this code block...
Dim primarykey(0) As DataColumn
primarykey(0) = dt.Columns("Ages")
dt.PrimaryKey = primarykey
Return dt
Catch ex As Exception
Throw
End Try
End Function
Update: It is erroring out on this line in the code.
Dim lineSep As String() = line.Split(New Char() {","c})
It say that the Object reference is not set to an instance of an object. What's weird though is that it works through the whole data table fine. Could it be that the while loop is not terminating at the end of the file?
Try changing your While loop to handle the end of stream condition. It's not very clear what the IsNothing function is doing in your code.
While Not reader.EndOfStream
line = reader.ReadLine
'// Dim lineSep As String() = line.Split(New Char() {","c})
For your line split, in VB.Net, it's simple to just do this:
Dim lineSep As String() = line.Split(",")
You can use OLEDB provider for this.
string query = "SELECT Symbol, [Name of Company], FROM [just file name with extension]";
string connStr = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" + [csv file path without file name] + ";" + "Extended Properties=’text;HDR=YES;’";
//create dataadapter object
OleDbDataAdapter adapter = new OleDbDataAdapter(query, connStr);
// create table
DataTable dtSymbolDetails = new DataTable("ScriptDetails");
dtSymbolDetails.Columns.Add("Symbol");
dtSymbolDetails.Columns.Add("Name of Company");
// fill the table with data using adapter
adapter.Fill(dtDetails);