Set "NULL" in Empty cells in CsvReader - c#

Here is my code:
using (System.Net.WebResponse tmpRes = tmpReq.GetResponse())
{
using (System.IO.Stream tmpStream = tmpRes.GetResponseStream())
{
using (System.IO.TextReader tmpReader = new System.IO.StreamReader(tmpStream))
{
string fileContents = tmpReader.ReadToEnd();
for (int i = 0; i < fileContents.Length; i++)
{
if (fileContents[i] == "")
{
fileContents[i] = "null";
}
}
using (Stream s = GenerateStreamFromString(fileContents))
{}
}
}
}
this shows error 'string' to 'char' convert implicitly. Is there any other way to set "NULL" in empty fields in CSVReader

You are not using the CsvReader at all. You are also not splitting the string by your delimiter to get "cells". However, you can load a DataTable from the CsvReader and modify that.
Here's an example presuming tab as delimiter:
var tblCSV = new DataTable();
using (System.Net.WebResponse tmpRes = tmpReq.GetResponse())
using (System.IO.Stream tmpStream = tmpRes.GetResponseStream())
using (System.IO.TextReader tmpReader = new System.IO.StreamReader(tmpStream))
using (var csv = new CsvReader(tmpReader, true, '\t', '"', '\0', '\0', ValueTrimmingOptions.All))
{
csv.MissingFieldAction = MissingFieldAction.ParseError;
csv.DefaultParseErrorAction = ParseErrorAction.RaiseEvent;
csv.ParseError += csv_ParseError;
csv.SkipEmptyLines = true;
// load into DataTable
tblCSV.Load(csv, LoadOption.OverwriteChanges, csvTable_FillError);
}
Now loop the rows and columns and modify them accordingly:
foreach(DataRow row in tblCSV.Rows)
{
foreach(DataColumn col in tblCSV.Columns)
{
if(string.IsNullOrWhiteSpace(row.Field<string>(col)))
row.SetField(col, "null");
}
}
Update related to your comment:
I wants to add NULL value in empty cells in sql database when the csv
data Save in database. Is there any other way?
You could simply use the loop above to update your table instead:
using (var con = new SqlConnection("ConnectionString"))
{
con.Open();
foreach (DataRow row in tblCSV.Rows)
{
using (var cmd = new SqlCommand("INSERT INTO table(Col1,Col2) VALUES (#Col1,Col2);", con))
{
string col1 = row.Field<string>("Col1");
if (string.IsNullOrWhiteSpace(col1))
col1 = null;
string col2 = row.Field<string>("Col2");
if (string.IsNullOrWhiteSpace(col2))
col2 = null;
cmd.Parameters.AddWithValue("#col1", col1);
cmd.Parameters.AddWithValue("#col2", col2);
int inserted = cmd.ExecuteNonQuery();
}
}
}

Related

Selenium C# reading data from Excel for data driven testing Based On SheetName

I am trying to perform Data driven testing by loading client codes from Excel file to login and perform other operation.I am trying to iterate my test for all clients in the User_Name column. I only need to read Data From SheetName, right now i have Contstant SheetName (i,e sheet1), wanted to add SheetName Parameter
Any help with this would be much appreciated thank you.
I am using ExcelDataReader v3.4.0, ExcelDataReader.DataSet v3.4.0, selenium Webdriver v3.11.0
My Excel Generic Code is Below:
```
using ExcelDataReader;
using System;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Linq;
using System.Runtime.Remoting.Messaging;
using System.Text;
using System.Threading.Tasks;
namespace MyDemoAutomation
{
public class ExcelUtil
{
public DataTable ExcelToDatable(string fileName)
{
// open file and returns as stream
FileStream stream = File.Open(fileName, FileMode.Open, FileAccess.Read);
// create openXmlReader via ExcelReaderFactory
IExcelDataReader excelReader = ExcelReaderFactory.CreateOpenXmlReader(stream);
//Set the first row as column name
var result1 = excelReader.AsDataSet(new ExcelDataSetConfiguration()
{
ConfigureDataTable = (_) => new ExcelDataTableConfiguration()
{
UseHeaderRow = true
}
});
// Return as dataset
DataSet result = excelReader.AsDataSet();
// Get all tables
DataTableCollection table = result.Tables;
// Store in Database
DataTable resultTable = table["Sheet1"];
Here instead Sheet 1 i Want to Pass SheetName as Parameter(In Existing Code How can i Add
// return
return resultTable;
}
List<DataCollection> dataCol = new List<DataCollection>();
public void PopulateInCollection(string fileName)
{
DataTable table = ExcelToDatable(fileName);
for (int row = 1; row <= table.Rows.Count; row++)
{
for (int col = 0; col < table.Columns.Count; col++)
{
DataCollection dtTable = new DataCollection()
{
rowNumber = row,
colName = table.Columns[col].ColumnName,
colValue = table.Rows[row - 1][col].ToString()
};
dataCol.Add(dtTable);
}
}
}
public string ReadData(int rowNumber, string columnName)
{
try
{
// Retriving data using LINQ to reduce much of iterations
string data = (from colData in dataCol
where colData.colName == columnName && colData.rowNumber == rowNumber
select colData.colValue).SingleOrDefault();
return data.ToString();
}
catch (Exception e)
{
return null;
}
}
internal class DataCollection
{
public int rowNumber { get; internal set; }
public string colName { get; internal set; }
public string colValue { get; internal set; }
}
}
}
and the TestClass:
[Test]
public void DataDrivenTest_FromExcel()
{
Driver = new ChromeDriver();
ExcelUtil util = new ExcelUtil();
util.PopulateInCollection(#"C:\dan\AutomationTest\TestData\test.xlsx");
Driver.FindElement(By.Id("contentPlaceholder_txtClientCode"))
.SendKeys(util.ReadData(i));
Driver.FindElement(By.XPath("//*[#id='btnLogin']")).Click();
Driver.FindElement(By.XPath("//*
[#id='tabContent0']/table/tbody/tr[2]/td[1]")).Click();
Driver.FindElement(By.Id("contentPlaceholder_txtcloseButton")).Click();
Driver.Quit
}
List<DataCollection> dataCol = new List<DataCollection>();
public void PopulateInCollection(string fileName, string sheetName)
{
DataTable table = ExcelToDataTable(fileName, sheetName);
//Iterate through the rows and columns of the Table
for (int row = 1; row <= table.Rows.Count; row++)
{
for (int col = 0; col <= table.Columns.Count; col++)
{
Datacollection dtTable = new Datacollection()
{
rowNumber = row,
colName = table.Columns[col].ColumnName,
colValue = table.Rows[row - 1][col].ToString()
};
//Add all the details for each row
dataCol.Add(dtTable);
}
}
}
public DataTable ExcelToDaTable(string fileName, string sheetName)
{
// open file and returns as stream
FileStream stream = File.Open(fileName, FileMode.Open, FileAccess.Read);
// create openXmlReader via ExcelReaderFactory
IExcelDataReader excelReader = ExcelReaderFactory.CreateOpenXmlReader(stream);
//Set the first row as column name
var result = excelReader.AsDataSet(new ExcelDataSetConfiguration()
{
ConfigureDataTable = (_) => new ExcelDataTableConfiguration()
{
UseHeaderRow = true
}
});
// Return as dataset
DataSet result = excelReader.AsDataSet();
// Get all tables
DataTableCollection table = result.Tables;
// Store in Database
DataTable resultTable = table[sheetName];
return resultTable;
}
#End
//Define this first
util.PopulateInCollection("fileName", "sheetName");
//Reading from excel file
util.ReadData(rowNumber, columnName)

C# Reading CSV file with SQL conditions

I am using CsvHelper lib to read CSV file and I can successfully read the file with the lib. However I cannot use SQL condition to filter values. How can I do that without using SQL Server. I am really stuck on it.
It was very easy with Pandas and Pandasql libs in Python but it is being too hard in C#..
My Code:
public static void Main(string[] args)
{
var fileInfo = new FileInfo(#"filePath");
using (TextReader reader = fileInfo.OpenText())
using (var csvReader = new CsvReader(reader))
{
csvReader.Configuration.Delimiter = ",";
csvReader.Configuration.HasHeaderRecord = false;
csvReader.Configuration.IgnoreQuotes = true;
csvReader.Configuration.TrimFields = true;
csvReader.Configuration.WillThrowOnMissingField = false;
while (csvReader.Read())
{
var myStrinVar = csvReader.GetField<string>(0);
Console.Write(myStrinVar); //SELECT * FROM table...
}
}
}
I would suggest using LINQ to filter your results.
https://msdn.microsoft.com/en-us/library/bb397906.aspx
Say you have some class MyClass that you can serialize the lines in your file into.
For example:
public class MyClass
{
public int ID { get; set; }
}
var records = csv.GetRecords<MyClass>().ToList();
var filtered = records.Where(r => r.ID >= 10);
That example is a bit contrived but you can use any boolean expression you like in the where clause.
I know this is too late for OP, but the issue with the accepted answer is that you have to read in the entire result set to memory which may not be tenable for large files. Also, if you can extend this code below to get the top N rows without having to read the entire CSV if you find matches early in the file.
public static void Main(string[] args)
{
var fileInfo = new FileInfo(#"filePath");
var where = ""; //Code to set up where clause part of query goes here
using (TextReader reader = fileInfo.OpenText())
using (var csvReader = new CsvReader(reader))
{
csvReader.Configuration.Delimiter = ",";
csvReader.Configuration.HasHeaderRecord = false;
csvReader.Configuration.IgnoreQuotes = true;
csvReader.Configuration.TrimFields = true;
csvReader.Configuration.WillThrowOnMissingField = false;
DataTable dt = null;
while (csvReader.Read())
{
//Use the first row to initialize the columns.
if (dt == null)
{
dt = new DataTable();
for (var i = 0; i < csvReader.FieldCount; i++)
{
var fieldType = csvReader.GetFieldType(i);
DataColumn dc;
if (fieldType.IsNullableType())
{
dc = new DataColumn(csvReader.GetName(i), Nullable.GetUnderlyingType(fieldType));
dc.AllowDBNull = true;
}
else
dc = new DataColumn(csvReader.GetName(i), data.GetFieldType(i));
dt.Columns.Add(dc);
}
}
//Map DataReader to DataRow
var newRow = dt.Rows.Add();
foreach(DataColumn col in dt.Columns)
{
newRow[col.ColumnName] = csvReader[col.ColumnName];
}
//Create a temporary DataView and filter it with the where clause.
DataView dv = new DataView(dt);
dv.RowFilter = where;
var data = dv.Count > 0 ? dv[0] : null;
if(data != null)
{
//Row in here matches your where clause.
//Code to read this row or do something with it.
}
//Empty the temporary data table.
dt.Rows.Clear();
}
}
}

How to convert Handsontable data back into C# class

I am using an ajax post to send my column headers and data from a handsontable back to an ashx handler.
$.ajax({
type: 'POST',
url: "Scripts/SaveExcelData.ashx",
contentType: "application/json; charset=utf-8",
data: JSON.stringify({"columns": hot.getColHeader(), "rows": hot.getData()}),
success: function (data) {}
});
Currently I am using to following to deserialize the request, but have not been able to successfully come up with anything that converts the rows into an array of DataBaseRow class objects.
var jsonString = String.Empty;
context.Request.InputStream.Position = 0;
using (var inputStream = new StreamReader(context.Request.InputStream))
{
jsonString = inputStream.ReadToEnd();
var results = JsonConvert.DeserializeObject<dynamic>(jsonString);
var columns = results.columns;
var rows = results.rows;
//use columns here to convert rows into DataBaseRow class
}
columns looks like: {["Col1","Col2","Col3"]}
rows looks like: {[["Val1","Val2","Val3"],["Val1","Val2","Val3"],["Val1","Val2","Val3"]]}
How can I do this?
UPDATE
Instead of trying to convert the dynamic class into the DataBaseRow class, I found I could actually just manually loop through the array values and write them into new instances of the DataBaseRow class.
using (DBEntities edmx = new DBEntities())
{
foreach (var row in rows)
{
DataBaseRow dbr = new DataBaseRow();
edmx.DataBaseRow.Add(dbr);
dbr.LoadedTime = DateTime.Now;
for (int i = 0; i < row.Count; i++)
{
string colval = row[i].ToString();
string colname = columns[i].ToString();
switch (colname)
{
case "Col1":
dbr.DBCol1 = colval;
break;
case "Col2":
dbr.DBCol2 = colval;
break;
case "Col3":
dbr.DBCol3 = colval;
break;
}
}
}
edmx.SaveChanges();
}
This works, but is very slow (see comment for timings). Is there a faster/better way to process this data? (if it matters - I actually have 14 columns that I'm mapping in the switch)
So the technical answer to my question can be found in the Update I added (just reference the dynamic objects as arrays, don't try to convert them).
However, seems like Entity Framework is very poor at handling saving large datasets. This can be sped up by grouping the saves into chunks and recreating the context for every chunck. https://stackoverflow.com/a/5942176/266592
I ended up rewriting this to insert the values into a DataTable and then using SqlBulkCopy to save the records to the database.
var jsonString = String.Empty;
context.Request.InputStream.Position = 0;
using (var inputStream = new StreamReader(context.Request.InputStream))
{
jsonString = inputStream.ReadToEnd();
var results = JsonConvert.DeserializeObject<dynamic>(jsonString);
var columns = results.columns;
var rows = results.rows;
var dt = new DataTable();
for (int i = 0; i < columns.Count; i++)
{
dt.Columns.Add(columns[i].ToString());
}
foreach (var row in rows)
{
var datarow = dt.NewRow();
for (int i = 0; i < row.Count; i++)
{
datarow[i] = row[i];
}
dt.Rows.Add(datarow);
}
using (var connection = new SqlConnection(ConnectionString))
{
SqlTransaction transaction = null;
connection.Open();
try
{
transaction = connection.BeginTransaction();
using (var sqlBulkCopy = new SqlBulkCopy(connection, SqlBulkCopyOptions.TableLock, transaction))
{
sqlBulkCopy.DestinationTableName = "TABLENAME";
sqlBulkCopy.BatchSize = 100000;
sqlBulkCopy.BulkCopyTimeout = 0;
foreach (DataColumn col in dt.Columns)
{
sqlBulkCopy.ColumnMappings.Add(col.ColumnName, col.ColumnName);
}
sqlBulkCopy.WriteToServer(dt);
}
transaction.Commit();
}
catch (Exception)
{
transaction.Rollback();
}
}
}

Convert datatable into XML

I convert XML into datatable and now want to convert data table into XML again.
I have database in which four XML's are there and have to change that text xml
public ActionResult getChangeContent(string arg, string content)
{
char[] a = { ':' };
string[] id = arg.Split(a);
decimal tid = Convert.ToDecimal(id[0]);
decimal jid = Convert.ToDecimal(id[1]);
string groupname = id[2];
var gp = (from Trans_Mast in r2ge.Transcription_Master where Trans_Mast.Transcription_Id == tid && Trans_Mast.Entity_Id == jid select Trans_Mast).Distinct();
foreach (var g in gp)
{
DataSet ds = new DataSet();
ds.ReadXml(new XmlTextReader(new StringReader(g.Text_xml)));
DataTable text = ds.Tables[0];
for (int i = 0; i < text.Rows.Count; i++)
{
if (text.Rows[i]["group"].Equals(groupname))
{
text.Rows[i]["Text_Text"] = content;
}
text.AcceptChanges();
}
ConvertDatatableToXML(text);
}
return View();
}
public string ConvertDatatableToXML(DataTable dtTemp)
{
MemoryStream str = new MemoryStream();
dtTemp.WriteXml(str, true);
str.Seek(0, SeekOrigin.Begin);
StreamReader sr = new StreamReader(str);
string xmlstr;
xmlstr = sr.ReadToEnd();
return (xmlstr);
}
Data set is changed but there is no affect on XML.. any suggestion please..
use this piece of code :
private static string ConvertDataTableToXML(DataTable dtBuildSQL)
{
DataSet dsBuildSQL = new DataSet();
StringBuilder sbSQL;
StringWriter swSQL;
string XMLformat;
sbSQL = new StringBuilder();
swSQL = new StringWriter(sbSQL);
dsBuildSQL.Merge(dtBuildSQL, true, MissingSchemaAction.AddWithKey);
dsBuildSQL.Tables[0].TableName = “Table”;
foreach (DataColumn col in dsBuildSQL.Tables[0].Columns)
{
col.ColumnMapping = MappingType.Attribute;
}
dsBuildSQL.WriteXml(swSQL, XmlWriteMode.WriteSchema);
XMLformat = sbSQL.ToString();
return XMLformat;
}

Convert IEnumerable string array to datatable

I have a csv file delimited with pipe(|). I am reading it using the following line of code:
IEnumerable<string[]> lineFields = File.ReadAllLines(FilePath).Select(line => line.Split('|'));
Now, I need to bind this to a GridView. So I am creating a dynamic DataTable as follows:
DataTable dt = new DataTable();
int i = 0;
foreach (string[] order in lineFields)
{
if (i == 0)
{
foreach (string column in order)
{
DataColumn _Column = new DataColumn();
_Column.ColumnName = column;
dt.Columns.Add(_Column);
i++;
//Response.Write(column);
//Response.Write("\t");
}
}
else
{
int j = 0;
DataRow row = dt.NewRow();
foreach (string value in order)
{
row[j] = value;
j++;
//Response.Write(column);
//Response.Write("\t");
}
dt.Rows.Add(row);
}
//Response.Write("\n");
}
This works fine. But I want to know if there is a better way to convert IEnumerable<string[]> to a DataTable. I need to read many CSVs like this, so I think the above code might have performance issues.
Starting from .Net 4:
use ReadLines.
DataTable FileToDataTable(string FilePath)
{
var dt = new DataTable();
IEnumerable<string[]> lineFields = File.ReadLines(FilePath).Select(line => line.Split('|'));
dt.Columns.AddRange(lineFields.First().Select(i => new DataColumn(i)).ToArray());
foreach (var order in lineFields.Skip(1))
dt.Rows.Add(order);
return dt;
}
(edit: instead this code, use the code of #Jodrell answer, This prevents double charging of the Enumerator).
Before .Net 4:
use streaming:
DataTable FileToDataTable1(string FilePath)
{
var dt = new DataTable();
using (var st = new StreamReader(FilePath))
{
// first line procces
if (st.Peek() >= 0)
{
var order = st.ReadLine().Split('|');
dt.Columns.AddRange(order.Select(i => new DataColumn(i)).ToArray());
}
while (st.Peek() >= 0)
dt.Rows.Add(st.ReadLine().Split('|'));
}
return dt;
}
since, in your linked example, the file has a header row.
const char Delimiter = '|';
var dt = new DataTable;
using (var m = File.ReadLines(filePath).GetEnumerator())
{
m.MoveNext();
foreach (var name in m.Current.Split(Delimiter))
{
dt.Columns.Add(name);
}
while (m.MoveNext())
{
dt.Rows.Add(m.Current.Split(Delimiter));
}
}
This reads the file in one pass.

Categories