I do not know what I am doing wrong, when I add the values from my database to a list<object> it always returns the list with the total of objects but all the values are those of the last record that was made in the loop while.
This is my code:
public List<object> getdata(string storedProcedure)
{
List<object> list = new List<object>();
try
{
using (var conn = new NpgsqlConnection(connstring))
{
conn.Open();
NpgsqlTransaction tran = conn.BeginTransaction();
NpgsqlDataReader reader;
var cmd = new NpgsqlCommand(storedProcedure, conn);
cmd.CommandType = CommandType.StoredProcedure;
reader = cmd.ExecuteReader();
int fieldCount = reader.FieldCount;
object[] fieldValues = new object[fieldCount];
while (reader.Read())
{
int instances = reader.GetValues(fieldValues);
for (int fieldCounter = 0; fieldCounter < fieldCount; fieldCounter++)
{
if (Convert.IsDBNull(fieldValues[fieldCounter]))
fieldValues[fieldCounter] = "NA";
}
list.Add(fieldValues);
}
reader.Close();
tran.Commit();
conn.Close();
return list;
}
}
catch (Exception ex)
{
}
return list;
}
This is what I get in all positions, it is the last value:
You need to move the declaration and initialization of your object array inside the loop
....
while (reader.Read())
{
object[] fieldValues = new object[fieldCount];
....
}
The problem that you experience is caused by the fact that when you initialize the array outside the loop and reuse it at every loop of the datareader, you replace the previous content with the content of the current record.
But when you add the array to the list of objects you add the same reference to initial array where only the content has been changed. Obviously when you reach the last record there is only one array while the list contains n reference to the same memory area. So you display n time the same last record.
Moving the initialization inside the loop provides the list with a new reference at each loop and each reference maintain the data of the record received during the loop.
Related
I'm trying to return multiple values from a function in which I pass a parameter and store it in variable ZDO_S
In variable ZDO_S I'm trying to store two values which I will get by passing single value 'row["TVM_LS_NO"].ToString()' which is obtain from database in 'getdo' function
var ZDO_S = getDo(row["TVM_LS_NO"].ToString());
Here is the method :
private static (string TVD_LS_NO, string TVD_INV_NO) getDo(string DoNo)
{
try
{
using (OracleConnection con = new OracleConnection(ConfigurationManager.ConnectionStrings["Oracle_To_Sql.Properties.Settings.Setting"].ToString()))
{
string query = "select SUBSTR(TVD_DO_ITEM_NO,'1','10') from T_VEHICLE_DTL1 where TVD_LS_NO=:TVD_LS_NO";
OracleCommand myCommand = new OracleCommand(query, con);
DataTable dt = new DataTable();
OracleDataAdapter da = new OracleDataAdapter(myCommand);
da.SelectCommand = new OracleCommand(query, con);
da.Fill(dt);
foreach (DataRow row in dt.Rows)
{
string TVD_LS_NO = row["TVD_LS_NO"].ToString();
string TVD_INV_NO = row["TVD_INV_NO"].ToString();
}
return (TVD_LS_NO, TVD_INV_NO);
}
}
catch (Exception ex)
{
throw;
}
}
Expected values should be return and stored in ZDO_S variable.
actual getting error "cannot assign void to an implicitly-typed variable" at
var ZDO_S=getDo(row["TVM_LS_NO"].ToString());
IDentifier expected at
private static (string TVD_LS_NO, string TVD_INV_NO) getDo(string DoNo)
Since 'Program.getDo(String) returns void ,a return keyword must be followed by an object expression at
return (TVD_LS_NO, TVD_INV_NO);
I'm not that familiar with C#, but if you want to return multiple values in a single return statement, the only way to go is usually an array or an object. An array would be simplest in your case.
The return type definition doesn't look like an array return type, try
private static string[] getDo() {...
The return statement itself doesn't look like an array, try returning the values as an array
You are defining variables inside the foreach-loop, which usually makes them unaccessible outside the loop. Try defining the variables outside the loop and use them in the loop.
I don't know if this is valid C#, but something like this:
private static String[] getDo(string DoNo)
{
try
{
using (OracleConnection con = new OracleConnection(ConfigurationManager.ConnectionStrings["Oracle_To_Sql.Properties.Settings.Setting"].ToString()))
{
String[] toReturn = new String[2];
string TVD_LS_NO, TVD_INV_NO;
string query = "select SUBSTR(TVD_DO_ITEM_NO,'1','10') from T_VEHICLE_DTL1 where TVD_LS_NO=:TVD_LS_NO";
OracleCommand myCommand = new OracleCommand(query, con);
DataTable dt = new DataTable();
OracleDataAdapter da = new OracleDataAdapter(myCommand);
da.SelectCommand = new OracleCommand(query, con);
da.Fill(dt);
foreach (DataRow row in dt.Rows)
{
TVD_LS_NO = row["TVD_LS_NO"].ToString();
TVD_INV_NO = row["TVD_INV_NO"].ToString();
}
toReturn[0] = TVD_LS_NO;
toReturn[1] = TVD_INV_NO;
return toReturn;
}
}
catch (Exception ex)
{
throw;
}
}
Not sure if this will be the best answer, but I hope it will push you in the right direction.
There are a few concerns here, let's try to take them one by one.
First of all, you say you want to return two values, but you are assigning them in a for loop, which could mean they will be assigned many times. Is this on purpose ? Or are you expecting the for loop to run only once ?
Then, you declare your variables inside your for loop, which means they will be destroyed when you exit the loop, loosing their values and making the compiler angry (and sad).
You are trying to use a return result syntax that is known by compilers since C# 7. If you are using a prior version, it will not work at all.
Lastly, your try/catch is completely useless if you don't do something in your catch block, so you can remove it.
If I may suggest some additional understanding on TUples.
So, if we scratch it up in C# 7, it looks like this :
private static (string TVD_LS_NO, string TVD_INV_NO) getDo(string DoNo)
{
//We declare our return value(s)
var toReturn(TVD_LS_NO: "", TVD_INV_NO: "");
using (OracleConnection con = new OracleConnection(ConfigurationManager.ConnectionStrings["Oracle_To_Sql.Properties.Settings.Setting"].ToString()))
{
string query = "select SUBSTR(TVD_DO_ITEM_NO,'1','10') from T_VEHICLE_DTL1 where TVD_LS_NO=:TVD_LS_NO";
OracleCommand myCommand = new OracleCommand(query, con);
DataTable dt = new DataTable();
OracleDataAdapter da = new OracleDataAdapter(myCommand);
da.SelectCommand = new OracleCommand(query, con);
da.Fill(dt);
foreach (DataRow row in dt.Rows)
{
//We assign our return values
toReturn.TVD_LS_NO = row["TVD_LS_NO"].ToString();
toReturn.TVD_INV_NO = row["TVD_INV_NO"].ToString();
}
return toReturn;
}
But that works only in C# 7
If you don't use C# 7, you have other possibilities :
Use ref keyword
int value = 0;
MyFunction(ref value);
Console.WriteLine(value); //Outputs 10
void MyFunction(ref int value)
{
value = 10;
}
Use out keyword
int value;
MyFunction(value);
Console.WriteLine(value); //Outputs 10
void MyFunction(out int value)
{
value = 10;
}
To get the difference between the two, additional reading.
You can use any number of ref/out parameters as you like.
Use a small class/structure (preferred way)
public struct MyResultValues //could be public class MyResultValues
{
public int result1;
public string result2;
}
MyResultValues result = MyFunction();
Console.WriteLine(result.result1);
Console.WriteLine(result.result2);
public MyResultValues MyFunction()
{
MyResultValues toReturn = new MyResultValues();
toReturn.result1 = 10;
toReturn.result2 = "Hello World !";
return toReturn;
}
I'm currently executing my stored procedure below, and it works perfectly. But I can't specify the command timeout.
var results = await _dbContext.DbContext.Database.SqlQuery<GetOutputDto>(#"[dbo].[GetOutput] " + parameterString, list.ToArray()).ToListAsync();
Now I've change this to the below, and wondering what's the best way to convert the result to an object. I have over 30 properties, so setting each value would be quite tedious. Was wondering if there's a clean solution as Entity Framework solution.
using (var conn = new SqlConnection(_dbContextProvider.DbContext.Database.Connection.ConnectionString))
{
conn.Open();
SqlCommand cmd = new SqlCommand(#"[dbo].[GetOutput]", conn);
cmd.CommandTimeout = 60;
cmd.CommandType = CommandType.StoredProcedure;
foreach (var item in list)
{
cmd.Parameters.Add(item);
}
cmd.ExecuteNonQuery();
cmd.Connection.Close();
// How to get the result to entity in a clean manner.
}
Using System.reflection in those situation is really handy.
public static List<T> Convert<T>(IDataReader dr) where T : class, new()
{
List<T> list = new List<T>();
T obj = default(T);
while (dr.Read()) {
obj = Activator.CreateInstance<T>();
foreach (PropertyInfo prop in obj.GetType().GetProperties()) {
if (!object.Equals(dr[prop.Name], DBNull.Value)) {
prop.SetValue(obj, dr[prop.Name], null);
}
}
list.Add(obj);
}
return list;
}
using (var conn = new SqlConnection(_dbContextProvider.DbContext.Database.Connection.ConnectionString))
{
conn.Open();
SqlCommand cmd = new SqlCommand(#"[dbo].[GetOutput]", conn);
cmd.CommandTimeout = 60;
cmd.CommandType = CommandType.StoredProcedure;
foreach (var item in list)
{
cmd.Parameters.Add(item);
}
using ( var reader = cmd.ExecuteReader() ){
List<Entity> result = Convert<Entity>(reader); // convert to entity.
cmd.Connection.Close();
}
}
I would in all honesty send over as an array and convert to table type within SQL and do the dirty work on the server side. Also a good way to be able to specify the timeout can be done by either the connection strings within your config file or you can also pass that same parameter over to sql with a WAITFOR DELAY.
Cheers!
Not that hard, do it like this
note, this is lazy eval so it should perform well when there is user IO, still fairly fast in other cases, I've used it in data ETL projects with many records.
public static IEnumerable<dynamic>( /* params */)
{
// build command object here.
using (SqlDataReader reader = cmd.ExecuteReader())
{
if (reader.Read()) // read the first one to get the columns collection
{
var cols = reader.GetSchemaTable()
.Rows
.OfType<DataRow>()
.Select(r => r["ColumnName"]);
do
{
dynamic t = new System.Dynamic.ExpandoObject();
foreach (string col in cols)
{
((IDictionary<System.String, System.Object>)t)[col] = reader[col];
}
yield return t;
} while (reader.Read());
}
}
// remember to close connection
}
From my simple DB framework https://gist.github.com/hoganlong/b7f5c5e8dde61ae3cd6f
General info
I'm busy writing my own MySQL database class inside C#. At the moment I'm trying to write a Select method that accepts a select query and returns a List with all the data. I'm still very new to C# so if my approach to this problem is wrong, please do not hesitate to tell me.
The problem
Right now my method expects 2 parameters. The query and the number of columns selected by the query. With that info I prepare my List and start filling it up. To do this I'm relying on the amount of columns with a foreach loop. However, I have no idea on how to get the correct column names
when adding to the List. Aside from that, I'm not sure if my approach is going to work. So I hope you guys would like to take a look at my method and help me out finishing it.
The method
public List<string>[] Select(string query, int items)
{
//Create a list to store the result
List<string>[] resultList = new List<string>[items];
for(int i = 0; i < items; i++)
{
resultList[i] = new List<string>();
}
//Open connection
if (this.OpenConnection() == true)
{
//Create Command
MySqlCommand cmd = new MySqlCommand(query, connection);
//Create a data reader and Execute the command
MySqlDataReader dataReader = cmd.ExecuteReader();
//Read the data and store them in the list
while (dataReader.Read())
{
for(int j = 0; j < items; j++)
{
resultList[j].Add(dataReader["columnName_here"] + "");
}
}
//close Data Reader
dataReader.Close();
//close Connection
this.CloseConnection();
//return list to be displayed
return resultList;
}
else
{
return resultList;
}
}
MySqlDataReader is derived from System.Data.Common.DbDataReader, so you can use this code to get the columns:
for (int c = 0; c < dataReader.FieldCount; c++)
{
string name = dataReader.GetName(c);
Type type = dataReader.GetFieldType(c);
}
The follow code will insert some values in my database. It gets 6 random values, puts the stuff in an array and then inserts it in the database.
public void LottoTest(object sender, EventArgs e)
{
Dictionary<int, int> numbers = new Dictionary<int, int>();
Random generator = new Random();
while (numbers.Count < 6)
{
numbers[generator.Next(1, 49)] = 1;
}
string[] lotto = numbers.Keys.OrderBy(n => n).Select(s => s.ToString()).ToArray();
foreach (String _str in lotto)
{
Response.Write(_str);
Response.Write(",");
}
var connectionstring = "Server=C;Database=lotto;User Id=lottoadmin;Password=password;";
using (var con = new SqlConnection(connectionstring)) // Create connection with automatic disposal
{
con.Open();
using (var tran = con.BeginTransaction()) // Open a transaction
{
// Create command with parameters (DO NOT PUT VALUES IN LINE!!!!!)
string sql =
"insert into CustomerSelections(val1,val2,val3,val4,val5,val6) values (#val1,#val2,#val3,#val4,#val5,#val6)";
var cmd = new SqlCommand(sql, con);
cmd.Parameters.AddWithValue("val1", lotto[0]);
cmd.Parameters.AddWithValue("val2", lotto[1]);
cmd.Parameters.AddWithValue("val3", lotto[2]);
cmd.Parameters.AddWithValue("val4", lotto[3]);
cmd.Parameters.AddWithValue("val5", lotto[4]);
cmd.Parameters.AddWithValue("val6", lotto[5]);
cmd.Transaction = tran;
cmd.ExecuteNonQuery(); // Insert Record
tran.Commit(); // commit transaction
Response.Write("<br />");
Response.Write("<br />");
Response.Write("Ticket has been registered!");
}
}
}
What is the best way to loop and insert MASS entries into the database. Lets say, 100,000 records via C#? I want to be able to generate the random numbers by my method and utilize the insert which i have too..
For true large scale inserts, SqlBulkCopy is your friend. The easy but inefficient way to do this is just to fill a DataTable with the data, and throw that at SqlBulkCopy, but it can be done twice as fast (trust me, I've timed it) by spoofing an IDataReader. I recently moved this code into FastMember for convenience, so you can just do something like:
class YourDataType {
public int val1 {get;set;}
public string val2 {get;set;}
... etc
public DateTime val6 {get;set;}
}
then create an iterator block (i.e. a non-buffered forwards only reader):
public IEnumerable<YourDataType> InventSomeData(int count) {
for(int i = 0 ; i < count ; i++) {
var obj = new YourDataType {
... initialize your random per row values here...
}
yield return obj;
}
}
then:
var data = InventSomeData(1000000);
using(var bcp = new SqlBulkCopy(connection))
using(var reader = ObjectReader.Create(data))
{ // note that you can be more selective with the column map
bcp.DestinationTableName = "CustomerSelections";
bcp.WriteToServer(reader);
}
You need Sql bulk insert. There is a nice tutorial on msdn http://blogs.msdn.com/b/nikhilsi/archive/2008/06/11/bulk-insert-into-sql-from-c-app.aspx
MSDN Table Value Parameters
Basically, you fill a datatable with the data you want to put into SqlServer.
DataTable tvp = new DataTable("LottoNumbers");
forach(var numberSet in numbers)
// add the data to the dataset
Then you pass the data through ADO using code similar to this...
command.Parameters.Add("#CustomerLottoNumbers", SqlDbType.Structured);
command.Parameters["CustomerLottoNumbers"].Value = tvp;
Then you could use sql similar to this...
INSERT CustomerSelections
SELECT * from #CustomerLottoNumbers
I am having problems with this grid view. I am populating it with a query. However, it will not populate or even appear if I use a while(reader.Read()) structure. Without the while structure, it works fine. However, I need to access two specific fields. The code is below.
SqlDataReader myReader;
try
{
using (myConnection)
{
myConnection.Open();
ArrayList arrliGames = new ArrayList();
myReader = myCommand.ExecuteReader();
decimal decTicketCost = 0;
int intTicketCount = 0;
while (myReader.Read ())
{
decTicketCost = Convert .ToDecimal (myReader ["TicketCost"]);
intTicketCount =Convert .ToInt32 (myReader ["NumTickets"]);
}
//Binds listbox
grdEvents.DataSource = myReader ;
grdEvents.DataBind();
}
}
The SqlDataReader is forward-only. When you first iterate over the rows, there is "nothing left" in it to display afterwards.
I suggest that you use the reader to populate a strongly-typed list in memory, and then bind the GridView to the list instead. Example:
var myList = new List<TicketInfo>();
while (myReader.Read())
{
myList.Add(new TicketInfo
{
TicketCost = Convert.ToDecimal(myReader["TicketCost"]),
NumTickets = Convert.ToInt32(myReader["NumTickets"])
});
}
grdEvents.DataSource = myList;
grdEvents.DataBind();
The code example above assumes that you have a class called TicketInfo defined as:
class TicketInfo
{
public decimal TicketCost { get; set; }
public int NumTickets { get; set; }
}
If you haven't used generics (such as List<TicketInfo> in the example) before, I suggest you do some reading on the subject.
create a class with two properties
1. decTicketCost
2. intTicketCount
now in while loop create instance and assign the value to the object properties
and add it in a list.
Finally bind the list.
I guest you have set datasource to myList instead myReader
grdEvents.DataSource = myList;
Edit: You need to add other column in your list object.
while (myReader .Read ())
{
//myList-- Add other columns you need to display in the gridview
//As I don't know the your Data Reader column, I can't give you exact mylist object
myList.Add(new TicketInfo{TicketCost = Convert.ToDecimal(myReader["TicketCost"]),NumTickets = Convert.ToInt32(myReader["NumTickets"]) });
}
You could replace
while (myReader .Read ())
{
decTicketCost = Convert .ToDecimal (myReader ["TicketCost"]); intTicketCount = Convert .ToInt32 (myReader ["NumTickets"]);
}
//Binds listbox
grdEvents.DataSource = myReader ;
grdEvents.DataBind();
to
grdEvents.DataSource = myReader ;
grdEvents.DataBind();
And then the gridview for some values
hope this help
replace the following line
grdEvents.DataSource = myReader;
with
grdEvents.DataSource = myList;
How about this:
using (myConnection)
{
myConnection.Open();
DataSet ds = myCommand.ExecuteDataSet();
//Binds listbox
grdEvents.DataSource = ds;
grdEvents.DataBind();
}
ForEach (DataRow dr in ds.tables[0].rows)
myList.Add(new TicketInfo{TicketCost = Convert.ToDecimal(myReader["TicketCost"]),NumTickets = Convert.ToInt32(myReader["NumTickets"]) });