Hello everyone I'm trying to write code that can get select table information with (string)tableName, but i get error when i try to put value to Dictionary.
P.S : I have generated EF DB model.
public Dictionary<string, List<object>> GetTableInformation(string tableName, FinkonaDatabaseType type)
{
Dictionary<string, List<object>> _returnableDictionary = new Dictionary<string, List<object>>();
PropertyInfo prop = optimumEntities.GetType().GetProperty(tableName);
Type tableType = prop.PropertyType.GenericTypeArguments[0];
var items = optimumEntities.Database.SqlQuery(tableType, "SELECT * FROM " + tableName);
foreach (var item in items)
{
foreach (PropertyInfo info in item.GetType().GetProperties())
{
if (!_returnableDictionary.ContainsKey(info.Name))
{
_returnableDictionary.Add(info.Name, new List<object>());
}
_returnableDictionary[info.Name].Add(info.GetValue(info, null));
// System.Reflection.TargetException, Object does not match target type.
}
}
return _returnableDictionary;
}
Using ADO.NET DataTables will be easier here, as EF is used for strongly typing data. Since you are not too worried about the data types coming back, a DataTable will be easier to navigate.
Here is an examples of this:
public Dictionary<string, List<object>> GetTableInformation(string tableName, FinkonaDatabaseType type)
{
var sqlText = "SELECT * from " + tableName;
DataTable dt = new DataTable();
// Use DataTables to extract the whole table in one hit
using(SqlDataAdapter da = new SqlDataAdapter(sqlText, optimumEntities.Database.ConnectionString)
{
da.Fill(dt);
}
var tableData = new Dictionary<string, List<object>>();
// Go through all columns, retrieving their names and populating the rows
foreach(DataColumn dc in dt.Columns)
{
string columnName = dc.Name;
rowData = new List<object>();
tableData.Add(columnName, rowData);
foreach(DataRow dr in dt.Rows)
{
rowData.Add(dr[columnName]);
}
}
return tableData;
}
Related
I have a datatable as shown below
Dosage Drug Patient
-----------------------------------
25 Indocin David
50 Enebrel,Crozine Sam
10 Hydralazine Christoff
21 Combivent Janet
100 Dilantin Melanie
which should be converted in to as shown below based on comma split
Dosage Drug Patient
------------------------------
25 Indocin David
50 Enebrel Sam
50 Crozine Sam
10 Hydralazine Christoff
21 Combivent Janet
100 Dilantin Melanie
I did following code which is not giving the expected result. Can somebody suggest an answer?
private static void ProcessDatatable(DataTable dt)
{
DataTable dtnew = new DataTable();
IEnumerable<string[]> allRowValues = dt.AsEnumerable()
.Select(r => r.Field<string>(1).Split(','));
dtnew = allRowValues.ToDataTable();
}
Extension method:
public static DataTable ToDataTable<T>(this IEnumerable<T> collection, string tableName)
{
DataTable tbl = ToDataTable(collection);
tbl.TableName = tableName;
return tbl;
}
public static DataTable ToDataTable<T>(this IEnumerable<T> collection)
{
DataTable dt = new DataTable();
Type t = typeof(T);
PropertyInfo[] pia = t.GetProperties();
// Create the columns in the DataTable
foreach (PropertyInfo pi in pia)
{
dt.Columns.Add(pi.Name, pi.PropertyType);
}
// Populate the table
foreach (T item in collection)
{
DataRow dr = dt.NewRow();
dr.BeginEdit();
foreach (PropertyInfo pi in pia)
{
dr[pi.Name] = pi.GetValue(item, null);
}
dr.EndEdit();
dt.Rows.Add(dr);
}
return dt;
}
What you think you want is this:
IEnumerable<object[]> allRowValues = dataTable.AsEnumerable()
.SelectMany(dataRow =>
dataRow.Field<string>(1).Split(',').Select(drug => new[] { dataRow[0], drug, dataRow[2] }));
But what you really want is this:
IEnumerable<Record> allRowValues = dataTable.AsEnumerable()
.Select(dataRow => new Record(dataRow))
.SelectMany(record => record.SplitDrugs());
// ...
public class Record
{
public int Dosage { get; }
public string Drug { get; }
public string Patient { get; }
public Record(int dosage, string drug, string patient)
{
Dosage = dosage;
Drug = drug;
Patient = patient;
}
public Record(DataRow dataRow)
: this((int)dataRow["Dosage"], (string)dataRow["Drug"], (string)dataRow["Patient"])
{
}
public IEnumerable<Record> SplitDrugs()
{
return Drug.Split(',').Select(drug => new Record(Dosage, drug, Patient));
}
}
Short explanation: with a fancy LINQ you are trying to solve just too many problems of the world liek extracting information from data table, processing rows one by one, applying bussiness logic and merging the result to new data table. That is a good way how to write error-prone, unreadable, untestable, unstable and unmaintanable code.
Incomplete list of people who will eventually thank you for chosing the second option:
your future self
your teammates
your code reviewer
unit test writer
end users
your teacher (if assignment)
SO community
While I'm at it I'll save you some time debugging your transformation of allRowValues (which in your case is of type IEnumerable<string[]>) back to DataTable. If you think it will contain 3 columns, then you're wrong. Instead, it will contain columns like Length, LongLength, Rank, ... Have a look at properties Array class to figure out why.
Edit
OP has refined the original intent in a comment under another answer.
... , but i just posted a prototype of datatable,infact actually 180 columns are there.DO i need to add all 180 columns manually in newRow.ItemArray, when there is a split of comma seperated values???Any easier way?
Yes, there is an easier way. Involving generics you can expand the usage beyond this limited use case:
// extension method
public static DataTable ExpandColumn<T>(this DataTable dataTable, string columnName,
Func<T, IEnumerable<T>> expandField)
{
var clonedDataTable = dataTable.Clone();
var columnIndex = dataTable.Columns.IndexOf(columnName);
var column = dataTable.Columns[columnIndex];
foreach (var dataRow in dataTable.AsEnumerable())
{
var valueToExpand = dataRow.Field<T>(column);
var expandedValues = expandField(valueToExpand);
var originalValues = dataRow.ItemArray;
foreach (var value in expandedValues)
{
originalValues[columnIndex] = value;
clonedDataTable.Rows.Add(originalValues);
}
}
return clonedDataTable;
}
// usage
var dataTableNew = dataTable.ExpandColumn<string>("Drug", drug => drug.Split(','));
The above extension method clones DataTable instance by copying original rows and expands values in specified column by applying expandField function for each value.
I'd still like you to learn lesson from what I wrove above the edit and think twice about your design.
I am only barely comfortable with C# so I had to do this the old fashioned way but it works.
public partial class Form1 : Form
{
private DataTable dt;
private DataTable dtExpanded;
public Form1()
{
InitializeComponent();
LoadTable();
LoadExpandedTable();
}
//Dosage Drug Patient
private void LoadTable()
{
dt = new DataTable();
using (SqlConnection cn = new SqlConnection("Your connection string"))
{
using (SqlCommand cmd = new SqlCommand("Select * From DrugDoses", cn))
{
cn.Open();
dt.Load(cmd.ExecuteReader());
}
}
dataGridView1.DataSource = dt;
}
private void LoadExpandedTable()
{
dtExpanded = new DataTable();
dtExpanded.Columns.Add("Dose");
dtExpanded.Columns.Add("Drug");
dtExpanded.Columns.Add("Patient");
foreach (DataRow r in dt.Rows)
{
string s = (string)r["Drug"];
if(s.Contains(","))
{
string[] splitName = s.Split(',');
foreach (string drug in splitName)
{
DataRow newRow = dtExpanded.NewRow();
newRow.ItemArray = new Object[] { r["Dosage"], drug , r["Patient"]};
dtExpanded.Rows.Add(newRow);
}
}
else
{
dtExpanded.Rows.Add(r.ItemArray);
}
}
dataGridView2.DataSource = dtExpanded;
}
}
I have an object class which I cobvert into a List. I would like to convert the class into a list, so I can do a SQLBulkCopy, as my current method takes long to write data intop SQL database table.
class ExtractedInfo
{
public string Date { get; set; }
public string Client { get; set; }
public string Path { get; set; }
}
List<ExtractedInfo> extractedList = new List<ExtractedInfo>();
try
{
Console.WriteLine("Writing to DB");
using (SqlConnection conn = new SqlConnection(connectionStringPMT))
{
conn.Open();
SqlCommand cmd =
new SqlCommand(
"IF NOT EXISTS (SELECT 1 FROM [FileTrckT] WHERE Path = #Path) " +
"INSERT INTO [FileTrckT] (Date, Client, Path, DateAddedToDb, FileName) " + // dont forget to add "DateAddedToDb" on live code
"VALUES (#Date, #Client, #Path, #DateAddedToDb, #FileName)");// dont forget to add " #DateAddedToDb" on live code
cmd.CommandType = CommandType.Text;
cmd.Connection = conn;
cmd.Parameters.Add("#Date", DbType.DateTime);
cmd.Parameters.Add("#Client", DbType.String);
cmd.Parameters.Add("#Path", DbType.String);
cmd.Parameters.Add(#"DateAddedToDb",DbType.DateTime); //uncomment on live code
cmd.Parameters.Add("#FileName", DbType.String);
foreach (var extractedRecord in extractedList)
{
cmd.Parameters[0].Value = extractedRecord.Date;
cmd.Parameters[1].Value = extractedRecord.Client;
cmd.Parameters[2].Value = extractedRecord.Path;
cmd.Parameters[3].Value = DateTime.Now; //uncomment on live code
cmd.Parameters[4].Value = extractedRecord.Path.Substring(extractedRecord.Path.LastIndexOf("/")+1);
cmd.ExecuteNonQuery();
}
conn.Close();
}
} catch(Exception ex)
{
Console.WriteLine(ex.Message.ToString());
Console.WriteLine("Error occured whilst inserting data into sql table FiletrckT");
log.Info("Error occured whilst inserting data into sql table FiletrckT");
log.Error(DateTime.Now + ": " + ex.Message.ToString());
}
}
catch(Exception ex)
{
log.Error(DateTime.Now.ToString() + " " + ex.Message.ToString());
Console.WriteLine(ex.Message);
}
}
A better option is to use FastMember's ObjectReader to create an IDataReader on top of the collection or IEnumerable. This way you avoid doubling memory usage by copying everything into a DataTable.
From the repo's samples:
using(var bcp = new SqlBulkCopy(connection))
using(var reader = ObjectReader.Create(myList, "Id", "Name", "Description"))
{
bcp.DestinationTableName = "SomeTable";
bcp.WriteToServer(reader);
}
You don't need to pass a field list if you want to export all fields.
The library is available through NuGet
If you want to create a DataTable for other reasons, you can use MoreLINQ's ToDataTable. It works the same as ToList or ToArray but generates a DataTable. The full MoreLINQ library is available through NuGet. Individual extensions are available as code packages, eg ToDataTable's source is available here
UPDATE
The ExtractedInfo class doesn't seem to match the fields of the FileTrckT table. This can be fixed by adding a Select call that converts the ExtractedInfo objects to the form expected by the table, eg:
var itemsToAdd= from it in extractedList
select new { Date= DateTime.Parse(it.Date), //Or ParseExact
it.Client,
it.Path,
DateAddedToDb = DateTime.Now,
FileName = it.Path.Substring(it.Path.LastIndexOf("/")+1)
};
var fields=new []{"Date", "Client", "Path","DateAddedToDb","FileName"};
using(var bcp = new SqlBulkCopy(connection))
using(var reader = ObjectReader.Create(itemsToAdd, fields))
{
bcp.DestinationTableName = "FileTrckT";
bcp.WriteToServer(reader);
}
You can write your own generic conversion method, like this for example:
private DataTable ConvertToDatatable<T>(IList<T> data)
{
var props = TypeDescriptor.GetProperties(typeof(T));
DataTable table = new DataTable();
foreach (PropertyDescriptor prop in props)
table.Columns.Add(prop.Name, Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType);
foreach (T item in data)
{
DataRow row = table.NewRow();
foreach (PropertyDescriptor prop in props)
row[prop.Name] = prop.GetValue(item) ?? DBNull.Value;
table.Rows.Add(row);
}
return table;
}
or an extension method:
static class MyExtenstions
{
public static DataTable ToDataTable<T>(this IList<T> data)
{
var props = TypeDescriptor.GetProperties(typeof(T));
DataTable table = new DataTable();
foreach (PropertyDescriptor prop in props)
table.Columns.Add(prop.Name, Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType);
foreach (T item in data)
{
DataRow row = table.NewRow();
foreach (PropertyDescriptor prop in props)
row[prop.Name] = prop.GetValue(item) ?? DBNull.Value;
table.Rows.Add(row);
}
return table;
}
}
I need to do this in my codes;
Get data from 2 columns in a database (laborer and trx_date)
Place the extracted data to a DataTable
Explode the first column in the DataTable
Place exploded_laborer and trx_date to an array with
key->exploded_laborer
exploded=>trx_date
I am able to get to number 3 I just need to do number 4. My code is below:
private void GetLocalData()
{
const string sql = #"SELECT laborer, trx_date from tbl_jobs WHERE trx_date BETWEEN #fromDate AND #toDate";
var laborerDataTable = new DataTable();
using (var conn = new SqliteAccess().ConnectToSqlite())
{
using (var cmd = new SQLiteCommand(sql, conn))
{
conn.Open();
cmd.Parameters.AddWithValue("#fromDate", dtpFrom.Value.ToString("yyyy-MM-dd"));
cmd.Parameters.AddWithValue("#toDate", dtpTo.Value.ToString("yyyy-MM-dd"));
laborerDataTable.Load(cmd.ExecuteReader());
}
}
var exploded = new List<string>();
foreach (DataRow row in laborerDataTable.Rows)
{
exploded.Add(row["laborer"].ToString().Split('|')[0]);
}
}
Your help is very much appreciated.
I think you are supposed to create Dictionary<TKey,TValue> which represents a collection of keys and values. This might do the trick for you
laborerDataTable.AsEnumerable()
.Select(row => laborerDataTable.Columns.Cast<DataColumn>()
.ToDictionary(column => row[laborer] as string
column => row[trx_date] as string))
Thus the complete code might look like
private void GetLocalData()
{
const string sql = #"SELECT laborer, trx_date from tbl_jobs WHERE trx_date BETWEEN #fromDate AND #toDate";
var laborerDataTable = new DataTable();
using (var conn = new SqliteAccess().ConnectToSqlite())
{
using (var cmd = new SQLiteCommand(sql, conn))
{
conn.Open();
cmd.Parameters.AddWithValue("#fromDate", dtpFrom.Value.ToString("yyyy-MM-dd"));
cmd.Parameters.AddWithValue("#toDate", dtpTo.Value.ToString("yyyy-MM-dd"));
laborerDataTable.Load(cmd.ExecuteReader());
}
}
var LabDict = laborerDataTable.AsEnumerable()
.Select(row => laborerDataTable.Columns.Cast<DataColumn>()
.ToDictionary(column => row[laborer] as string
column => row[trx_date] as string))
}
Edit
This is just to create a dummy table.
static DataTable GetTable()
{
// Here we create a DataTable with four columns.
DataTable table = new DataTable();
table.Columns.Add("laborer", typeof(string));
table.Columns.Add("trx_date", typeof(string));
// Here we add five DataRows.
table.Rows.Add("Indocin", "12/12/2010");
table.Rows.Add("Enebrel", "12/1/2011");
table.Rows.Add("Hydralazine", "1/12/2012");
table.Rows.Add("Combivent", "11/12/2013");
table.Rows.Add("Dilantin", "12/11/2014");
return table;
}
normal core C# way
DataTable laborerDataTable = GetTable();
Dictionary<string, string> exploded = new Dictionary<string, string>();
foreach(DataRow row in laborerDataTable.Rows)
{
exploded.Add(row.Field<string>(0), row.Field<string>(1));
}
Also, how do I print each of the row to a console?
foreach(var dct in exploded)
{
Console.WriteLine(dct.Key + " Date is " + dct.Value);
}
If you are expecting the result to a Key-Value pair then why an array? why not a Dictionary<string, string>? You can try something like this :
Dictionary<string, string> laborerDict = laborerDataTable.AsEnumerable()
.ToDictionary(x => x.Field<string>("laborer"),
x => x.Field<string>("trx_date"));
Use in built HashMap in java refer the code below
HashMap map = new HashMap<>();
Map.put(key,value);
And to get data from map use
Map.get(key)
It has a lot of function, maybe you can refer them by searching Java API
when you want to have Key=>Value better you use Dictionary instead of List
var exploded = new Dictionary<string,string>();
foreach (DataRow row in laborerDataTable.Rows)
{
exploded.Add(row["laborer"].ToString(),(row["trx_date"].ToString());
}
can someone tells me how to do this?
I have this function in c#:
public string ConvertLocationTableToString()
{
int radius = 0;
string locationType = "marker";
DataTable dt = new DataTable();
Using (SqlConnection con = new SqlConnection(ConfigurationManager.ConnectionStrings["DefaultConnection"].ToString()))
{
Using (SqlCommand cmd = new SqlCommand("SELECT lat=Latitude, lng=Longitude, FROM Locations", con))
{
con.Open();
SqlDataAdapter da = new SqlDataAdapter(cmd);
da.Fill(dt);
System.Web.Script.Serialization.JavaScriptSerializer serializer = new System.Web.Script.Serialization.JavaScriptSerializer();
List<Dictionary<string, object>> rows = new List<Dictionary<string, object>>();
Dictionary<string, object> row;
foreach (DataRow dr in dt.Rows)
{
row = new Dictionary<string, object>();
foreach (DataColumn col in dt.Columns)
{
row.Add(col.ColumnName, dr[col]);
}
rows.Add(row);
}
return serializer.Serialize(rows);
}
}
}
The table Locations contains two rows with latitude and lontitude values.
It produces string value of:
[{"lat":24.816925048828125,"lng":-107.37641906738281}
, {"lat":24.815664291381836,"lng":-107.38169097900391}]
.
But I want to produce the following:
[{"Coordinates": [{"lat":24.816925048828125,"lng":-107.37641906738281}], "Radius": 0,"LocationType": "marker"}
,{ "Coordinates": [{lat":24.815664291381836,"lng":-107.38169097900391}],"Radius": 0,"LocationType": "marker"}}]
Please notice that ‘Radius’ and ‘LocationType’ are not fields in the table.
Thank you for your help.
rubenc
You are serializing rows, which is returned from the table. If you created an object in C# that matches your desired output, then looped over the rows returned and set the relevant items, you could then serialize that.
In your case, the object would consist of:
- Coordinates - some type of list
- Radius - int I assume
- LocationType - guessing enum
Then you would create an arrray of these, and serialize the array.
I have a method in my app that populates DataTable with the data using the following code:
DataTable dt = this.attachmentsDataSet.Tables["Attachments"];
foreach (Outlook.Attachment attachment in this.mailItem.Attachments)
{
DataRow dr = dt.NewRow();
dr["Index"] = attachment.Index;
dr["DisplayName"] = String.Format(
CultureInfo.InvariantCulture,
"{0} ({1})",
attachment.FileName,
FormatSize(attachment.Size));
dr["Name"] = attachment.FileName;
dr["Size"] = attachment.Size;
dt.Rows.Add(dr);
}
I was wondering if I could achieve the same functionality using LINQ in order to shorten this code a bit. Any ideas?
Yes, Easy
public void FillFromList(List<T> col)
{
Type elementType = typeof(T);
// Nested query of generic element list of property
// values (using a join to the DataTable columns)
var rows = from row in col
select new
{
Fields = from column in m_dataTable.Columns.Cast<DataColumn>()
join prop in elementType.GetProperties()
on column.Caption equals prop.Name
select prop.GetValue(row, null)
};
// Add each row to the DataTable
int recordCount = 0;
foreach ( var entry in rows )
{
m_dataTable.Rows.Add(entry.Fields.ToArray());
}
This assumes that the properties on T are the same as the DataTable columns.
Well this code isnt shorter or Linq but I did an externsion method that takes a IList and turns it into a DataTable for you.
public static DataTable ToDataTable<T>(this IList<T> theList)
{
DataTable theTable = CreateTable<T>();
Type theEntityType = typeof(T);
// Use reflection to get the properties of the generic type (T)
PropertyDescriptorCollection theProperties = TypeDescriptor.GetProperties(theEntityType);
// Loop through each generic item in the list
foreach (T theItem in theList)
{
DataRow theRow = theTable.NewRow();
// Loop through all the properties
foreach (PropertyDescriptor theProperty in theProperties)
{
// Retrieve the value and check to see if it is null
object thePropertyValue = theProperty.GetValue(theItem);
if (null == thePropertyValue)
{
// The value is null, so we need special treatment, because a DataTable does not like null, but is okay with DBNull.Value
theRow[theProperty.Name] = DBNull.Value;
}
else
{
// No problem, just slap the value in
theRow[theProperty.Name] = theProperty.GetValue(theItem);
}
}
theTable.Rows.Add(theRow);
}
return theTable;
}
You first identify whether you can query this.mailItem.Attachments and
if it is possible, you can convert query result to a datatable from extension method create by Steve Sloka...