can sombody explain diffrence between SqlConnectionOwnership.Internal vs SqlConnectionOwnership.External - c#

I am new to ADO.Net.
I have this code ?
SqlDataReader dataReader = null;
var mustCloseConnection = false;
var cmd = new SqlCommand();
try
{
PrepareCommand(cmd, connection, transaction, commandType, commandText, commandParameters, out mustCloseConnection);
if(connectionOwnership ==SqlConnectionOwnership.Internal)
{
dataReader = cmd.ExecuteReader();
}
else
{
dataReader = cmd.ExecuteReader(CommandBehavior.CloseConnection);
}
var canClear = true;
foreach (SqlParameter commandParameter in cmd.Parameters)
{
if (commandParameter.Direction != ParameterDirection.Input)
canClear = false;
}
if (canClear)
{
cmd.Parameters.Clear();
}
return dataReader;
}
catch
{
if (mustCloseConnection)
connection.Close();
throw;
}
Can somebody explain ?
if(connectionOwnership ==SqlConnectionOwnership.Internal)
{
dataReader = cmd.ExecuteReader();
}
else
{
dataReader = cmd.ExecuteReader(CommandBehavior.CloseConnection);
}
I know SqlConnectionOwnership for pooling.
Any luck ?

That type does not appear to be part of .NET itself. Based on this page, people are creating their own helper libraries for ADO.NET code and the corresponding parameter is used to indicate whether the connection object was passed in by the calling code or created within the helper. This is the first example from that page:
/// <summary>
/// Create and prepare a SqlCommand, and call ExecuteReader with the appropriate CommandBehavior.
/// </summary>
/// <remarks>
/// If we created and opened the connection, we want the connection to be closed when the DataReader is closed.
///
/// If the caller provided the connection, we want to leave it to them to manage.
/// </remarks>
/// <param name="connection">a valid SqlConnection, on which to execute this command</param>
/// <param name="transaction">a valid SqlTransaction, or 'null'</param>
/// <param name="commandType">the CommandType (stored procedure, text, etc.)</param>
/// <param name="commandText">the stored procedure name or T-SQL command</param>
/// <param name="commandParameters">an array of SqlParameters to be associated with the command or 'null' if no parameters are required</param>
/// <param name="connectionOwnership">indicates whether the connection parameter was provided by the caller, or created by SqlHelper</param>
/// <returns>SqlDataReader containing the results of the command</returns>
private static SqlDataReader ExecuteReader(SqlConnection connection, SqlTransaction transaction, CommandType commandType, string commandText, SqlParameter[] commandParameters, SqlConnectionOwnership connectionOwnership)
{
//create a command and prepare it for execution
SqlCommand cmd = new SqlCommand();
PrepareCommand(cmd, connection, transaction, commandType, commandText, commandParameters);
//create a reader
SqlDataReader dr;
// call ExecuteReader with the appropriate CommandBehavior
if (connectionOwnership == SqlConnectionOwnership.External)
{
dr = cmd.ExecuteReader();
}
else
{
dr = cmd.ExecuteReader(CommandBehavior.CloseConnection);
}
// detach the SqlParameters from the command object, so they can be used again.
cmd.Parameters.Clear();
return dr;
}
Note the method documentation for the relevant parameter:
indicates whether the connection parameter was provided by the caller, or created by SqlHelper
Presumably External means passed in by the calling code, so it is left up to that calling code to decide when to close the connection, which may be being used for other things. Internal would mean created by the helper, so the connection's only purpose is to be used for that data reader, so it is closed when the reader is closed.
Note that I have never heard of this type before and I found this information simply by searching the web.

Related

SQL User with sysadmin role can't login to database

I have a domain user account that I use to run a particular ASP.NET web application it is NETOPS\websvr. This domain account is setup in the SQL Server in question and is in the sysadmin role. However, I randomly get errors stating that the login failed for he user. Sometimes it is on this database and others it is on a different database on the same server.
I have been searching the web for the past several hours trying to find an answer for this and I am no closer to an answer than when I started so I thought I would ask here to see if anyone has any ideas. Below is a output from my instance of Stackexchange.Exceptional
Exceptions Log: WebSync
Cannot open database "alsmi" requested by the login. The login failed. Login failed for user 'NETOPS\websvr'.
System.Data.SqlClient.SqlException
System.Data.SqlClient.SqlException (0x80131904): Cannot open database "alsmi" requested by the login. The login failed.
Login failed for user 'NETOPS\websvr'.
at System.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction)
at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj, Boolean callerHasConnectionLock, Boolean asyncClose)
at System.Data.SqlClient.TdsParser.TryRun(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj, Boolean& dataReady)
at System.Data.SqlClient.TdsParser.Run(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj)
at System.Data.SqlClient.SqlInternalConnectionTds.CompleteLogin(Boolean enlistOK)
at System.Data.SqlClient.SqlInternalConnectionTds.AttemptOneLogin(ServerInfo serverInfo, String newPassword, SecureString newSecurePassword, Boolean ignoreSniOpenTimeout, TimeoutTimer timeout, Boolean withFailover)
at System.Data.SqlClient.SqlInternalConnectionTds.LoginNoFailover(ServerInfo serverInfo, String newPassword, SecureString newSecurePassword, Boolean redirectedUserInstance, SqlConnectionString connectionOptions, SqlCredential credential, TimeoutTimer timeout)
at System.Data.SqlClient.SqlInternalConnectionTds.OpenLoginEnlist(TimeoutTimer timeout, SqlConnectionString connectionOptions, SqlCredential credential, String newPassword, SecureString newSecurePassword, Boolean redirectedUserInstance)
at System.Data.SqlClient.SqlInternalConnectionTds..ctor(DbConnectionPoolIdentity identity, SqlConnectionString connectionOptions, SqlCredential credential, Object providerInfo, String newPassword, SecureString newSecurePassword, Boolean redirectedUserInstance, SqlConnectionString userConnectionOptions, SessionData reconnectSessionData)
at System.Data.SqlClient.SqlConnectionFactory.CreateConnection(DbConnectionOptions options, DbConnectionPoolKey poolKey, Object poolGroupProviderInfo, DbConnectionPool pool, DbConnection owningConnection, DbConnectionOptions userOptions)
at System.Data.ProviderBase.DbConnectionFactory.CreatePooledConnection(DbConnectionPool pool, DbConnection owningObject, DbConnectionOptions options, DbConnectionPoolKey poolKey, DbConnectionOptions userOptions)
at System.Data.ProviderBase.DbConnectionPool.CreateObject(DbConnection owningObject, DbConnectionOptions userOptions, DbConnectionInternal oldConnection)
at System.Data.ProviderBase.DbConnectionPool.UserCreateRequest(DbConnection owningObject, DbConnectionOptions userOptions, DbConnectionInternal oldConnection)
at System.Data.ProviderBase.DbConnectionPool.TryGetConnection(DbConnection owningObject, UInt32 waitForMultipleObjectsTimeout, Boolean allowCreate, Boolean onlyOneCheckConnection, DbConnectionOptions userOptions, DbConnectionInternal& connection)
at System.Data.ProviderBase.DbConnectionPool.TryGetConnection(DbConnection owningObject, TaskCompletionSource`1 retry, DbConnectionOptions userOptions, DbConnectionInternal& connection)
at System.Data.ProviderBase.DbConnectionFactory.TryGetConnection(DbConnection owningConnection, TaskCompletionSource`1 retry, DbConnectionOptions userOptions, DbConnectionInternal oldConnection, DbConnectionInternal& connection)
at System.Data.ProviderBase.DbConnectionInternal.TryOpenConnectionInternal(DbConnection outerConnection, DbConnectionFactory connectionFactory, TaskCompletionSource`1 retry, DbConnectionOptions userOptions)
at System.Data.ProviderBase.DbConnectionClosed.TryOpenConnection(DbConnection outerConnection, DbConnectionFactory connectionFactory, TaskCompletionSource`1 retry, DbConnectionOptions userOptions)
at System.Data.SqlClient.SqlConnection.TryOpenInner(TaskCompletionSource`1 retry)
at System.Data.SqlClient.SqlConnection.TryOpen(TaskCompletionSource`1 retry)
at System.Data.SqlClient.SqlConnection.Open()
at Microsoft.ApplicationBlocks.Data.SqlHelper.ExecuteScalar(String connectionString, CommandType commandType, String commandText, SqlParameter[] commandParameters)
at Microsoft.ApplicationBlocks.Data.SqlHelper.ExecuteScalar(String connectionString, CommandType commandType, String commandText)
at GetPatchLevel.ProcessRequest(HttpContext context)
ClientConnectionId:e33983c0-da22-4050-a894-55274b46f645
Full Trace:
ProcessRequest at offset 424 in file:line:column :0:0
System.Web.HttpApplication.IExecutionStep.Execute at offset 342 in file:line:column :0:0
ExecuteStep at offset 70 in file:line:column :0:0
ResumeSteps at offset 1085 in file:line:column :0:0
BeginProcessRequestNotification at offset 96 in file:line:column :0:0
ProcessRequestNotificationPrivate at offset 187 in file:line:column :0:0
ProcessRequestNotificationHelper at offset 727 in file:line:column :0:0
ProcessRequestNotification at offset 31 in file:line:column :0:0
MgdIndicateCompletion at offset 0 in file:line:column :0:0
ProcessRequestNotificationHelper at offset 1110 in file:line:column :0:0
ProcessRequestNotification at offset 31 in file:line:column :0:0
occurred 2 hours ago on web1 (delete)
Server Variables
CONTENT_LENGTH 0
HTTP_X_FORWARDED_FOR 0.0.0.0
HTTP_X_SSL_CIPHER AES128-SHA SSLv3 Kx=RSA Au=RSA Enc=AES(128) Mac=SHA
LOCAL_ADDR 192.168.1.117
REMOTE_ADDR 192.168.1.216
REMOTE_HOST 192.168.1.216
REQUEST_METHOD GET
URL /Services/GetPatchLevel.ashx
URL and Query http://website.net/Services/GetPatchLevel.ashx?dbname=alsmi&username=0000&pwd=0000&clientid=0000
Custom
SQL-Server sqlserver1
SQL-ErrorNumber 4060
SQL-LineNumber 65536
QueryString
clientid 0000
dbname alsmi
pwd 0000
username 0000
RequestHeaders
Host website.net
X-Forwarded-For 0.0.0.0
X-SSL-cipher AES128-SHA SSLv3 Kx=RSA Au=RSA Enc=AES(128) Mac=SHA
Exceptional 1.0.0.111
SQL Error Store
Server time is 1/30/2015 5:50:04 PM
Here is the code that generates the connection string for the database.
public static string GetConnString(string DBName)
{
string retval = "";
WebSyncDatabase db = WebSyncDatabase.GetDatabase(DBName);
SqlConnectionStringBuilder csb = new SqlConnectionStringBuilder();
csb.DataSource = db.DBServer;
csb.InitialCatalog = db.DBName;
csb.IntegratedSecurity = true;
csb.ConnectTimeout = 25;
csb.MaxPoolSize = 150;
retval = csb.ConnectionString;
return retval;
}
Here is the data access code. The SqlHelper.ExecuteScalar is from Microsoft Data Access Application Block for .NET code unmodified. he below code literally works tens of millions of times per day for us with random exceptions.
string connString = WebSyncUtils.GetConnString(dbName);
switch (context.Request.HttpMethod.ToUpper())
{
case "GET":
int retval = 0;
try
{
retval = (int)SqlHelper.ExecuteScalar(connString, CommandType.Text, "SELECT PatchLevel FROM PatchStatus");
}
catch (Exception ex)
{
WebSyncUtils.LogMessage(string.Format("Get Next Patch Level for {0} {1} {2}", dbName, userName, pwd), ex);
}
context.Response.Write(retval);
break;
case "POST":
break;
default:
break;
}
more code sample for Methodman
/// <summary>
/// Execute a stored procedure via a SqlCommand (that returns a 1x1 resultset) against the database specified in
/// the connection string using the provided parameter values. This method will query the database to discover the parameters for the
/// stored procedure (the first time each stored procedure is called), and assign the values based on parameter order.
/// </summary>
/// <remarks>
/// This method provides no access to output parameters or the stored procedure's return value parameter.
///
/// e.g.:
/// int orderCount = (int)ExecuteScalar(connString, "GetOrderCount", 24, 36);
/// </remarks>
/// <param name="connectionString">A valid connection string for a SqlConnection</param>
/// <param name="spName">The name of the stored procedure</param>
/// <param name="parameterValues">An array of objects to be assigned as the input values of the stored procedure</param>
/// <returns>An object containing the value in the 1x1 resultset generated by the command</returns>
public static object ExecuteScalar(string connectionString, string spName, params object[] parameterValues)
{
if( connectionString == null || connectionString.Length == 0 ) throw new ArgumentNullException( "connectionString" );
if( spName == null || spName.Length == 0 ) throw new ArgumentNullException( "spName" );
// If we receive parameter values, we need to figure out where they go
if ((parameterValues != null) && (parameterValues.Length > 0))
{
// Pull the parameters for this stored procedure from the parameter cache (or discover them & populate the cache)
SqlParameter[] commandParameters = SqlHelperParameterCache.GetSpParameterSet(connectionString, spName);
// Assign the provided values to these parameters based on parameter order
AssignParameterValues(commandParameters, parameterValues);
// Call the overload that takes an array of SqlParameters
return ExecuteScalar(connectionString, CommandType.StoredProcedure, spName, commandParameters);
}
else
{
// Otherwise we can just call the SP without params
return ExecuteScalar(connectionString, CommandType.StoredProcedure, spName);
}
}
/// <summary>
/// Execute a SqlCommand (that returns a 1x1 resultset and takes no parameters) against the provided SqlConnection.
/// </summary>
/// <remarks>
/// e.g.:
/// int orderCount = (int)ExecuteScalar(conn, CommandType.StoredProcedure, "GetOrderCount");
/// </remarks>
/// <param name="connection">A valid SqlConnection</param>
/// <param name="commandType">The CommandType (stored procedure, text, etc.)</param>
/// <param name="commandText">The stored procedure name or T-SQL command</param>
/// <returns>An object containing the value in the 1x1 resultset generated by the command</returns>
public static object ExecuteScalar(SqlConnection connection, CommandType commandType, string commandText)
{
// Pass through the call providing null for the set of SqlParameters
return ExecuteScalar(connection, commandType, commandText, (SqlParameter[])null);
}
/// <summary>
/// Execute a SqlCommand (that returns a 1x1 resultset) against the specified SqlConnection
/// using the provided parameters.
/// </summary>
/// <remarks>
/// e.g.:
/// int orderCount = (int)ExecuteScalar(conn, CommandType.StoredProcedure, "GetOrderCount", new SqlParameter("#prodid", 24));
/// </remarks>
/// <param name="connection">A valid SqlConnection</param>
/// <param name="commandType">The CommandType (stored procedure, text, etc.)</param>
/// <param name="commandText">The stored procedure name or T-SQL command</param>
/// <param name="commandParameters">An array of SqlParamters used to execute the command</param>
/// <returns>An object containing the value in the 1x1 resultset generated by the command</returns>
public static object ExecuteScalar(SqlConnection connection, CommandType commandType, string commandText, params SqlParameter[] commandParameters)
{
if( connection == null ) throw new ArgumentNullException( "connection" );
// Create a command and prepare it for execution
SqlCommand cmd = new SqlCommand();
bool mustCloseConnection = false;
PrepareCommand(cmd, connection, (SqlTransaction)null, commandType, commandText, commandParameters, out mustCloseConnection );
// Execute the command & return the results
object retval;
try
{
retval = cmd.ExecuteScalar();
}
finally
{
// Detach the SqlParameters from the command object, so they can be used again
cmd.Parameters.Clear();
if (mustCloseConnection)
connection.Close();
}
return retval;
}
/// <summary>
/// Execute a stored procedure via a SqlCommand (that returns a 1x1 resultset) against the specified SqlConnection
/// using the provided parameter values. This method will query the database to discover the parameters for the
/// stored procedure (the first time each stored procedure is called), and assign the values based on parameter order.
/// </summary>
/// <remarks>
/// This method provides no access to output parameters or the stored procedure's return value parameter.
///
/// e.g.:
/// int orderCount = (int)ExecuteScalar(conn, "GetOrderCount", 24, 36);
/// </remarks>
/// <param name="connection">A valid SqlConnection</param>
/// <param name="spName">The name of the stored procedure</param>
/// <param name="parameterValues">An array of objects to be assigned as the input values of the stored procedure</param>
/// <returns>An object containing the value in the 1x1 resultset generated by the command</returns>
public static object ExecuteScalar(SqlConnection connection, string spName, params object[] parameterValues)
{
if( connection == null ) throw new ArgumentNullException( "connection" );
if( spName == null || spName.Length == 0 ) throw new ArgumentNullException( "spName" );
// If we receive parameter values, we need to figure out where they go
if ((parameterValues != null) && (parameterValues.Length > 0))
{
// Pull the parameters for this stored procedure from the parameter cache (or discover them & populate the cache)
SqlParameter[] commandParameters = SqlHelperParameterCache.GetSpParameterSet(connection, spName);
// Assign the provided values to these parameters based on parameter order
AssignParameterValues(commandParameters, parameterValues);
// Call the overload that takes an array of SqlParameters
return ExecuteScalar(connection, CommandType.StoredProcedure, spName, commandParameters);
}
else
{
// Otherwise we can just call the SP without params
return ExecuteScalar(connection, CommandType.StoredProcedure, spName);
}
}
/// <summary>
/// Execute a SqlCommand (that returns a 1x1 resultset and takes no parameters) against the provided SqlTransaction.
/// </summary>
/// <remarks>
/// e.g.:
/// int orderCount = (int)ExecuteScalar(trans, CommandType.StoredProcedure, "GetOrderCount");
/// </remarks>
/// <param name="transaction">A valid SqlTransaction</param>
/// <param name="commandType">The CommandType (stored procedure, text, etc.)</param>
/// <param name="commandText">The stored procedure name or T-SQL command</param>
/// <returns>An object containing the value in the 1x1 resultset generated by the command</returns>
public static object ExecuteScalar(SqlTransaction transaction, CommandType commandType, string commandText)
{
// Pass through the call providing null for the set of SqlParameters
return ExecuteScalar(transaction, commandType, commandText, (SqlParameter[])null);
}
/// <summary>
/// Execute a SqlCommand (that returns a 1x1 resultset) against the specified SqlTransaction
/// using the provided parameters.
/// </summary>
/// <remarks>
/// e.g.:
/// int orderCount = (int)ExecuteScalar(trans, CommandType.StoredProcedure, "GetOrderCount", new SqlParameter("#prodid", 24));
/// </remarks>
/// <param name="transaction">A valid SqlTransaction</param>
/// <param name="commandType">The CommandType (stored procedure, text, etc.)</param>
/// <param name="commandText">The stored procedure name or T-SQL command</param>
/// <param name="commandParameters">An array of SqlParamters used to execute the command</param>
/// <returns>An object containing the value in the 1x1 resultset generated by the command</returns>
public static object ExecuteScalar(SqlTransaction transaction, CommandType commandType, string commandText, params SqlParameter[] commandParameters)
{
if( transaction == null ) throw new ArgumentNullException( "transaction" );
if( transaction != null && transaction.Connection == null ) throw new ArgumentException( "The transaction was rollbacked or commited, please provide an open transaction.", "transaction" );
// Create a command and prepare it for execution
SqlCommand cmd = new SqlCommand();
bool mustCloseConnection = false;
PrepareCommand(cmd, transaction.Connection, transaction, commandType, commandText, commandParameters, out mustCloseConnection );
// Execute the command & return the results
object retval = cmd.ExecuteScalar();
// Detach the SqlParameters from the command object, so they can be used again
cmd.Parameters.Clear();
return retval;
}
/// <summary>
/// Execute a stored procedure via a SqlCommand (that returns a 1x1 resultset) against the specified
/// SqlTransaction using the provided parameter values. This method will query the database to discover the parameters for the
/// stored procedure (the first time each stored procedure is called), and assign the values based on parameter order.
/// </summary>
/// <remarks>
/// This method provides no access to output parameters or the stored procedure's return value parameter.
///
/// e.g.:
/// int orderCount = (int)ExecuteScalar(trans, "GetOrderCount", 24, 36);
/// </remarks>
/// <param name="transaction">A valid SqlTransaction</param>
/// <param name="spName">The name of the stored procedure</param>
/// <param name="parameterValues">An array of objects to be assigned as the input values of the stored procedure</param>
/// <returns>An object containing the value in the 1x1 resultset generated by the command</returns>
public static object ExecuteScalar(SqlTransaction transaction, string spName, params object[] parameterValues)
{
if( transaction == null ) throw new ArgumentNullException( "transaction" );
if( transaction != null && transaction.Connection == null ) throw new ArgumentException( "The transaction was rollbacked or commited, please provide an open transaction.", "transaction" );
if( spName == null || spName.Length == 0 ) throw new ArgumentNullException( "spName" );
// If we receive parameter values, we need to figure out where they go
if ((parameterValues != null) && (parameterValues.Length > 0))
{
// PPull the parameters for this stored procedure from the parameter cache (or discover them & populate the cache)
SqlParameter[] commandParameters = SqlHelperParameterCache.GetSpParameterSet(transaction.Connection, spName);
// Assign the provided values to these parameters based on parameter order
AssignParameterValues(commandParameters, parameterValues);
// Call the overload that takes an array of SqlParameters
return ExecuteScalar(transaction, CommandType.StoredProcedure, spName, commandParameters);
}
else
{
// Otherwise we can just call the SP without params
return ExecuteScalar(transaction, CommandType.StoredProcedure, spName);
}
}
/// <summary>
/// Execute a SqlCommand (that returns a 1x1 resultset) against the database specified in the connection string
/// using the provided parameters.
/// </summary>
/// <remarks>
/// e.g.:
/// int orderCount = (int)ExecuteScalar(connString, CommandType.StoredProcedure, "GetOrderCount", new SqlParameter("#prodid", 24));
/// </remarks>
/// <param name="connectionString">A valid connection string for a SqlConnection</param>
/// <param name="commandType">The CommandType (stored procedure, text, etc.)</param>
/// <param name="commandText">The stored procedure name or T-SQL command</param>
/// <param name="commandParameters">An array of SqlParamters used to execute the command</param>
/// <returns>An object containing the value in the 1x1 resultset generated by the command</returns>
public static object ExecuteScalar(string connectionString, CommandType commandType, string commandText, params SqlParameter[] commandParameters)
{
if( connectionString == null || connectionString.Length == 0 ) throw new ArgumentNullException( "connectionString" );
// Create & open a SqlConnection, and dispose of it after we are done
using (SqlConnection connection = new SqlConnection(connectionString))
{
connection.Open();
// Call the overload that takes a connection in place of the connection string
return ExecuteScalar(connection, commandType, commandText, commandParameters);
}
}
From the error log, I see you cannot open a database. Since you didn't share the code, that's pretty much the only thing we can currently take care of - so let's focus on that.
Inability to open a database might be a result of a few causes, the most popular ones being:
You lack premissions
The database is already locked by another source
The database is not well specified.
Permissions
Databases, in the end, are a file handled by the system. Thus, in order to edit them, you need the required credetinals - which could depend on many things. For example, a file which is held on a user on a windows NT system may be only accessable for this user and a few others; another good example is if you try to execute a command from the CMD which requires an administrator.
Solution:
Run as admin. See what happens. Solved your problem? require an admin access in the configuration file, and the problem is solved.
Database locked
Database systems have many shananigans to them. Let's take for example Microsoft Access - It's a great system, but new users take a few trys before they understand how to fix their mistakes with it. The reason is simple - Access is very egoistic about it's objects. Their names, threads, constants, and methods have to occur in a very specific way, otherwise the system just throws an exception and a message box, which in visual studio will usually result in an undetailed exception. In your case, the database system might be similar - It might be currently locked because of many reasons.
Solution: Close the DB application after saving it if it's open. Manually remove the processess related, and if none of the above worked, restart your computer.
Database specification
This error is actually quite similar to the last one, but here the source is not the database application, but the operating system itself. This might be a result of wrong file names, DB types, etc. and the solution is to look deep into your code (feel free to share it, we could help you with that) and look for bad castings, connections, paths, and all that stuff.
Solution: As i mentioned, just take a deep look into the code. Rewrite the connection part if neccessary.
Edit: Regarding the error, i just noticed you get the SQL 4060. That's actually perfectly fit for your description - executting it from different workspaces might get you different premissions. Try the solutions i mentioned under the "Premissions" paragraph and let us know how it went.

Pass Parameters Generically

I am wanting to create a generic database class so all our developers can follow the exact same syntax by just importing a database dll. That being said, I am somewhat stuck on how to round off this part. I need to find a way to pass an unknown amount of parameters through this method. I tried doing a list, but didn't seem to get anywhere with it. Also just for clarity sake the db.ValidateConnection(Connection) portion just makes sure the connection isn't already in use or open, then opens it.
Want to call this method:
public static SqlDataReader ExecuteReader(string CommandName,
SqlConnection Connection,
PARAMETERS??!?!?)
{
using (SqlCommand cmd = new SqlCommand(CommandName, Connection))
{
// Ensure we are executing a stored procedure.
cmd.CommandType = CommandType.StoredProcedure;
// Ensure our connection is not already open and then open it.
db.ValidateConnection(Connection);
// Return our SqlDataReader object with the desired execution results.
return cmd.ExecuteReader();
}
}
With this idea of code:
// Want to add these Parameters..//
cmd.Parameters.AddWithValue("#Param0", Param0);
cmd.Parameters.AddWithValue("#Param1", Param1);
cmd.Parameters.AddWithValue("#Param2", Param2);
// Or These Parameter...
cmd.Parameters.AddWithValue("#Param2", Param2);
// Or NO Parameters...
// To this Execution statement.
ExecuteReader(CommandName: "[MyStoredProcedure]",
Connection: "MyConnection",
PARAMTERS??!?!?!: "ListOfAllParametersToAttach")
--UPDATE
Final working code! Invoked in our .dll:
public static SqlDataReader ExecuteReader(string CommandName,
SqlConnection Connection,
params SqlParameter[] Parameters)
{
// ExecuteReader
//
// #CommandName string :
// Name of the stored procedure we are looking to execute.
//
// #Connection SqlConnection :
// SQL Connection to be used when executing the provided #CommandName.
//
// #Parameters params SqlParameter[] :
// If any parameters exist this will contain the list of we will attach to our SqlCommand.
using (SqlCommand cmd = new SqlCommand(CommandName, Connection))
{
// Ensure we are executing a stored procedure.
cmd.CommandType = CommandType.StoredProcedure;
// Iterate through our list and add all required parameters.
foreach (SqlParameter Parameter in Parameters)
{
cmd.Parameters.Add(Parameter);
}
// Ensure our connection is not already open and then open it.
db.ValidateConnection(Connection);
// Return our SqlDataReader object with the desired execution results.
return cmd.ExecuteReader();
}
}
Called from application :
using (SqlDataReader dr = SMCConnect.ExecuteReader("[ProcedureName]",
con,
new SqlParameter("#ParameterName", ParameterName))) { }
I'd use params SqlParameter[]. Which is called as "variable length parameters" or Variadic.
public static SqlDataReader ExecuteReader(string CommandName,
SqlConnection Connection,
params SqlParameter[] parameters)
{
using (SqlCommand cmd = new SqlCommand(CommandName, Connection))
{
cmd.CommandType = CommandType.StoredProcedure;
foreach (var param in parameters)
{
cmd.Parameters.Add(param);
}
//Do something..
return cmd.ExecuteReader();
}
}
Use case:
ExecuteReader("MyStoredProcedure",myConnection,new SqlParameter("#param1",value1),new SqlParameter("#param2",value2),...);
Use params[] KeyValuePair<string, object> in your signature, like this:
public static SqlDataReader ExecuteReader(string CommandName,
SqlConnection Connection,
params[] KeyValuePair<string, object> parameters)
//...
foreach (var p in parameters)
cmd.Parameters.Add(p.Key, p.Value)
//...
public static SqlDataReader ExecuteReader(string CommandName,
SqlConnection Connection,
Dictionary<string, object> parameters)
and then
foreach (var key in parameters.Keys)
{
cmd.Parameters.AddWithValue(key, parameters[key]);
}

How to manage ado.net transaction

I'm not sure what a good way to manage this transaction in code would be.
Say I have the following
Service layer (non-static class)
Repository layer (static class)
// Service layer class
/// <summary>
/// Accept offer to stay
/// </summary>
public bool TxnTest()
{
using (SqlConnection conn = new SqlConnection(ConnectionString))
{
conn.Open();
SqlTransaction txn = conn.BeginTransaction();
using (SqlCommand cmd = conn.CreateCommand())
{
cmd.Transaction = txn;
try
{
DoThis(cmd);
DoThat(cmd);
txn.Commit();
}
catch (SqlException sqlError)
{
txn.Rollback();
}
}
}
}
// Repo Class
/// <summary>
/// Update Rebill Date
/// </summary>
public static void DoThis(SqlCommand cmd)
{
cmd.Parameters.Clear();
cmd.Parameters.AddWithValue("#SomeParam", 1);
cmd.CommandText = "Select * from sometable1";
cmd.CommandType = CommandType.Text;
cmd.ExecuteNonQuery();
}
/// <summary>
/// Update Rebill Date
/// </summary>
public static void DoThat(SqlCommand cmd)
{
cmd.Parameters.Clear();
cmd.Parameters.AddWithValue("#SomeParam", 2);
cmd.CommandText = "Select * from sometable2";
cmd.CommandType = CommandType.Text;
cmd.ExecuteNonQuery();
}
Is the above approach any good? Is it wise to use a static class for the respository or will that create problems?
Is there a way to do this without having to pass a command (cmd) object around?
You might want to take a look at the unit of work pattern.
The unit of work pattern defines exactly what it suggests, a unit of work that is committed all at once, or not at all.
This occurs by defining an interface that has two parts:
Methods that handle your insert, update, deleted operations (note, you don't have to expose all of these operations, and you aren't limited to one entity type)
A method to commit (if you rollback, you simply don't call commit). This is where you would handle the transaction as well as the inserting, updating and/or deletion of all the entities that registered to be changed.
Then, you would pass an implementation of this interface around, and commit the changes at the outer boundaries (your service) when all the operations are complete.
Note that the ObjectContext class in LINQ-to-Entities and the DataContext class in LINQ-to-SQL are both examples of units of work (you perform operations and then save them in a batch).

How to find the Transaction Status

I am using 'TransactionScope', and I need to just do some DML within the C# Code, which I have successfully.
I need to find out that what is the status of the Transaction, that is has it completed successfully or not ?
Because on the basis of the Transaction Status, if the transaction is complete then I need to perform a redirect to another page, otherwise if the transaction is not completed successfully then I need to show the error on the page.
I want to redirect after the following:-
scope.Complete();
scope.Dispose();
Please help me in this regards.
If you visit the MSDN page for TransactionScope, you would find this well-documented example:
try
{
// Create the TransactionScope to execute the commands, guaranteeing
// that both commands can commit or roll back as a single unit of work.
using (TransactionScope scope = new TransactionScope())
{
using (SqlConnection connection1 = new SqlConnection(connectString1))
{
// Opening the connection automatically enlists it in the
// TransactionScope as a lightweight transaction.
connection1.Open();
// Create the SqlCommand object and execute the first command.
SqlCommand command1 = new SqlCommand(commandText1, connection1);
returnValue = command1.ExecuteNonQuery();
writer.WriteLine("Rows to be affected by command1: {0}", returnValue);
// If you get here, this means that command1 succeeded. By nesting
// the using block for connection2 inside that of connection1, you
// conserve server and network resources as connection2 is opened
// only when there is a chance that the transaction can commit.
using (SqlConnection connection2 = new SqlConnection(connectString2))
{
// The transaction is escalated to a full distributed
// transaction when connection2 is opened.
connection2.Open();
// Execute the second command in the second database.
returnValue = 0;
SqlCommand command2 = new SqlCommand(commandText2, connection2);
returnValue = command2.ExecuteNonQuery();
writer.WriteLine("Rows to be affected by command2: {0}", returnValue);
}
}
// The Complete method commits the transaction. If an exception has been thrown,
// Complete is not called and the transaction is rolled back.
scope.Complete();
}
}
catch (TransactionAbortedException ex)
{
writer.WriteLine("TransactionAbortedException Message: {0}", ex.Message);
}
catch (ApplicationException ex)
{
writer.WriteLine("ApplicationException Message: {0}", ex.Message);
}
The comment that contains the most value is this one:
The Complete method commits the transaction. If an exception has been thrown, Complete is not called and the transaction is rolled back.
So, if no exceptions are thrown, you can continue on. Put your redirect after scope.Complete(). If an exception is thrown, the transaction failed and has automatically been rolled back. You could double-check the transaction status (as others have posted) after the call to Complete() and before you redirect, via Transaction.Current.TransactionInformation.Status:
if (Transaction.Current.TransactionInformation.Status == TransactionStatus.Committed)
{
// do redirect
}
Use Transaction.Current.TransactionInformation.Status
Transaction.Current Property
Transaction.TransactionInformation Property
How about:
TransactionStatus status = Transaction.Current.TransactionInformation.Status;
The best method I've found for capturing this most efficiently / correctly is as follows:
Inside the transactionscope using statement, and before the call to scope/Complete().
//Register for the transaction completed event for the current transaction
Transaction.Current.TransactionCompleted += new TransactionCompletedEventHandler(Current_TransactionCompleted);
Then create the event handler function as follows:
/// <summary>
/// Handles the TransactionCompleted event of the Current control.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">The <see cref="System.Transactions.TransactionEventArgs"/> instance containing the event data.</param>
static void Current_TransactionCompleted(object sender, TransactionEventArgs e)
{
if (e.Transaction.TransactionInformation.Status == TransactionStatus.Committed)
{
/// Yay it's committed code goes here!
}
}
To quote MSDN
"You can register for this event instead of using a volatile enlistment to get outcome information for transactions. The parameter passed to the TransactionCompletedEventHandler delegate is a Transaction instance. You can then query the TransactionInformation property of the specific instance to get an instance of TransactionInformation, whose Status property contains the status of a transaction with either the Committed or Aborted value."

C# 'using' statement question

If you employ a using clause to dispose of a connection, are other items within the clause that implement IDisposable also automatically disposed? If not, how do you handle making sure all IDisposable items are automatically disposed?
public static DataTable ReturnDataTable(
string ConnectionString, string CommandTextString, CommandType CommandType,
int CommandTimeout, List<System.Data.SqlClient.SqlParameter> ParameterList = null)
{
using (System.Data.SqlClient.SqlConnection Connection =
new System.Data.SqlClient.SqlConnection())
{
Connection.ConnectionString = ConnectionString;
System.Data.SqlClient.SqlCommand Command =
new System.Data.SqlClient.SqlCommand();
Command.Connection = Connection;
Command.CommandText = CommandTextString;
Command.CommandType = CommandType;
Command.CommandTimeout = CommandTimeout;
if (ParameterList != null)
{
if (ParameterList.Count > 0)
{
foreach (SqlParameter parameter in ParameterList)
{
Command.Parameters.AddWithValue(
parameter.ParameterName, parameter.Value);
}
}
}
System.Data.DataTable DataTable = new System.Data.DataTable();
System.Data.SqlClient.SqlDataAdapter DataAdapter =
new System.Data.SqlClient.SqlDataAdapter();
DataAdapter.SelectCommand = Command;
DataAdapter.Fill(DataTable);
return DataTable;
}
}
You can stack the statements like this (to initialize all disposable objects early on)
using (...)
using (...)
{
...
}
or you can use nested using statements for each disposable object you need
using (...)
{
using (...) { ... }
using (...) { ... }
}
Only an object created in the using clause will be disposed. If you want to make sure that call to dispose is automatically generated for every disposable object created inside of the using block you will need to wrap each of them in a using clause (or you can just call dispose or close, whichever they support, of course). So, the answer is not.
No. You will have to explicitly call Dispose on the ones that are not inside the parameters of the using statement.

Categories

Resources