I am trying to update a record in an access file (.accdb). I am trying to use the .net OleDbCommand and OleDbParameters. I am also trying to use a generic model and store all of the commands and parameters in the System.Data.Common abstract equivalents so that I can easily switch over to SQL Server (which I do plan to do)
So here is the actual command being used
EDIT 2/2/2013 - 9:10pm
the command.ExecuteNonQuery is inside the method named ExecuteNonQuery()
the connectionString and command are defined in the DataAccess class constructor
public class DataAccess
{
private string connectionString;
private DbConnection connection;
private DbCommand command;
private DbDataReader reader;
private DataTable data;
public DataAccess()
{
connectionString = ConfigurationSettings.AppSettings["ConnectionString"];
switch (ConfigurationSettings.AppSettings["DataBaseType"])
{
case "oledb":
connection = new OleDbConnection(connectionString);
command = new OleDbCommand(string.Empty, (OleDbConnection)connection);
break;
case "SQL":
connection = new SqlConnection(connectionString);
command = new SqlCommand(string.Empty, (SqlConnection)connection);
break;
default:
break;
}
}
public void ExecuteNonQuery(string SQL, params DbParameter[] parameters)
{
command.CommandType = CommandType.Text;
command.CommandText = SQL;
command.Parameters.AddRange(parameters);
try
{
command.Connection.Open();
try
{
command.ExecuteNonQuery();
}
catch (Exception ex)
{
throw ex;
}
finally
{
command.Connection.Close();
}
}
catch (Exception ex)
{
throw ex;
}
}
public DbParameter NewParameter(string name, object value)
{
DbParameter param;
switch (ConfigurationSettings.AppSettings["DataBaseType"])
{
case "oledb":
param = new OleDbParameter(name, value);
break;
case "SQL":
param = new SqlParameter(name, value);
break;
default:
param = null;
break;
}
return param;
}
These are the properties in the App.Config File
<add key="DataBaseType" value="oledb"/>
<add key="ConnectionString" value="Provider=Microsoft.ACE.OLEDB.12.0;Data Source=data.accdb"/>
Now the problem is when using parameters in an update statement, the update never happens and also never throws an error. Here is the code for it.
EDIT 2/2/2013 - 9:10pm
the function DataAccess.NewParameter is in the first code block
DALayer.ExecuteNonQuery("UPDATE TileTypes SET Title = #Title, Picture = #Picture, Color = #Color WHERE ID = #ID",
DALayer.NewParameter("#Title", titleTextBox.Text.Trim()),
DALayer.NewParameter("#Picture", typePictureBox.ImageLocation),
DALayer.NewParameter("#Color", colorButton.BackColor.ToArgb()),
DALayer.NewParameter("#ID", id));
I have copied the query into access and replaced all of the parameter names with the actual data being passed, this works fine. I have tried replacing all of the parameters in the SQL text to the ? character to no effect. I have tried enclosing all of the table and column names in brackets [] also to no effect.
ID is an AutoNumber field
Title is a Text field
Picture is a Text field
Color is a Long Integer field
This is some example data that was copied directly from the parameters in the watch window for Visual Studio:
"Edit" (title)
-1 (color)
"data\images\Edit_000000.jpg" (picture)
740 (id)
That ID does exist in the database and was unchanged after the query executed.
EDIT 2/2/2013 - 9:10pm
I am not sure how to check which database is actually being updated, the only thing I could think of was that using the same connection string and connection object I did an insert statement with the same ExecuteNonquery method and it worked in the database I was viewing. And the update statement works just fine like this (without parameters):
DALayer.ExecuteNonQuery("UPDATE TileTypes SET Title = '" + titleTextBox.Text +
"', Color = " + colorButton.BackColor.ToArgb() + ", Picture = '" +
imageLocation + "' WHERE ID = " + id);
EDIT 2/2/2013 - 9:41pm
I have used everything.exe to search my computer for all of the data.accdb files on my computer, I have found no actual .accdb files besides the original but I did find these .lnk files, I do not believe they could have altered this process but I will mention it anyway
data.accdb.LNK
What you are trying to do is something I too have done in the past, but allowed to connect to OleDB (such as Access, Visual FoxPro, etc), SQL-Server, SyBase SQLAnywhere and maybe my implementation might help you. First, each of the elements you would use for connecting work on a common interface, such as IDbConnection, IDbCommand, IDbParameter, etc.
The following I'm posting is a small segment of how I originally structured such multi-database connection type. I've stripped a bunch out and not actually tested this stripped version, but it SHOULD be a good baseline for you to run with.
The premise is a baseline "MyConnection" to almost be like an abstract, but has properties and some "common" methods that would exist under EITHER subclassed definition. From this, each of the functions and parameter types are based on the "I"nterface, not a specific. However, each of the derived will create its OWN proper type. This removes the need to "Case" everything. Hope this helps you along with your Data Access Layer development.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
// for OleDB (Access, VFP, etc)
using System.Data.OleDb;
// for SQL-Server
using System.Data.SqlClient;
namespace DataMgmt
{
public class MyConnection
{
// no matter the connection to server, it will require some "handle"
// that is of type "IDbConnection"
protected IDbConnection sqlConnectionHandle;
// when querying, ANY query could have an exception that needs to have
// possible further review for handling
public Exception LastException
{ get; protected set; }
// When calling an execute command (select, insert, update, delete),
// they all can return how many rows affected
public int RowsAffectedByQuery
{ get; protected set; }
// different databases could have different connection strings. Make
// virtual and throw exception so sub-classed must return proper formatted.
public virtual string GetConnectionString()
{ throw new Exception("GetConnectionString() method must be overridden."); }
// each has its own "IDbConnection" type too
protected virtual IDbConnection SQLConnectionHandle()
{ return sqlConnectionHandle; }
public virtual IDbCommand GetSQLDbCommand()
{ throw new Exception("GetSQLDbCommand() method must be overridden."); }
// generic routine to get a data parameter...
public virtual IDbDataParameter AddDbParmSpecificValue(string ParmName, object UnknownValue)
{ throw new Exception("AddDbParmSpecificValue() method must be overwritten per specific connection."); }
// generic "Connection" since they are all based on IDbCommand...
public override bool SQLConnect()
{
// pre-blank exception in case remnant from previous activity
LastException = null;
if (sqlConnectionHandle.State != System.Data.ConnectionState.Open)
try
{
// if not open, always make sure we get updated connection string
// if ever changed by some other "unknown" condition...
sqlConnectionHandle.ConnectionString = GetConnectionString();
sqlConnectionHandle.Open();
}
catch (Exception ex)
{
// Preserve in generic sqlException" property for analysis OUTSIDE this function
LastException = ex;
}
// if NOT connected, display message to user and set error code and exception
if (sqlConnectionHandle.State != System.Data.ConnectionState.Open)
LastException = new Exception("Unable to open database connection.");
// return if it IS successful at opening the connection (or was already open)
return sqlConnectionHandle.State == System.Data.ConnectionState.Open;
}
// likewise disconnect could be common
public void SQLDisconnect()
{
if (sqlConnectionHandle != null)
if (sqlConnectionHandle.State == ConnectionState.Open)
sqlConnectionHandle.Close();
}
public bool SqlExecNonQuery( IDbCommand SQLCmd, DataTable oTbl)
{
// pre-clear exception
LastException = null;
// fill the table...
SQLConnect();
try
{
RowsAffectedByQuery = SQLCmd.ExecuteNonQuery();
}
catch (Exception e)
{
LastException = e;
throw e;
}
finally
{
SQLDisconnect();
}
// Its all ok if no exception error
return LastException == null;
}
}
// Now, build your connection manager per specific type
public class MyAccessConnection : MyConnection
{
public MyAccessConnection()
{ sqlConnectionHandle = new OleDbConnection(); }
public override string GetConnectionString()
{ return "Your Connection String from AppSettings.. any changes if OleDb vs SQL"; }
public override IDbCommand GetSQLDbCommand()
{ return new OleDbCommand( "", (OleDbConnection)sqlConnectionHandle ); }
public override IDbDataParameter AddDbParmSpecificValue(string ParmName, object UnknownValue)
{ return new OleDbParameter( ParmName, UnknownValue ); }
}
public class MySQLConnection : MyConnection
{
public MySQLConnection()
{ sqlConnectionHandle = new SqlConnection(); }
public override string GetConnectionString()
{ return "Your Connection String from AppSettings... any alterations needed??? "; }
public override IDbCommand GetSQLDbCommand()
{ return new SqlCommand ("", (SqlConnection)sqlConnectionHandle); }
public override IDbDataParameter AddDbParmSpecificValue(string ParmName, object UnknownValue)
{ return new SqlParameter(ParmName, UnknownValue); }
}
// Now to implement... pick one... Access or SQL-Server for derivation...
public class MyDataLayer : MyAccessConnection
{
public void SomeSQLCall()
{
IDbCommand sqlcmd = GetSQLDbCommand();
sqlcmd.CommandText = "UPDATE TileTypes SET Title = #Title, "
+ "Picture = #Picture, "
+ "Color = #Color "
+ "WHERE ID = #ID";
sqlcmd.Parameters.Add( AddDbParmSpecificValue( "#Title", titleTextBox.Text.Trim() ));
sqlcmd.Parameters.Add( AddDbParmSpecificValue( "#Picture", typePictureBox.ImageLocation) );
sqlcmd.Parameters.Add( AddDbParmSpecificValue( "#Color", colorButton.BackColor.ToArgb()) );
sqlcmd.Parameters.Add( AddDbParmSpecificValue( "#ID", id));
if( SqlExecNonQuery(sqlcmd))
// Good to go
DoSomethingWithTheData;
else
// Notify of whatever error thrown....
}
}
}
So.. as you can see, my last class specifically is derived from EITHER Access OR SQL. Then, I can create my methods to get data, call updates, whatever. Get a SQL Command (which returns proper type and automatically is attached to its corresponding "Connection Handle" object, prepare the text, add your parameters, execute it.
Related
I'm using Selenium + postgre integration in order to validate correct data on database. After an action on UI then I'm using Npgsql library in order to receive scalar value from cell and then use it for assertion.
Code looks line:
public static string GetCreditAmount(string orderId, string accountNumber)
{
string findCreditQuery = $#"SELECT ""Credit"" FROM accounting.""AccountLines"" al INNER JOIN accounting.""Accounts"" acc ON al.""Account_FK"" = acc.""Id""
WHERE
""OrderId"" = '{orderId}'
AND ""AccountNumber"" = '{accountNumber}'
ORDER BY al.""CreatedDateUtc"" DESC";
using (var connection = new NpgsqlConnection(Configuration.AdminConnectionString))
{
connection.Open();
var transaction = connection.BeginTransaction();
try
{
var account = connection.ExecuteScalar(findCreditQuery).ToString();
return account;
}
catch (NullReferenceException e)
{
Thread.Sleep(3000);
var account = connection.ExecuteScalar(findCreditQuery).ToString();
return account;
}
catch (Exception e)
{
transaction.Rollback();
throw e;
}
}
}
The problem which I'm receiving is NullReferenceException, however results are really random. In one test I'm firing & asserting few times where in like 85% it does not work (Null reference) and from time to time in like 15% it works (test passed). Strange case is that assertion which was fine in one run fails in another one.
I was trying to add static sleeps (Thread.Sleep) in order to give database few extra second for proceeding but all it still fails.
Is there any proper solution which can be used?
If you do this:
object o = null;
string s = o.ToString();
I believe you will get the same error. I believe the issue is your database object is null.
This will probably fix it:
object o = connection.ExecuteScalar(findCreditQuery);
string account = o == null ? null : o.ToString();
Alternatively, you can implement a DbDataReader and use the IsDbNull method. It will be more work but might also be more scalable if you plan to do more than what's currently being done:
string account;
using (NpgsqlCommand cmd = new NpgsqlCommand(findCreditQuery, connection))
{
using (NpgsqlDataReader reader = cmd.ExecuteReader())
{
reader.Read();
if (!reader.IsDBNull(0))
account = reader.GetString(0);
}
}
Whats the Perfered Method to handle Errors in a SQL Stored Procedure when Using Entity Framework.
I have a chunk of code that uses a database context and EF to call a stored procedure. If everything is perfect, the stored procedure returns a list of keys to tell me which records were modified. If something goes wrong in the stored procedure, the transaction rolls back and I believe the response type is a string and causes this error:
The data reader has more than one field. Multiple fields are not valid for EDM primitive or enumeration types.
After doing so digging I noticed that the SQL developer had put "PRINT: ERROR MESSAGE HERE" statement to pass back a message, However the current call does not allow for this,
Here is how the call is currently being made:
var partIdsCreated = context.Database.SqlQuery<int>("EXEC My_SP #FilePath, #FileName", args).ToList();
The Current Code is expecting a list of Int and if a string is Returned it Errors out with the Error Message listed above, So what is the best way to handle errors coming back from a Stored Procedure in a Transaction Using Entity Framework 6?
Below I have provided two similar ways to handle messages returned by SQL Server. As noted in the comments, both involve the usage of the InfoMessage event on the connection.
To answer your question, I don't really know of a preferred way to do handle PRINT statements as errors, but I am also not really sure of another way to handle the output.
Solution 1: Catch Exception
public class DataLoader_01
{
public int LoadData()
{
_infoMessage = null;
using (var context = new ApplicationDbContext(#"Server=localhost\SQLEXPRESS;Database=StackOverflow;Trusted_Connection=True;"))
{
var sqlConnection = (SqlConnection)context.Database.Connection;
sqlConnection.InfoMessage += InfoMessage;
try
{
var t = context.Database.SqlQuery<int>("EXEC ErrorMessageHandling", new object[] {});
return t.First();
}
catch (EntityCommandExecutionException e)
{
if (string.IsNullOrEmpty(_infoMessage))
{
//do some error handling specific to your application
throw new ApplicationSpecificException(_infoMessage);
}
throw;
}
}
}
private void InfoMessage(object sender, SqlInfoMessageEventArgs e)
{
_infoMessage = e.Message;
}
private string _infoMessage;
}
Solution 2: Check Field Count
public class DataLoader_02
{
public int LoadData()
{
_infoMessage = null;
using (var context = new ApplicationDbContext(#"Server=localhost\SQLEXPRESS;Database=StackOverflow;Trusted_Connection=True;"))
{
var sqlConnection = (SqlConnection)context.Database.Connection;
sqlConnection.InfoMessage += InfoMessage;
var cmd = context.Database.Connection.CreateCommand();
cmd.CommandText = "[dbo].[SomeProc]";
try
{
context.Database.Connection.Open();
var reader = cmd.ExecuteReader();
if (reader.FieldCount == 0)
{
throw new ApplicationSpecificException(_infoMessage);
}
var result = ((IObjectContextAdapter)context).ObjectContext.Translate<int>(reader);
return result.First();
}
finally
{
context.Database.Connection.Close();
}
}
}
private void InfoMessage(object sender, SqlInfoMessageEventArgs e)
{
_infoMessage = e.Message;
}
private string _infoMessage;
}
I'm receiving an error that says that the connection property has not been initialized (see https://imgur.com/CTHIabz). This is confusing, as it is the same code that is in other methods that work just fine. Here is the code:
public bool AddBirthday(string full_name, string month, int day, int year)
{
if (!string.IsNullOrWhiteSpace(full_name) && month.ToString() != "" && day.ToString() != "" && year.ToString() != "")
{
// all required fields were filled out
// determine which month
values = birthdayMonths.Where(v => v.Value == month).ToList();
using (OleDbConnection dbConn = new OleDbConnection(#"Provider=Microsoft.Jet.OLEDB.4.0;Data Source=|DataDirectory|\qbcdb.mdb"))
{
dbConn.Open();
using (cmd = new OleDbCommand("INSERT INTO birthdays (full_name, birthday_month, birthday_day, birthday_year) VALUES (#full_name, #birthday_month, #birthday_day, #birthday_year)"))
{
foreach (KeyValuePair<int, string> key in values)
{
if (key.Value != "")
{
try
{
cmd.Parameters.AddWithValue("#full_name", full_name);
cmd.Parameters.AddWithValue("#birthday_month", key.Value);
cmd.Parameters.AddWithValue("#birthday_day", day);
cmd.Parameters.AddWithValue("#birthday_year", year);
if (cmd.ExecuteNonQuery() > 0)
{
return true;
}
}
catch (Exception e)
{
MessageBox.Show(e.Message);
}
}
else
{
MessageBox.Show("No values were given.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
}
}
dbConn.Close();
}
}
return false;
}
I've gone over the code and checked to make sure the database was in the right location and everything else I could think of. I'm confused on why this work would on another method that uses the same code (almost) when it comes to the database query and binds it to a dvg.
Any help would be appreciated.
Thanks!
Oh btw, it inserts selected values from a listboxes, as demonstrated here:
insertBirthday.Click += (senders, argss) =>
{
if (btd.AddBirthday(name.Text, birthdays.SelectedItem.ToString(), Convert.ToInt32(dayListBox.SelectedItem.ToString()),
Convert.ToInt32(yearListBox.SelectedItem.ToString())))
{
MessageBox.Show("Birthday Added Successfully", "Success", MessageBoxButtons.OK);
}
};
Use this constructor for OleDbCommand: OleDbCommand Constructor (String, OleDbConnection)
You haven't assigned the connection to your OleDbCommand. There are two ways you could to this:
cmd.Connection = dbConn;
or in the constructor:
using (cmd = new OleDbCommand(sql, dbConn)) { ... }
It's also good practice not to open your connection until you are going to use it, i.e. right before cmd.ExecuteNonQuery(). Since you are using a using block, you also don't need to close the connection, that will be taken care of for you, but it's probably not hurting anything.
I recommend separating your database code from your UI code and validation logic (showing MessageBox and checking values) into a separate "repository" class. All of the data necessary to perform the operation should also be passed in as a parameter to your database code so that you don't have any confusing issues where those values aren't being set somewhere else. Calculate the values to insert before calling the method and then pass those as parameters.
I'm just learning asp.net/C# and am building my first application.
In my application, I am rendering data from sql on almost every view.
My controllers are getting large, because every time I make a request, I'm using somthing like:
try
{
sqlConnection = new SqlConnection(dbConnectionString);
SqlCommand command = new SqlCommand("sp_Test", sqlConnection);
command.CommandType = CommandType.StoredProcedure;
sqlConnection.Open();
return command.ExecuteNonQuery();
sqlConnection.Close();
}
catch (SqlException ex)
{
Console.WriteLine("SQL Error" + ex.Message.ToString());
return 0;
}
Is there a way to turn the sql into a simple using block?
Maybe something like:
using(myConnection){
SqlCommand command = new SqlCommand("sp_Test", sqlConnection);
command.CommandType = CommandType.StoredProcedure;
}
There are many better approaches do it. You can create a SqlHelper class that can be used to execute stored procedures and SQL queries and also return DataReader and DataTable/DataSets.
public class SqlHelper
{
public SqlHelper(string connString)
{
}
public DataSet GetDatasetByCommand(string Command);
public SqlDataReader GetReaderBySQL(string strSQL);
public SqlDataReader GetReaderByCmd(string Command);
public SqlConnection GetSqlConnection();
public void CloseConnection();
}
You can see one such sample here:
http://www.nullskull.com/a/1295/sql-helper-class-in-c.aspx
If you want more advanced approach you can go for Enterprise Library Data Access Block
http://msdn.microsoft.com/en-us/magazine/cc163766.aspx
The best thing to do is refactor that statement into a seperate method. It looks like the only thing that could vary is the name of the procedure.
So create an object with two properties, a boolean success and an error message.
Call the function and pass in the name of the sql command. Your function should run your repeated code in the try block based on the given procedure name, then return an object with true/false and an error message if the call failed. This should make your controllers much smaller. Example code for the controller:
var result = MyNewMethod("sp_Test");
if(!result.Success)
{
Console.WriteLine(result.ErrorMessage);
return 0;
}
Given a method
public static bool Connection.TryCreate(out Connection connection) {}
And a piece of calling code:
Connection connection;
if (!Connection.TryCreate(out connection))
// handle failure gracefully.
/*
* work with connection
*
* …
*
*/
connection.Dispose();
I'm using the same pattern as bool.TryParse and friends, i.e. TryCreate returns whether the operation was successful.
I realize the using() variable needs to be read-only within its block, but is there a way to turn the above into a using() {} block (TryCreate only sets it once), like so:
using (Connection connection)
{
if (!Connection.TryCreate(out connection))
// this would leave the using() block prematurely
/*
* work with sconnection
*
* …
*
*/
}
(This doesn't compile:
error CS1657: Cannot pass 'connection' as a ref or out argument because it is a 'using variable'
)
No, that is not possible.
The using (x) {...} construct makes a copy of x when it enters the block, so you can do this:
var x = new FileStream(...);
using (x)
{
x = null;
}
The stream will still be disposed when the using block ends.
The corollary is that this won't work either:
Stream x = null;
using (x)
{
x = new FileStream(...);
}
here the stream you construct inside the using block will not be disposed.
What you can do, however, is this:
Connection connection;
if (Connection.TryCreate(out connection))
using (connection)
{
}
In C# 7.0 and onwards you can combine this with "out variables" to form:
if (Connection.TryCreate(out var connection))
using (connection)
{
}
Looks like a bad use of the Try* pattern (some would argue this is an anti-pattern).
Instead of a TryCreate, just have a Create method that throws an exception if not successful and that returns the created connection.
Then you could do the usual:
using(Connection connection = Connection.Create())
{
}
Alternatively, if you want to avoid an exception being thrown and the required try{}catch{}, have the Create method return null when a connection could not be created and test for that.
You can do it like this:
Connection connection;
if (Connection.TryCreate(out connection))
{
using (connection)
{
…
}
}
But it might be better if you just returned null on failure:
using (Connection connection = Connection.Create())
{
if (connection != null)
{
…
}
}
The finally block that is created by the using checks whether connection is null and doesn't do anything if it is.
Also, if you're not declaring the variable in the using, then it doesn't have to be read-only.
No. If you are concerned about exceptions in the gap between a method call and the using, you could use try/finally:
Connection conn = null;
try {
if(!conn.TryCreate(out conn)) return;
...
} finally {
if(conn != null) conn.Dispose();
}
A step sideways ?
public class ConnectTo : IDisposable
{
public Connection CurrentConnection {get; private set;}
public ConnectTo()
{
CurrentConnection = null;
// Connect up to whatever.
}
#region IDisposable
// Blah blah
#endregion
}
then
using( ConnectedTo conn = new ConnectTo())
{
if (conn.CurrentConnection != null)
{
//Do Stuff
}
}