I try to insert hundreds of records into empty database table using TableDirect type of SqlCeCommand. The problem is I get an exception SqlCeException "Unspecified error" when calling SqlCeResultSet::Insert. Below is my code. Any hints?
Thanks
public bool StoreEventsDB2(List<DAO.Event> events)
{
try
{
SqlCeCommand command = new SqlCeCommand("Event");
command.CommandType = System.Data.CommandType.TableDirect;
SqlCeResultSet rs = _databaseManager.ExecuteResultSet(command, ResultSetOptions.Updatable | ResultSetOptions.Scrollable );
foreach (DAO.Event theEvent in events)
{
SqlCeUpdatableRecord record = rs.CreateRecord();
record.SetInt32( 0, theEvent.ID );
record.SetInt32( 1, theEvent.ParentID);
record.SetString(2, theEvent.Name);
record.SetDateTime(3, theEvent.DateTime);
record.SetDateTime(4, theEvent.LastSynced);
record.SetInt32(5, theEvent.LastSyncedTS);
record.SetString(6, theEvent.VenueName);
record.SetBoolean(7, theEvent.IsParentEvent);
record.SetDateTime(11, DateTime.Now);
rs.Insert(record);
}
}
catch (SqlCeException e)
{
Log.Logger.GetLogger().Log(Log.Logger.LogLevel.ERROR, "[EventManager::StoreEventsDB] error: {0}", e.Message);
return false;
}
catch (Exception e)
{
Log.Logger.GetLogger().Log(Log.Logger.LogLevel.ERROR, "[EventManager::StoreEventsDB] error: {0}", e.Message);
return false;
}
return true;
}
I am unsure how your connection is managed with the database manager which could be the culprit - make sure you are using one connection (sqlce doesn't play nice). Also the results set option "ResultSetOption.Scrollable" is not needed (at least I have never used it for an insert).
Below is the syntax I use when doing direct table inserts. Every database/data access object is wrapped in a using statement to dispose of objects after use - this is very important especially with the compact framework and sqlce as the garbage collection is less than ideal (you WILL get out of memory exceptions!). I have added a transaction to your code also so that the option is all or nothing.
Hope this helps:
using (var transaction = connection.BeginTransaction())
{
using (var command = connection.CreateCommand())
{
command.Transaction = transaction;
command.CommandType = CommandType.TableDirect;
command.CommandText = "Event";
using (var rs = command.ExecuteResultSet(ResultSetOptions.Updatable))
{
var record = rs.CreateRecord();
foreach (DAO.Event theEvent in events)
{
record.SetInt32(0, theEvent.ID);
record.SetInt32(1, theEvent.ParentID);
record.SetString(2, theEvent.Name);
record.SetDateTime(3, theEvent.DateTime);
record.SetDateTime(4, theEvent.LastSynced);
record.SetInt32(5, theEvent.LastSyncedTS);
record.SetString(6, theEvent.VenueName);
record.SetBoolean(7, theEvent.IsParentEvent);
record.SetDateTime(11, DateTime.Now);
rs.Insert(record);
}
}
transaction.Commit();
}
}
Related
I have a BO (Country) with a child BO (State) which also has a child BO (City). When I update the parent BO (Country), add a child State and run save, when an exception occurs in the DAL (on purpose), the transaction is not rolled back. I am using SqlCE. I am attaching a sample stripped down project that demonstrates the issue. What am I doing wrong?
Test code:
Country originalCountry = null;
try
{
originalCountry = Country.GetCountry(1);
var country = Country.GetCountry(1);
country.CountryName = "My new name";
var state = country.States.AddNew();
state.StateName = "Dummy state";
country.States.EndNew(country.States.IndexOf(state));
country.Save();
}
catch (Exception exception)
{
var country = Country.GetCountry(1);
if (originalCountry.CountryName != country.CountryName)
{
System.Console.WriteLine("Values ARE NOT the same: " + originalCountry.CountryName + " vs. " + country.CountryName);
}
else
{
System.Console.WriteLine("Values are the same: " + originalCountry.CountryName + " vs. " + country.CountryName);
}
}
Country.cs
[Transactional(TransactionalTypes.TransactionScope)]
protected override void DataPortal_Update()
{
Update();
}
private void Update()
{
using (var ctx = DalFactory.GetManager())
{
var dal = ctx.GetProvider<ICountryDal>();
using (BypassPropertyChecks)
{
var dto = new CountryDto();
TransferToDto(dto);
dal.Update(dto);
}
FieldManager.UpdateChildren(this);
throw new Exception("Rollback should occur.");
}
}
Sample project
From my understanding of SQL CE and transactions, they only support a transaction on a single database connection when using TransactionScope.
It looks like your code is following the model put forward by some of the CSLA samples, but the actual opening/closing of the database connection is hidden in the GetManager or GetProvider abstraction, so there's no way to say for sure how that's handled.
It does seem that SQL CE has some limitations on transactions with TransactionScope though, so you should make sure you aren't violating one of their restrictions somehow.
The DalManager (and the ConnectionManager) relies on reference counting to determine when close the actual connection.
The rules are not making sure to dispose the DalManager and hence the DalManager and reference counting is off. Resulting in the update happening on a connection that was created and opened in one of the Fetch operations and is therefore not be enlisted in the TransactionScope on the Update method.
See: http://msdn.microsoft.com/en-us/library/bb896149%28v=sql.100%29.aspx
All rules must be changed to dispose of the DalManager. Original rule:
protected override void Execute(RuleContext context)
{
var name = (string)context.InputPropertyValues[_nameProperty];
var id = (int)context.InputPropertyValues[_idProperty];
var dal = DalFactory.GetManager();
var countryDal = dal.GetProvider<ICountryDal>();
var exists = countryDal.Exists(id, name);
if (exists)
{
context.AddErrorResult("Country with the same name already exists in the database.");
}
}
DalManager is IDisposable but is not explicitly disposed here so it depends on when the GC will actually collect the object.
Should be:
protected override void Execute(RuleContext context)
{
var name = (string)context.InputPropertyValues[_nameProperty];
var id = (int)context.InputPropertyValues[_idProperty];
using (var dal = DalFactory.GetManager())
{
var countryDal = dal.GetProvider<ICountryDal>();
var exists = countryDal.Exists(id, name);
if (exists)
{
context.AddErrorResult("Country with the same name already exists in the database.");
}
}
}
I am trying to access external sql-tables via the following code. Accessing works as intended, but I have problems to process the values, if datatype has to be casted for futher processsing.
The following code can be executed as job in ax:
static void Job1(Args _args)
{
str serverName;
str catalogName;
str ConnectionString;
str sqlQuery;
System.Data.SqlClient.SqlConnectionStringBuilder connectionStringBuilder;
System.Data.SqlClient.SqlConnection connection;
System.Data.SqlClient.SqlCommand command;
System.Data.SqlClient.SqlParameterCollection parameterCollection;
System.Data.SqlClient.SqlDataReader dataReader;
;
new InteropPermission( InteropKind::ClrInterop ).assert();
sqlQuery = "SELECT TOP 10 * FROM PRODROUTE";
serverName = SysSQLSystemInfo::construct().getLoginServer();
catalogName = SysSQLSystemInfo::construct().getloginDatabase();
connectionStringBuilder = new System.Data.SqlClient.SqlConnectionStringBuilder();
connectionStringBuilder.set_DataSource(serverName);
connectionStringBuilder.set_IntegratedSecurity(true);
connectionStringBuilder.set_InitialCatalog(catalogName);
ConnectionString = connectionStringBuilder.get_ConnectionString();
connection = new System.Data.SqlClient.SqlConnection(ConnectionString);
command = new System.Data.SqlClient.SqlCommand(sqlQuery);
command.set_Connection(connection);
try
{
connection.Open();
try
{
dataReader = command.ExecuteReader();
while(dataReader.Read())
{
//info( dataReader.get_Item( "PRODID" )); // ok
//info( dataReader.get_Item( "LEVEL" )); // not working
info ( int2str( dataReader.GetInt32( 23 ))); // not working
//info( any2str(dataReader.get_Item( "LEVEL" ))); // not working
}
dataReader.Dispose();
}
catch
{
dataReader.Dispose();
}
catch(Exception::CLRError)
{
dataReader.Dispose();
}
connection.Dispose();
}
catch
{
connection.Dispose();
}
catch(Exception::CLRError)
{
connection.Dispose();
}
command.Dispose();
CodeAccessPermission::revertAssert();
}
There are four codelines to access data:
//info( dataReader.get_Item( "PRODID" )); // ok
//info( dataReader.get_Item( "LEVEL" )); // not working
info ( int2str( dataReader.GetInt32( 23 ))); // not working
//info( any2str(dataReader.get_Item( "LEVEL" ))); // not working
As you see, only one line won't throw an error, since datatype of field fits to the desired operation. Info-logging is just an example. If I try to assign data to ax-table-fields, the same problem occurs.
Casting via int2str(), any2str() and so on doesn't work as well.
So what's the right way to handle data read, for further processing?
#edit: ErrorMsg
Fehler während der Verarbeitung: Elementtyp für Variablenzuweisung ungültig.
I just tried running your query directly in SSMS, and found that the field LEVEL didn't exist on the actual table on sql server, but there is a field named "LEVEL_" (which is the 3rd field on my table).
Additionally, this talks about any2str not doing what you would expect it to do, but I don't think that is your problem on retrieving the LEVEL field.
http://abraaxapta.blogspot.com/2012/02/kernel-function-madness-any2str.html
I am trying to execute a stored procedure in asp.net. The stored procedure requires 3 parameters, all 3 are ID's(ints). The 3 parameters are : TaskID, ExhibitID, and InvestigatorID.
I have a hidden field that contains an array of ExhibitID's that came from a javascript function.
My question is how do I get the query to execute as I am looping through the array?
Here is an example of my stored procedure:
var cnSaveTask = new SqlConnection(ConfigurationManager.ConnectionStrings["OSCIDConnectionString"].ToString());
var comLinkExhibitToTask = new SqlCommand("p_CaseFileTasksExhibitLinkAdd", cnSaveTask) { CommandType = CommandType.StoredProcedure };
foreach (string exhibit in hidExhibitsIDs.Value.Split(','))
{
comLinkExhibitToTask.Parameters.AddWithValue("#TaskID", taskID);
comLinkExhibitToTask.Parameters.AddWithValue("#ExhibitID", Convert.ToInt32(exhibit));
comLinkExhibitToTask.Parameters.AddWithValue("#InvestigatorID", int.Parse(Session["InvestigatorID"].ToString()));
}
try
{
cnSaveTask.Open();
comLinkExhibitToTask.ExecuteNonQuery();
}
It is not working in my DB though. Nothing gets added. My guess is that since it is iterating and not executing, it just keeps replacing the "exhibitID" everytime then eventually tries to execute it. But I don't think just adding "comLinkExhibitToTask.ExecuteNonQuery()"
outside the try is a good idea. Any suggestions?
you can either move the try block into the foreach loop or wrap the foreach loop with a try block. (depending on what error handling you wish - continue with the next exhibit on error or completely abort execution)
I've never used AddWithValue, so I can't speak to its functionality. Here's how I typically write a DB call like this.
using (SqlConnection cnSaveTask = new SqlConnection(ConfigurationManager.ConnectionStrings["OSCIDConnectionString"].ConnectionString))
{
cnSaveTask.Open();
using (SqlCommand comLinkExhibitToTask = new SqlCommand("p_CaseFileTasksExhibitLinkAdd", cnSaveTask))
{
comLinkExhibitToTask.CommandType = CommandType.StoredProcedure;
comLinkExhibitToTask.Parameters.Add(new SqlParameter("#TaskID", SqlDbType.Int) {Value = taskID});
// etc.
comLinkExhibitToTask.ExecuteNonQuery();
}
}
The solution:
var cnSaveTask = new SqlConnection(ConfigurationManager.ConnectionStrings["OSCIDConnectionString"].ToString());
try
{
var comLinkExhibitToTask = new SqlCommand("p_CaseFileTasksExhibitLinkAdd", cnSaveTask) { CommandType = CommandType.StoredProcedure };
cnSaveTask.Open();
comLinkExhibitToTask.Parameters.Add(new SqlParameter("#TaskID", SqlDbType.Int));
comLinkExhibitToTask.Parameters.Add(new SqlParameter("#ExhibitID", SqlDbType.Int));
comLinkExhibitToTask.Parameters.Add(new SqlParameter("#InvestigatorID", SqlDbType.Int));
foreach (string exhibit in hidExhibitsIDs.Value.Split(','))
{
comLinkExhibitToTask.Parameters["#TaskID"].Value = taskID;
comLinkExhibitToTask.Parameters["#ExhibitID"].Value = Convert.ToInt32(exhibit);
comLinkExhibitToTask.Parameters["#InvestigatorID"].Value = int.Parse(Session["InvestigatorID"].ToString());
comLinkExhibitToTask.ExecuteNonQuery();
}
}
catch (Exception ex)
{
ErrorLogger.Log(0, ex.Source, ex.Message);
}
finally
{
if (cnSaveTask.State == ConnectionState.Open)
{
cnSaveTask.Close();
}
}
Since I was in a loop it kept adding parameters. So just declare the parameters outside the loop, and only pass the values in the loop. That way there are only 3 parameters, and the values will be passed in accordingly
I have some strange behavior occurring in an ASP.NET application that I am trying to fix.
I am getting the following error from code:
Timeout expired. The timeout period elapsed prior to completion of the operation or the server is not responding.
I am familiar with this error and many of it's causes. All of my typical avenues of trouble shooting have failed.
Here are some of the dynamics:
The server was recently re-built. So it could be a server configuration issue.
This error only happens on a specific web server. When I run the application it locally, and from other servers it is fast. I cannot re-pro perf issues with the proc in MSSMS from my machine.
This tells me that it is specific to this server.
When I run The same proc using the OSql command--line utility It works. Fast.
This indicates that it is likely something .NET related, NOT db related
Prior to this code executing, I have executed other Stored procedures on this server, and on this same DB.
This suggests the proc may be related , but is not a "server X cannot talk to server Y"
I have gotten confirmation From DB owners that the DB has received the command, executed (>1second )it and returned the data.
By tracing the code, I see the results are returned and it is not until I try to close the data reader that the error occurs.
In fact I see it takes 36 milliseconds to execute the stored procedure and iterate through the result.
It looks like the call to DataReader.Close is what is taking time, and eventually timing out.
I have increased the max Pool from 31 to 100.
Here is a sample of what my code looks like, how it is structured. I have been hacking at it for trouble shooting: I have added the explict close to ensure I know where the error occurs. There may be syntax issues: I have made it generic, and may have introduced bugs in doing so.
public double GetMyData()
{
double returnValue;
// Used in logging to see if code was reached & how long it took.
System.Diagnostics.Stopwatch s = new System.Diagnostics.Stopwatch();
using (SqlConnection cn = Connections.GetSqlConnection())
{
cn.Open();
using (SqlCommand cmd = getSQLCommmand("SomeProcName"))
{
Log.Log.WriteTrace(string.Format("Execute {0} ","SomeProcName"),0);
s.Start();
SqlDataReader dr= null;
try
{
dr = cmd.ExecuteReader();
s.Stop();
Log.Log.WriteTrace("Timer", "ExecuteReader done " + s.ElapsedMilliseconds + "ms ", 0);
s.Start();
if (dr != null)
{
if (dr.Read())
{
returnValue =
Conversion.DBNullToDouble(
dr[0]);
}
s.Stop();
Log.Log.WriteTrace("Timer", "dr.read done (result:" + returnValue + ")" + s.ElapsedMilliseconds + "ms ", 0); // I get Here
}
}catch(Exception ex)
{
Log.Log.PersistException(ex);
}
//}
if(dr!=null && !dr.IsClosed)
dr.Close();// This times out
if (cn != null && cn.State !=ConnectionState.Closed)
cn.Close();
Log.Log.WriteTrace("DONE "),
;
}
}
return (returnValue);
}
UPDATE
dr.Close(); takes 2 minutes to execute. Just on this server. Locally it takes less than a second.
UPDATE
Per the accepted answer's comments: I have a proc that has multiple records. I am taking the fist one. Calling cmd.Cancel() has not fixed, but has drastically reduced the time taken to close the data reader. Exploring this should help me fix the problem. I do not know why this is only happening on this server, as the server is a dev server.
I see few problems in your code.
Try to use the Reader in a using statement it closes and disposes when finishing it.
You are closing 2 times the connection (when using
if (cn != null && cn.State !=ConnectionState.Closed)
cn.Close();
and in the using (SqlConnection cn = Connections.GetSqlConnection()) -- this one do it for you at the end of the statement
In your code you are not checking if the DataReader.HasRows So if your Sproc doesn't return a value it will throw an exception in the if (dr.Read()) so that could be the reason that's why you are getting sometimes the time out exception
If you only want to retrieve the value of the first column of the first row you should take a look to ExecuteScalar
Finally
I would rewrite your code like this (using DataReader)
public double GetMyData()
{
double returnValue;
// Used in logging to see if code was reached & how long it took.
System.Diagnostics.Stopwatch s = new System.Diagnostics.Stopwatch();
using (SqlConnection cn = Connections.GetSqlConnection())
{
cn.Open();
using (SqlCommand cmd = getSQLCommmand("SomeProcName"))
{
Log.Log.WriteTrace(string.Format("Execute {0} ","SomeProcName"),0);
s.Start();
using(SqlDataReader dr = cmd.ExecuteReader())
{
s.Stop();
Log.Log.WriteTrace("Timer", "ExecuteReader done " + s.ElapsedMilliseconds + "ms ", 0);
s.Start();
if (dr != null)
{
if(dr.HasRows)
{
if (dr.Read())
{
returnValue =
Conversion.DBNullToDouble(
dr[0]);
}
}
s.Stop();
Log.Log.WriteTrace("Timer", "dr.read done (result:" + returnValue + ")" + s.ElapsedMilliseconds + "ms ", 0); // I get Here
}
}
}
}
return (returnValue);
}
Or (with ExecuteScalar)
public double GetMyData()
{
double returnValue;
// Used in logging to see if code was reached & how long it took.
System.Diagnostics.Stopwatch s = new System.Diagnostics.Stopwatch();
using (SqlConnection cn = Connections.GetSqlConnection())
{
cn.Open();
using (SqlCommand cmd = getSQLCommmand("SomeProcName"))
{
Log.Log.WriteTrace(string.Format("Execute {0} ","SomeProcName"),0);
s.Start();
try
{
returnValue = Conversion.DBNullToDouble(cmd.ExecuteScalar());
s.Stop();
}
catch(Exception ex)
{
Log.Log.PersistException(ex);
}
}
}
return (returnValue);
}
I have a web page that needs to update multiple records. This page gets all the information and then begins a transaction sending multiple UPDATE queries to the data base.
foreach row
{
Prepare the query
Hashtable Item = new Hashtable();
Item.Add("Id", Id);
Item.Add("Field1", Field1);
Item.Add("Field2", Field2);
Item.Add("Field3", Field3);
...
}
Then we launch the ytransaction
DO CHANGES()
public void execute_NonQuery_procedure_transaction(string StoredProcedure, List<Hashtable> Params)
{
using (MySqlConnection oConnection = new MySqlConnection(ConfigurationManager.AppSettings[DB]))
{
MySqlTransaction oTransaction;
bool HasErrors = false;
oConnection.Open();
oTransaction = oConnection.BeginTransaction();
try
{
MySqlCommand oCommand = new MySqlCommand(StoredProcedure, oConnection);
oCommand.CommandType = CommandType.StoredProcedure;
oCommand.Transaction = oTransaction;
foreach (Hashtable hParams in Params)
{
oCommand.Parameters.Clear();
IDictionaryEnumerator en = hParams.GetEnumerator();
while (en.MoveNext())
{
oCommand.Parameters.AddWithValue("_" + en.Key.ToString(), en.Value);
oCommand.Parameters["_" + en.Key.ToString()].Direction = ParameterDirection.Input;
}
oCommand.ExecuteNonQuery();
}
}
catch (Exception e)
{
HasErrors = true;
throw e;
}
finally
{
if (HasErrors)
oTransaction.Rollback();
else
oTransaction.Commit();
oConnection.Close();
}
}
}
Is there another way to do this or this is the most efficient way?
It depends on the situation, like if you have multiple row updates or adding new rows or deleting some rows or a combination of these, which modifies the database table then, the efficient way to do this is to have Batch Update...
Please go through this link Batch Update
Hope this helps...
it looks fine to me, you could eventually do not clear the Command.Parameters list but just assign the values on following iterations but probably this leads to no visible improvements.
pay attention your throw is wrong, in C# don't use throw e; but simply throw;.