I'm trying to come up with a way just to load a table from SQL Server into a class, without having to tell it anything. Basically, just create the class and have it know what to load, based on that. Here's what I have so far.
My question is, is there some way to keep from having to hard code the types, to call reader.readString, reader. readInt32, etc.. based on the FieldType?
private Int32? readInt32(SqlDataReader reader, string columnName)
{
Int32? result = null;
if (!reader.IsDBNull(reader.GetOrdinal(columnName)))
{
result = reader.GetInt32(reader.GetOrdinal(columnName));
};
return result;
}
public List<T> readTable(string table, string wherecls, string connStr)
{
List<T> result = new List<T>();
using (SqlConnection connection = new SqlConnection(connStr))
{
using (SqlCommand command = connection.CreateCommand())
{
command.CommandText = "select * from " + table;
if (wherecls.Length > 0) command.CommandText += " where " + wherecls;
connection.Open();
using (var reader = command.ExecuteReader())
{
while (reader.Read())
{
Object i = Activator.CreateInstance(typeof(T));
System.Reflection.FieldInfo[] fieldInfoList = typeof(T).GetFields();
foreach (System.Reflection.FieldInfo f in fieldInfoList)
{
if (f.FieldType == typeof(string)) f.SetValue(i, readString(reader, f.Name));
if (f.FieldType == typeof(Int32)) f.SetValue(i, readInt32(reader, f.Name));
if (f.FieldType == typeof(Int16)) f.SetValue(i, readInt16(reader, f.Name));
if (f.FieldType == typeof(byte)) f.SetValue(i, readByte(reader, f.Name));
if (f.FieldType == typeof(short)) f.SetValue(i, readShort(reader, f.Name));
}
result.Add((T)i);
}
}
}
}
return result;
}
Thank you,
Dan Chase
What you describe is a lot of work... and is exactly what tools like "dapper" already do. So my suggestion here: use dapper:
// Dapper adds a Query<T>(this DbConnection, ...) extension method
var data = connection.Query<T>(sql, args).AsList();
I would, however, say that string wherecls sends shivers down my spine - that sounds like a SQL injection nightmare. But... that's up to you.
Try this.
Make sure the type has a public default constructor--one that takes no arguments--and that the column names in the SQL string exactly match the name of the type's public properties.
namespace MyNamespace {
using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Reflection;
public static class MyExtensions {
public static IEnumerable<T> Query<T>(this SqlConnection cn, string sql) {
Type TypeT = typeof(T);
ConstructorInfo ctor = TypeT.GetConstructor(Type.EmptyTypes);
if (ctor == null) {
throw new InvalidOperationException($"Type {TypeT.Name} does not have a default constructor.");
}
using (SqlCommand cmd = new SqlCommand(sql, cn)) {
using (SqlDataReader reader = cmd.ExecuteReader()) {
while (reader.Read()) {
T newInst = (T)ctor.Invoke(null);
for (int i = 0; i < reader.FieldCount; i++) {
string propName = reader.GetName(i);
PropertyInfo propInfo = TypeT.GetProperty(propName);
if (propInfo != null) {
object value = reader.GetValue(i);
if (value == DBNull.Value) {
propInfo.SetValue(newInst, null);
} else {
propInfo.SetValue(newInst, value);
}
}
}
yield return newInst;
}
}
}
}
}
}
Maybe my solution is a bit better. I populate type T using extension with handling null values and populating properties in order I like.
Example:
public async Task<ObservableCollection<T>> Search_data<T>()
{
var data = new ObservableCollection<T>();
try
{
using (OracleConnection con = new OracleConnection(connn_string))
{
con.Open();
OracleCommand cmd = new OracleCommand("MySchema.SomeTable", con)
{
CommandType = CommandType.StoredProcedure
};
cmd.Parameters.Add("result", OracleDbType.RefCursor, ParameterDirection.Output);
using (OracleDataReader rdr = cmd_iskanje_apo.ExecuteReader())
{
while (await rdr.ReadAsync())
{
var item = Activator.CreateInstance<T>();
item.SetValue("NAME", rdr.IsDBNull(0) ? null : rdr.GetString(0));
item.SetValue("SURNAME", rdr.IsDBNull(1) ? null : rdr.GetString(1));
item.SetValue("ADDRESS", rdr.IsDBNull(2) ? null : rdr.GetString(2));
data.Add(item);
};
}
}
return data;
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
return null;
}
}
Extension:
public static void SetValue<T>(this T _source, string _property_name, object _value)
{
_source.GetType().GetProperty(_property_name).SetValue(_source, _value);
}
I tried to look through some links but didn't work for me but this is what I have:
[HttpGet]
public string GetTimes()
{
var varCheck = checkUser();
if (varCheck.Item1)
{
using (SqlConnection sc = new SqlConnection(connectionTest))
{
try
{
sc.Open();
using (SqlDataAdapter sda = new SqlDataAdapter("GetTheTimes", sc))
{
sda.SelectCommand.CommandType = CommandType.StoredProcedure;
sda.Fill(ds);
foreach (DataTable table in ds.Tables)
{
foreach (DataRow row in table.Rows)
{
listItems.Add(new DataCheck
{
var1 = row["var1"].ToString(),
var2 = row["var2"].ToString()
});
}
}
}
}
catch (SqlException sqe)
{
return "There was an error with retrieving the data, please try again later";
}
finally
{
sc.Close();
}
}
var jsonSerialier = new JavaScriptSerializer();
var json = jsonSerialier.Serialize(listItems);
return json;
}
else
{
return "Failed";
}
}
When the API is called it is returned with escape double quotes. How can I modify so it returns a pure Json Object.
Try this:
[HttpGet]
//to prevent conflict with HttpGet use inline namespacing
public System.Web.Mvc.JsonResult GetTimes()
{
return new System.Web.Mvc.JsonResult { Data = result };
}
I have a problem with my method. Environment prompts me that "not all code paths return a value". I don't understand why. I don't see this path which doesn't return a value. Can someone explain me why ? Thanks and regards.
I'm using Visual Studio 2015 Community
private CognexDataMan.EnumCodeKind CheckCode(String strCodeToCheck)
{
String[] splitCode = strCodeToCheck.Split(new char[] { ',' });
String strCode = splitCode[1];
if (strCode == String.Empty)
{
// code incorrect
return CognexDataMan.EnumCodeKind.CODE_INCORRECT;
}
else
{
CognexDataMan.EnumCodeKind resultToReturn;
SqlConnection sqlCnn = new SqlConnection(GlobalData.CnnString);
SqlCommand sqlCmd = new SqlCommand("sp_CheckCode", sqlCnn);
sqlCmd.Parameters.AddWithValue("#Code", strCode);
sqlCmd.Parameters.Add(new SqlParameter()
{
ParameterName = "#IsUnique",
DbType = DbType.Int32,
Direction = ParameterDirection.Output
});
try
{
sqlCnn.Open();
sqlCmd.ExecuteNonQuery();
sqlCnn.Close();
// 1 means is unique
if ((Int32)sqlCmd.Parameters["#IsUnique"].Value == 1)
{
return CognexDataMan.EnumCodeKind.CODE_OK;
}
else // 0 means is NOT unique
{
return CognexDataMan.EnumCodeKind.CODE_NOK;
}
}
catch (SqlException sqlEx)
{
// error SQL while checking code
}
catch (Exception ex)
{
// general exception
}
}
}
All code paths must return a value including your catch blocks and as well as after the if/else statement:
private CognexDataMan.EnumCodeKind CheckCode(String strCodeToCheck)
{
String[] splitCode = strCodeToCheck.Split(new char[] { ',' });
String strCode = splitCode[1];
if (strCode == String.Empty)
{
// code incorrect
return CognexDataMan.EnumCodeKind.CODE_INCORRECT;
}
else
{
CognexDataMan.EnumCodeKind resultToReturn;
SqlConnection sqlCnn = new SqlConnection(GlobalData.CnnString);
SqlCommand sqlCmd = new SqlCommand("sp_CheckCode", sqlCnn);
sqlCmd.Parameters.AddWithValue("#Code", strCode);
sqlCmd.Parameters.Add(new SqlParameter()
{
ParameterName = "#IsUnique",
DbType = DbType.Int32,
Direction = ParameterDirection.Output
});
try
{
sqlCnn.Open();
sqlCmd.ExecuteNonQuery();
sqlCnn.Close();
// 1 means is unique
if ((Int32)sqlCmd.Parameters["#IsUnique"].Value == 1)
{
return CognexDataMan.EnumCodeKind.CODE_OK;
}
else // 0 means is NOT unique
{
return CognexDataMan.EnumCodeKind.CODE_NOK;
}
}
catch (SqlException sqlEx)
{
// error SQL while checking code
return CognexDataMan.EnumCodeKind.CODE_NOK;
}
catch (Exception ex)
{
// general exception
return CognexDataMan.EnumCodeKind.CODE_NOK;
}
}
return CognexDataMan.EnumCodeKind.CODE_NOK;
}
I have three/four functions in my VSTO addin and some member variables:
Member Variables:
private Dictionary<string, string> clientDict;
private Dictionary<string, string> clientHistoryDict;
Function that works with those variables:
public void generateClientDict()
{
clientDict.Add("alcatel-lucent.com", "Alcatel-Lucent");
clientDict.Add("emerson.com", "Emerson");
clientDict.Add("ericsson.com", "Ericsson");
clientDict.Add("fortress-technologies.com", "Fortress Technologies");
clientDict.Add("genesys.com", "Genesys");
clientDict.Add("hitachi.com", "Hitachi Data Systems");
clientDict.Add("hp.com", "Hewlett Packard");
clientDict.Add("lg.com", "LG Electronics");
clientDict.Add("samsung.com", "Samsung");
clientDict.Add("sap.com", "SAP");
clientDict.Add("tellabs.com", "Tellabs");
clientDict.Add("thiel-audio.com", "Thiel Audio");
clientDict.Add("xerox.com", "Xerox");
clientDict.Add("zebra.com", "Zebra Technologies");
clientHistoryDict.Add("3com.com", "3Com- CommWorks");
clientHistoryDict.Add("3m.com", "3M");
clientHistoryDict.Add("abis.com", "ABIS");
clientHistoryDict.Add("acxiom.com", "Acxiom");
clientHistoryDict.Add("ajusa.com", "AJ-USA");
clientHistoryDict.Add("akamai.com", "Akamai Technologies");
clientHistoryDict.Add("alcatel-lucent.com", "Alcatel-Lucent");
clientHistoryDict.Add("avaya.com", "Avaya");
clientHistoryDict.Add("beckmancoulter.com", "Beckman Coulter");
clientHistoryDict.Add("bellsouth.com", "BellSouth");
clientHistoryDict.Add("bridgevine.com", "Bridgevine");
clientHistoryDict.Add("casio.com", "Casio");
clientHistoryDict.Add("cca.com", "CCA");
clientHistoryDict.Add("ccs.com", "CCS");
clientHistoryDict.Add("centurylink.com", "CenturyLink");
clientHistoryDict.Add("chinatelecom.com", "China Telecom");
clientHistoryDict.Add("cisco.com", "Cisco");
clientHistoryDict.Add("comcast.com", "Comcast");
clientHistoryDict.Add("comodo.com", "Comodo");
clientHistoryDict.Add("comverge.com", "Comverge");
clientHistoryDict.Add("coriant.com", "Coriant (Spin off from Tellabs)");
clientHistoryDict.Add("daneelectric.com", "Dane Electric");
clientHistoryDict.Add("dell.com", "Dell");
clientHistoryDict.Add("disney.com", "Disney");
clientHistoryDict.Add("siemens.com", "Efficient Networks- Siemens");
clientHistoryDict.Add("emc.com", "EMC");
clientHistoryDict.Add("emergentcommunications.com", "Emergent Communications");
clientHistoryDict.Add("emerson.com", "Emerson");
clientHistoryDict.Add("epson.com", "Epson");
clientHistoryDict.Add("ericsson.com", "Ericsson");
clientHistoryDict.Add("exigen.com", "Exigen Services");
clientHistoryDict.Add("frbny.com", "Federal Reverse Bank of New York");
clientHistoryDict.Add("hometeamsports.com", "Fox Home Team Sports");
clientHistoryDict.Add("freemansoundlabs.com", "Freeman Sound Labs");
clientHistoryDict.Add("genesys.com", "Genesys");
clientHistoryDict.Add("here.com", "HERE, a Nokia Company");
clientHistoryDict.Add("hp.com", "Hewlett Packard");
clientHistoryDict.Add("hitachi.com", "Hitachi Data Systems");
clientHistoryDict.Add("intel.com", "Intel");
clientHistoryDict.Add("lg.com", "LG Electronics");
clientHistoryDict.Add("samsung.com", "Samsung");
clientHistoryDict.Add("sap.com", "SAP");
clientHistoryDict.Add("subway.com", "Subway");
clientHistoryDict.Add("tellabs.com", "Tellabs");
clientHistoryDict.Add("thiel-audio.com", "Thiel Audio");
clientHistoryDict.Add("xerox.com", "Xerox");
clientHistoryDict.Add("zebra.com", "Zebra Technologies");
}
Now this function works with the member variables. (All of these are in the same class). But these functions do not:
public void populateClientDict(SqlConnection conn)
{
//Dictionary<string, string> clientDict = new Dictionary<string, string>(); If I don't add this I get an error
try
{
using (conn)
{
SqlCommand command = new SqlCommand(
#"SELECT ClientDirName, ClientEmailDomain FROM ClientTable;",
conn);
conn.Open();
SqlDataReader reader = command.ExecuteReader();
if (reader.HasRows)
{
while (reader.Read())
{
string clientDir = reader.GetString(0);
string clientEmail = reader.GetString(1);
clientDict.Add(clientEmail, clientDir);
}
}
else
{
MessageBox.Show("No rows found in ClientTable", "Rows Not Found", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
reader.Close();
}
}
catch (InvalidOperationException ex)
{
MessageBox.Show(String.Format("Exception while accessing ClientTable: {0}", ex), "Exception", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
catch (SqlException ex)
{
MessageBox.Show(String.Format("Exception while accessing ClientTable: {0}", ex), "Exception", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
public void populateClientHistoryDict(SqlConnection conn)
{
//Dictionary<string, string> clientHistoryDict = new Dictionary<string, string>(); if I don't add this I get an error
try
{
using (conn)
{
SqlCommand command = new SqlCommand(
#"SELECT ClientDirName, ClientEmailDomain FROM ClientHistoryTable;",
conn);
conn.Open();
SqlDataReader reader = command.ExecuteReader();
if (reader.HasRows)
{
while (reader.Read())
{
string clientDir = reader.GetString(0);
string clientEmail = reader.GetString(1);
clientHistoryDict.Add(clientEmail, clientDir);
}
}
else
{
MessageBox.Show("No rows found in ClientHistoryTable", "Rows Not Found", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
reader.Close();
}
}
catch (InvalidOperationException ex)
{
MessageBox.Show(String.Format("Exception while accessing ClientHistoryTable: {0}", ex), "Exception", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
catch (SqlException ex)
{
MessageBox.Show(String.Format("Exception while accessing ClientHistoryTable: {0}", ex), "Exception", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
As I wrote in the commented out line in the functions, unless i declare the dictionaries in those functions I get this error:
I get the error on this line:
clientDict.Add(clientEmail, clientDir); in populateClientDict() and clientHistoryDict.Add(clientEmail, clientDir); in populateClientHistoryDict()
An exception of type 'System.NullReferenceException' occurred in Archive.dll but was not handled in user code
Additional information: Object reference not set to an instance of an object.
I had a feeling the error is related this part of the functions where it says:
while (reader.Read())
{
clientDir = reader.GetString(0);
string clientEmail = reader.GetString(1);
clientDict.Add(clientEmail, clientDir);
}
ID is the first column, clientDir is the second column, and clientEmail is the third. Maybe I'm using reader.GetString() incorrectly? I read somewhere I could do something like reader.GetString["ClientDirName"] (ClientDirName is the column name) but I'm not sure what the correct thing to do is.
Could this be causing the error? If so, how can I access the 2nd and 3rd columns (provided ID is the first column) properly to avoid this error.
If not, what else could cause it?
I've tried a ton of combinations, as I said it works if I move the Dictionary instantiations within the functions but I don't think that's solving the actual problem.
Any assistance is much appreciated.
I'm guessing you forgot to instantiate the clientDict member variable like you do in your function.
Since your are declaring the same variable in function and instantiating, it takes the local scope (when using without this) and it works.
You could either instantiate your private member in the constructor or during the declaration itself.
private Dictionary<string, string> clientDict=new Dictionary<string, string>();
private Dictionary<string, string> clientHistoryDict=new Dictionary<string, string>();
Please follow this link, if you want to know more about scope.
I am trying to create a method that runs a query then reads the 2 values it returns then place them in the global variables so I can access them in another page. My question is what method should I use because I have two variables to set. Typically I pass the variables that I will be using but in this case I'm not. This main seem simple but I can't think of a way to get these values. I am not sure how to look this problem up to research it either. I have included the code below what I have attempted so far. Thank you for you help.
public string getTotals3()
{
WorkerData workerData = new WorkerData();
StringBuilder sqlString = new StringBuilder();
sqlString.Append("SELECT DISTINCT DataWin8Data, DataWin7Data ");
sqlString.Append("FROM Data ");
sqlString.Append("WHERE Number = 4");
SqlDataReader reader = null;
SqlConnection dbConn = App_Code.DBHelper.getConnection();
try
{
reader = App_Code.DBHelper.executeQuery(dbConn, sqlString.ToString(), null);
if (reader != null)
{
while (reader.Read())
{
workerData.TotalCases4 = reader["DataWin8Data"] != DBNull.Value ? reader["DataWin8Data"].ToString() : string.Empty;
workerData.TotalPercentage4 = reader["DataWin7Data"] != DBNull.Value ? reader["DataWin7Data"].ToString() : string.Empty;
}
}
else
throw new Exception("No records returned");
}
catch (Exception ex)
{
throw ex;
}
finally
{
if (dbConn != null)
{
try { dbConn.Close(); dbConn.Dispose(); }
catch { }
}
if (reader != null)
{
try { reader.Close(); reader.Dispose(); }
catch { }
}
}
return workerData.ToString();
}
Don't use global variables. Return the values out of the method. The calling code should be in charge of placing those values wherever it needs. I recommend reading about Dependency Inversion Principle.
public WorkerData GetWorkerData()
{
...
using (SqlDataReader reader = ...)
{
if (reader.Read())
{
return new WorkerData
{
TotalCases4 = reader["DataWin8Data"] != DBNull.Value ? reader["DataWin8Data"].ToString() : string.Empty,
TotalCases3 = workerData.TotalPercentage4 = reader["DataWin7Data"] != DBNull.Value ? reader["DataWin7Data"].ToString() : string.Empty;
}
}
}
throw new ApplicationException("Could not retrieve worker data.");
}
From your calling class, simply do whatever you want with the return value:
WorkerData workerData = someClass.GetWorkerData();