Inserting a txt file into database - c#

public partial class Form1 : Form
{
public SqlConnection con = new SqlConnection(#"Data Source=(LocalDB)\v11.0;AttachDbFilename=C:\Users\Alexander\Desktop\Archivos.mdf;Integrated Security=True;Connect Timeout=30");
private void Readbtn_Click(object sender, EventArgs e)
{
con.Open();
Propiedades prop = new Propiedades();
List<string> myValues = new List<string>();
string line;
StreamReader file = new StreamReader(#"c:\temp\archivo.txt");
if ((line = file.ReadLine()) != null)
{
string[] fields = line.Split(',');
prop.matricula = fields[0].ToString();
prop.nombre = fields[1].ToString();
prop.sueldo = decimal.Parse(fields[2]);
for (int i = 0; i < fields.Length; i++)
{
listBox1.Items.Add(fields[i]);
}
}
SqlCommand cmd = new SqlCommand("INSERT INTO Archivos(Codigo, Nombre, Sueldo) VALUES (#Matricula, #Nombre, #Sueldo", con);
cmd.Parameters.AddWithValue("#Matricula", prop.matricula);
cmd.Parameters.AddWithValue("#Nombre", prop.nombre);
cmd.Parameters.AddWithValue("#Sueldo", prop.sueldo);
cmd.ExecuteNonQuery();
con.Close();
}
Hello Guys, I just need a little bit of help on modifying this code. I already have this to save the first line of the text file but I dont have any ideas on how to make it read the other lines. Also I would like to validate that if the SQL table contains already that info, it will launch an exception or message box letting the user know that the file already exist. Please help

There are a few things you need to do the the first that jumps out at me is that you want to use a while loop in place of your first if statement
while ((line = file.ReadLine()) != null)
{
string[] fields = line.Split(',');
prop.matricula = fields[0].ToString();
prop.nombre = fields[1].ToString();
prop.sueldo = decimal.Parse(fields[2]);
for (int i = 0; i < fields.Length; i++)
{
listBox1.Items.Add(fields[i]);
}
}
Also depending how you want to display the items in the list box you may want to use instead of that internal for loop:
listBox1.Items.Add(prop.matricula+','+prop.nombre+',prop.sueldo.toString());

I would recomend reading the file line by line, adding the values to a DataTable and then using SQL Bulk Insert to store it to the database.
It would be something like
int batchSize = 10000;
SqlBulkCopy bc = new SqlBulkCopy(con);
DataTable dtArchivos = new DataTable();
dtArchivos.Columns.AddRange(new []
{
new DataColumn("Codigo", typeof(string)),
new DataColumn("Nombre", typeof(string)),
new DataColumn("Sueldo", typeof(decimal)),
});
StreamReader file = new StreamReader(#"c:\temp\archivo.txt");
string line = file.ReadLine();
while (line != null)
{
string[] fields = line.Split(',');
DataRow dr = dtArchivos.NewRow();
dr["Codigo"] = fields[0].ToString();
dr["Nombre"] = fields[1].ToString();
dr["Sueldo"] = decimal.Parse(fields[2]);
dtArchivos.Rows.Add(dr);
if (dtArchivos.Rows.Count == batchSize)
{
bc.WriteToServer(dtArchivos);
dtArchivos.Clear();
}
line = file.ReadLine();
}
bc.WriteToServer(dtArchivos);
I added some logic to do batching, as a large file might cause the application to run out of memory due to the size of the DataTable.

First of all, consider using LINQ for your code - if you are not overly concerned about slightly more overhead, LINQ will make your life a whole lot easier. It will automatically cache the information for you, and can be more efficient for a lot of things, not to mention that the code is a lot easier to understand at times. For this, you would need to, in Visual Studio, create a ado.net model of the database, which shouldn't take too long. The precise way, I think, would be to
File -> Add Item -> Add new Item
or something of the like, then searching for ado.net entity. Then, just fill in the details that it asks. You can then use this entity (if you can't find out how to do it, then please reply so that I can fix it as need be) to add data, using the way that you specified.
Now, the reason why it may not be working code be a code problem. I think using the seperator may be the problem, and the following ammended code may be helpful:
StreamReader file = new StreamReader(#"c:\temp\archivo.txt");
while(file.Read()){
//YOUR CODE FOR CONCATENATING DATA HERE
}
The reason is that the reader would read until it has a return value of False or Null. Your code for some reason seems to be leaving after the first line, and this may solve it.

Related

Use of unassigned local variable - but I know that by the time the program reaches it, it will be assigned

So, I have this section of code:
void Readfile()
{
using (reader = new StreamReader(file))
{
string line = "";
DataTable table;
// Search for relevant "tables" in the file
while ((line = reader.ReadLine()) != null)
{
if (line.StartsWith("!"))
{
table = CreateDataTable(reader, line);
}
else
{
AddToTable(table); // Error: "Unassigned local variable"
}
}
}
}
DataTable CreateDataTable(StreamReader reader, string line)
{
if (line.Contains("!CUST"))
{
DataTable custTable = new DataTable();
custTable.TableName = "Customer";
string[] columns = line.Split(Convert.ToChar(9));
foreach (string s in columns)
{
custTable.Columns.Add(s);
}
return custTable;
}
return null;
}
The file this program is reading from will always be in this format:
!Line1
Line2
Line3
!Line4
[etc...]
So I know that this code is sound, in terms of "flow". It will always Create the Table first, before it adds to it. However, the way I have structured the code clearly doesn't work.
My original idea was that if I did create the DataTable before hand, (I.e. DataTable table = new DataTable();) then there would be an empty table floating around.
How should this be written?
You know, but not a compiler, so initialize it with null:
DataTable table = null;
you are getting lines from a file. which can be any file. (
if it is going in production and user changes that file - even you as programmer will not be sure that first line will starts with !)
Initially you have kept table unassigned and
here on this line,
while ((line = reader.ReadLine()) != null)
{
if (line.StartsWith("!"))
{
table = CreateDataTable(reader, line);
}
else
{
AddToTable(table); // Error: "Unassigned local variable"
}
}
you are either creating a table or calling AddToTable method passing table into that.
you know that file is having such data that first line of file will always starts with "!" , but compiler cannot be sure with that fact at compile time.
So as there are two cases in while loop : if and else. there are equal chances that flow will either go in if or in else.
So compiler will always get worried that at first iteration if flow goes in else part, by that time table will not be assigned to any value (not even null). So it generated compile time error.
to avoid such error as Backs suggested, initailize table will null (which will be the best solution)
DataTable table = null;
and when you are doing so, for the sake of being in safe side, you should check for table is not null in AddToTable method at first line.
void AddToTable(DataTable table)
{
if(table != null)
{
//your logic
}
}

Import txt data into existing SQL Server table in .net using LINQ

The txt file is of a specific form, it uses ';' as delimiter and has a specific number of columns. I also have a table that I created code-first with Entity Framework, which has the same number of columns.
So far I was able to import that kind of txt files to tables using "raw" SQL queries like BULK INSERT. But I am trying to learn how to do this from a web app using C# (or LINQ if needed).
I came across this solution from another question, but it seems that it creates a table named tbl, what I would like to do instead is to insert the data into an existing one.
public DataTable ConvertToDataTable (string filePath, int numberOfColumns)
{
DataTable tbl = new DataTable();
for(int col =0; col < numberOfColumns; col++)
tbl.Columns.Add(new DataColumn("Column" + (col+1).ToString()));
string[] lines = System.IO.File.ReadAllLines(filePath);
foreach(string line in lines)
{
var cols = line.Split(':');
DataRow dr = tbl.NewRow();
for(int cIndex=0; cIndex < 3; cIndex++)
{
dr[cIndex] = cols[cIndex];
}
tbl.Rows.Add(dr);
}
return tbl;
}
First of all, my advise would be not to read the CSV file yourself. Use a NUGET CSV file serializer like CSVHelper
With CSVHelper you directly convert the lines into your destination type:
using (TextReader txtReader = new StreamReader(sourceFileName)
{
csvReader = new CsvReader(txtReader)
IEnumerable<MyClass> result = csvReader.GetRecords<MyClass>()
// TODO: put result into database
}
One of the constructors of CsvReader takes a configuration object in which you can define your delimiter (":"); header rows; Comment lines; what to do with empty lines etc.
If you decide not to use CsvHelper you will need to convert your lines into MyClass objects:
IEnumerable<MyClass> ConvertTxtFile(string fileName)
{
// TODO: checks to see if fileName is proper file
IEnumerable<string> lines = System.IO.File.ReadAllLines(fileName);
foreach(string line in lines)
{
yield return StringToMyClass(line);
}
}
MyClass StringToMyClass(string line)
{
// TODO: code to convert your line into a MyClass.
}
As you don't ask how to convert a line into a MyClass, I leave this to you.
After a while, you have a sequence of MyClass objects. Your question is how to add them to your database using Entity Framework and Linq
Well, that will be the easy part (once you've learned how to use entity framework).
Supposing your DbContext has a DbSet<MyClass>, representing a table of MyClass objects
IEnumerable<MyClass> readItems = ConvertTxtFile(fileName);
using (var dbContext = new MyDbContext())
{
dbContext.MyClasses.AddRange(readItems.ToList());
dbContext.SaveChanges();
}

Insert Contents of CSV into strings

I've been struggling with a small piece of code for a little while now. I have a CSV file with one column that contains a string of numbers. I can import that file without issues and display it.
My goal is to take the numbers in each of the tables and put it into a separate string, run that string through a function and then put the results back into my datagrid in column two. Is there a way that I should be doing this using the code below; the foreach statement is where I believe this should be done.
Edit: I tweaked the code and it now works the way that I want it to but I can't insert my result into any columns except for the first one. Is there a way that I should be targeting the results so they go in the second column?
using (var fs = File.OpenRead(Dialog.FileName))
using (var reader = new StreamReader(fs))
{
List<string> lista = new List<string>();
List<string> listb = new List<string>();
while (!reader.EndOfStream)
{
var line = reader.ReadLine();
var values = line.Split(',');
lista.Add(values[0]);
dt1.Rows.Add(values[0]);
}
foreach (var item in lista)
{
string temp;
GetLuhnCheckDigit(item);
listb.Add(last.ToString());
temp = item + last.ToString();
dt1.Rows.Add(temp); //This only adds to the first column
}
dataGridView1.DataSource = dt1;
Without knowing what GetLuhnCheckDigit method does, it is not possible to determine what values you want the second column to contain. Looking at the posted code, there are many things missing like how many columns the data table has, where is the Dialog variable definition? What is last?
Assuming there are at least two columns in the DataTable dt1, I am not sure why you are adding the items to the first column then loop through a list of those items to set the second column. It appears adding both of the columns at the same time would be easier.
You could do all this while reading the file like below:
try {
using (var fs = File.OpenRead(Dialog.FileName)) {
using (var reader = new StreamReader(fs)) {
List<string> lista = new List<string>();
List<string> listb = new List<string>();
string temp;
while (!reader.EndOfStream) {
var line = reader.ReadLine();
var values = line.Split(',');
lista.Add(values[0]);
GetLuhnCheckDigit(values[0]); // <-- What is this method doing???
listb.Add(last.ToString());
temp = values[0] + last.ToString();
dt1.Rows.Add(values[0], temp); // <-- this adds both columns
}
dataGridView1.DataSource = dt1;
}
}
}
catch (Exception e) {
MessageBox.Show("Error: " + e.Message);
}
Let me know if I am missing something, as I am clueless as to what the GetLuhnCheckDigit method could be doing.

Saving unknown number of rows/columns into a string list

I'm fairly new to C#, so please bear with me. I have a class FixData:
private class FixData
{
public int ID { get; set; }
public List<string> content { get; set; }
}
And there's also private List<FixData> IDList = new List<FixData>();
I'm querying data from sql database using IDs already stored in IDList andSqlDataReader and then trying to save it into IDList.content. But that's where the hard part starts. I don't really know how many rows or columns this data has and trying to read that from debugger made me so much more confused (in other words: I fail to read it). Despite this, I tried to save it in so many ways and so many times that I'm completly lost at this point. Here's the code:
foreach (var record in IDList)
{
SqlCommand nonQuerycmd = new SqlCommand(NonQuery, connection);
nonQuerycmd.Parameters.Add(new SqlParameter("ScenarioID", record.ID));
nonQuerycmd.ExecuteNonQuery();
SqlCommand cmd = new SqlCommand(FixQuery, connection);
sqlreader = cmd.ExecuteReader();
ArrayList rowList = new ArrayList();
while (sqlreader.Read())
{
object[] values = new object[sqlreader.FieldCount];
sqlreader.GetValues(values);
rowList.Add(values);
record.content = values.Cast<object>().Select(x => x.ToString()).ToList();
}
sqlreader.Close();
}
Could you please help me and point me to an explanation or link or something that could help me understand how I should solve this?
Edit
I've managed to scramble something, but I'm not sure if this works as it was intended to.
Take a look at the MSDN documentation for SqlDataReader class. It should get you started.
The examples and other classes linked to there should help with proper usage of SqlCommand and other classes as well.
Without knowing what's in your table, the best I can do is:
if (read.Read())
{
for(int i = 0; i < read.FieldCount; i++)
{
record.content.Add(Convert.ToString(read[i]));
}
}
This will add every field selected to record.content as a string. I just changed "while" to "if" because you will only be handling one row (I think). If you need more help, let us know more information about your data and what you need. If, for some reason, you are putting multiple fields from multiple rows, use while. If you only want one field from multiple rows, change if to while, take out the for/next and change [i] to [0]. Shannon is not Carnac the Magnificent.

Read DataTable by RowState

I am reading my DataTable as follow:
foreach ( DataRow o_DataRow in vco_DataTable.Rows )
{
//Insert More Here
}
It crash; because I insert more records.
How can I read my DataTable without reading the new records? Can I read by RowState?
Thanks
Since I don't know what language you are using I can only give general advice.
In most (all?) languages it's not possible to do a foreach over a collection if you are modifying the collection. There are two common ways to deal with this.
Wild ass guessing pseudo code follows:
// first way uses array notation (if possible)
var no_of_rows = vco_DataTable.Rows.count();
for(var i = 0; i < no_of_rows; i++) {
DataRow o_DataRow = vco_DataTable.Rows[i];
//Insert More Here
}
// The second way copies the data
var my_copy = vco_DataTable.Copy()
foreach ( DataRow o_DataRow in my_copy.Rows )
{
//Insert More into vco_DataTable Here
}
copy.Dispose() // delete/destroy the copy

Categories