I'm trying to fetch data from DB with optional overload to pass the connection. I could do it in two ways.
public DataTable GetData()
{
using (SqlConnection con = new SqlConnection("..."))
{
return GetData(con);
}
}
public DataTable GetData(SqlConnection con)
{
// fetch data
return dtData;
}
or
public DataTable GetData(SqlConnection con=null)
{
bool OpenCon = false;
if (con == null)
{
con = new SqlConnection("...");
OpenCon = true;
}
try
{
// fetch data
}
finally
{
if (OpenCon)
con.Close();
}
return dtData;
}
The first case seems impressive. However, I am getting tons of methods for each transaction. In the second case, lots of code need to be written in each method as there is no way to use "using" block in this case.
The situation is still worse with other transactions like update or delete, since I need to have another overload to pass the transaction.
Is there a better way?
1st one is the best choice
public DataTable GetData()
{
using (SqlConnection con = new SqlConnection("..."))
{
return GetData(con);
}
}
public DataTable GetData(SqlConnection con)
{
// fetch data
return dtData;
}
Here you have Object oriented implementation, providing specific boundry as well as removal of object(using statement to destory objects ) are all there which is a good programmaing.
Related
I have created a simplified SQL Data class, and a class method for returning a ready to use resultset:
public SQL_Data(string database) {
string ConnectionString = GetConnectionString(database);
cn = new SqlConnection(ConnectionString);
try {
cn.Open();
} catch (Exception e) {
Log.Write(e);
throw;
}
}
public SqlDataReader DBReader(string query) {
try {
using (SqlCommand cmd = new SqlCommand(query, this.cn)) {
return cmd.ExecuteReader(CommandBehavior.CloseConnection);
}
} catch {
Log.Write("SQL Error with either Connection String:\n" + cn + " \nor Query:\n" + query);
throw;
}
}
(I catch any errors, log them, and then catch the error higher up the chain. Also, I did not include the ConnectionString() code for brevity. It just returns the requested connection string. That's all.)
This all works just fine, and with a single line of code, I'm ready to .Read() rows.
SqlDataReader rs = new SQL_Data("MyDatabase").DBReader(#"SELECT * FROM Employees");
while (rs.Read()) {
// code
}
rs.Close();
I want to expand this and add a .ColumnReader() method that I want to chain to .DBReader() like this:
string empID = new SQL_Data("MyDatabase").DBReader(#"SELECT * FROM Employees).ColumnReader("EmpID");
I attempted this by adding a .ColumnReader() method, but it ends up being a method of SQL_Data() class directly, not a member or extension of .DBReader(). I also tried adding the .ColumnReader() inside the .DBReader() (like a "closure"), but that didn't work either.
Can this be done?
This ended up working for me:
public static class SQLExtentions {
public static dynamic ColumnReader(this SqlDataReader rs, string colName) {
return rs[colName];
}
}
I will have to expand on it a bit to add some error checking, and perhaps return more than just the dynamic value - like return an object with the value and it's SQL data type. But Paul and Bagus' comments got me on the right track.
The model in the parameter has values all the way to the DAL method where the query is but somehow nothing changes in the database. Anyone got any clue why? Also no errors occurred.
Controller:
public ActionResult AddCar(CarModel car)
{
if(ModelState.IsValid)
{
u.AddCar(new DTO.CarDTO(car.Color, car.Type, car.NumberOfSeats, car.Price, car.MaxKilometres, car.Addition, car.ModelID, car.License_Plate, car.Fueltype, car.ReservationStatus));
return RedirectToAction("Index", "Home");
}
return View();
}
Logic:
public void AddCar(CarDTO c)
{
carDAL.AddCar(new CarDTO(c.Color, c.Type, c.NumberOfSeats, c.Price, c.MaxKilometres, c.Addition, c.ModelID, c.License_Plate, c.Fueltype, c.ReservationStatus));
}
Interface layer:
public interface ICarDAL<CarDTO>
{
public void AddCar(CarDTO c) { }
}
DAL, class does have connectionstring:
public void AddCar(CarDTO c)
{
using (SqlConnection con = new SqlConnection(connectionString))
{
string query = "INSERT INTO [dbo].[Car](Price, Addition, License_Plate, MaxKm, Type, Color, NumberOfSeats, Fueltype, reservationStatus, ModelID) " +
"VALUES(#Price, #Addition, '#LicensePlate', #MaxKm, '#Type', '#Color', #NumberOfSeats, #FuelType, #Reserved, #ModelID)";
using (SqlCommand cmd = new SqlCommand(query, con))
{
cmd.Parameters.AddWithValue("#Price", c.Price);
cmd.Parameters.AddWithValue("#Addition", c.Addition);
cmd.Parameters.AddWithValue("#LicensePlate", c.License_Plate);
cmd.Parameters.AddWithValue("#MaxKm", c.MaxKilometres);
cmd.Parameters.AddWithValue("#Type", c.Type);
cmd.Parameters.AddWithValue("#Color", c.Color);
cmd.Parameters.AddWithValue("#NumberOfSeats", c.NumberOfSeats);
cmd.Parameters.AddWithValue("#FuelType", c.Fueltype);
cmd.Parameters.AddWithValue("#ReservationStatus", c.ReservationStatus);
cmd.Parameters.AddWithValue("#ModelID", c.ModelID);
}
}
}
Found the error but I still dont know how to fix it:
https://imgur.com/xjKIqED
Solution: First of all I needed to use ExecuteNonQuery() to make the query actually do something...I thought SqlCommand already executed it for you. Secondly my password consisted out of ************ which I thought it wouldn't matter but I guess it did.
public void AddCar(CarDTO c)
{
using (SqlConnection con = new SqlConnection(connectionString))
{
if (con.State==ConnectionState.Closed)
{
con.Open();
}
[Your code]
con.Close();
}
}
You may need to use a "save" function, I'm not seeing anywhere in your code where the SQL command is actually executed, I see it being built, but I've used it in the past with something similar like cmd.Execute()
I have a process that is to be used to load data from various sources to a SQL Server database. Within the process, I have several methods that each consume file data and return a DataTable object. Depending on the type of data to be loaded, one of these methods is called for any single run of the process.
All of the DataTable objects created by these methods are consumed by the same target method, which transfers the data to SQL Server. This has led to some duplication of code:
if (useDT == 1)
{
using (DataTable dt = MakeDT1())
{
ConsumeDT(dt);
}
}
if (useDT == 2)
{
using (DataTable dt = MakeDT2())
{
ConsumeDT(dt);
}
}
(Simplified for clarity, real world names are descriptive)
I'd like to avoid this if at all possible. Is it possible to pre-calculate the correct method to call to generate the DataTable, then call ConsumeDT(dt) just once? E.g.
switch (useDT)
{
case 1
dtCall = MakeDT1()
break;
case 2
dtCall = MakeDT2()
}
using (DataTable dt = dtCall)
//etc
Thanks in advance, Iain
Write a little helper method that returns the correct kind of DT object:
private DataTable makeDt(bool useDT)
{
return useDT ? MakeDT1() : MakeDT2();
}
And then call that in the using like so:
using (var dt = makeDt(useDt))
{
ConsumeDT(dt);
}
This has the advantage of assigning the disposable dt inside a using making it unlikely that someone will write code that could cause a leak.
You can leave out the using block, you'll just have to make sure to correctly dispose the object in the end:
DataTable dt;
switch(useDT) {
case 1: dt = MakeDT1(); break;
case 2: dt = MakeDT2(); break;
}
try {
ConsumeDt(dt);
} finally {
dt.Dispose();
}
I'm working on a method that is static, and returns a value off a WinForm, it spawns the new form on a button click, and upon hitting the submit or cancel buttons it throws back its value.
The issue is, I cant refer to a combobox control on my form to populate it with the results of my sqlreader.
I have read suggestions that I use a wrapper that looks akin to
public ComboBox comboHolder { get return this.foo }
however I can't seem to refer to it either. Any suggestions to remedy this ?
Full code
public ComboBox comboboxWrapper
{
get { return this.comboUsernames; }
}
public static string SelectProfile()
{
Form selectProfile = new Select_Profile();
selectProfile.ShowDialog();
SqlConnection connection = new SqlConnection(#"Data Source=(LocalDB)\v11.0;AttachDbFilename=|DataDirectory|\Users.mdf;Integrated Security=True;Connect Timeout=30");
connection.Open();
SqlCommand command = new SqlCommand("SelectAllUsers", connection);
SqlDataReader usersReader = command.ExecuteReader();
List<string> accountNames = new List<string>();
while (usersReader.Read())
{
accountNames.Add((string)usersReader["Username"]);
}
//populate the combo box
foreach (string s in accountNames)
{
//I'd like to call comboboxWrapper here.
}
//set the combo box to have a default item
// combo.SelectedIndex = 0;
}
Also, this is a work in progress, I realize I should have some try, catch and a finally statement, other than that i'm open to any suggestions for code improvement.
Thanks!
I would suggest just not making the method static. But if you really need to for some reason, you could pass a reference to the form into your static method, e.g.:
SelectProfile(Form myForm)
Then you would be able to use it inside the method like this:
foreach (string s in accountNames)
{
// e.g myForm.comboboxWrapper
}
your static method need object of class for that you have to pass object of class where comboboxwrapper defined
public static string SelectProfile(ClassobjectofCoboboxWrapper obj)
{
obj.comboboxWrapper;
}
Call to this method from outside
SelectProfile(new ClassobjectofCoboboxWrapper())
Note:
As static method are not related to instace of object its related to class. So to refer element in static method which are not static you either need to create object of refering class or you need to pass object of class your want to refer.
This is your form instance:
Form selectProfile = new Select_Profile();
So you'd call comboboxWrapper on that instance:
selectProfile.comboboxWrapper
Though first you'll need to change its type, since Form doesn't have a member called comboboxWrapper. Declare it like this instead:
Select_Profile selectProfile = new Select_Profile();
or simply:
var selectProfile = new Select_Profile();
Even though the comboboxWrapper member is defined outside the static method, it's inside the form instance. A static member has no default notion of a particular instance and needs to be provided with one. Or, in this case, internally creates one.
First, decompose your solution: just don't cram database and UI into single method. Next think over what your method is supposed to return as a String:
public static IEnumerable<String> AccountNames() {
//TODO: Move it into Settings/Config...
String connectionString = #"Data Source=(LocalDB)\v11.0;AttachDbFilename=|DataDirectory|\Users.mdf;Integrated Security=True;Connect Timeout=30";
// Dispose (via using) all Disposable...
using (SqlConnection connection = new SqlConnection(connectionString)) {
connection.Open();
// Dispose: prevent resource leakage...
using (SqlCommand command = new SqlCommand("SelectAllUsers", connection)) {
using (SqlDataReader usersReader = command.ExecuteReader()) {
while (usersReader.Read())
yield return (string)usersReader["Username"];
}
}
}
}
// returns selected profile
// or null if no profile was seelcted
public static string SelectProfile() {
// var: You need Select_Profile, not just a Form, right?
// again (using): don't forget to clear up the resources
using (var selectProfile = new Select_Profile()) {
// Providing that comboboxWrapper is public (bad practice)
// or SelectProfile() is implemented within Select_Profile class (good one)
selectProfile.comboboxWrapper.Items.AddRange(AccountNames());
if (selectProfile.comboboxWrapper.Items.Count > 0)
selectProfile.comboboxWrapper.SelectedIndex = 0;
if (selectProfile.ShowDialog() == System.Windows.Forms.DialogResult.OK) {
if (selectProfile.comboboxWrapper.SelectedIndex < 0)
return null; // No item to select
else
selectProfile.comboboxWrapper.SelectedItem.ToString();
}
else
return null; // Just closed
}
}
In short I am writing a class handler to handle to database integration of some software I am writing for myself, however as there is not always a connection to the remote database I thought I would use SQLCE to create a local database buffer so when a connection is made the changes can be synchronized.
So far it is going well except for the parameters. The function I am looking to call is shown below however this function is complaining about invalid arguments.
public Object run(string query, List<Object> dbparams = null)
{
if (MyDB.isConnected())
{
return MyDB.run(query, dbparams);
}
else
{
SqlCeCommand sql = _OfflineConnection.CreateCommand();
sql.CommandText = query;
if (dbparams.Count > 0)
{
sql.Parameters.AddRange(dbparams.ToArray());
}
return sql;
}
}
MyDB.run is the exact same code as in the else statement except for mysql, the line that it is moaning about is the return mydb.run as the mydb class is expecting the dbparams list to be of mysqlparameters.
Does anyone know how I can achieve this? I attempted to use LINQ to do a convert but that failed miserably.
EDIT
At present I have the following working but I am sure there is a cleaner solution
public Object run(string query, List<Object> dbparams = null)
{
if (MyDB.isConnected())
{
List<MySqlParameter> mydbparams = null;
for (int i = 0; i < dbparams.Count; i++)
{
mydbparams.Add((MySqlParameter)dbparams[i]);
}
return MyDB.run(query, mydbparams);
}
else
{
SqlCeCommand sql = _OfflineConnection.CreateCommand();
sql.CommandText = query;
if (dbparams.Count > 0)
{
sql.Parameters.AddRange(dbparams.ToArray());
}
return sql;
}
}
A bit cleaner solution would be
mydbparams = dbparams.Cast<MySqlParameters>().ToList();
Also, you should check for and handle the null condition of dbparams.