EFCore 5.0 Interceptor statement type - ef-core-5.0

I've implemeted an DbCommandInterceptor adding a max recursion depth to the generated SQL:
public override ValueTask<InterceptionResult<DbDataReader>> ReaderExecutingAsync(DbCommand command, CommandEventData eventData, InterceptionResult<DbDataReader> result, CancellationToken cancellationToken = default)
{
if (command.CommandText.StartsWith("SELECT") && !command.CommandText.EndsWith($" OPTION(MAXRECURSION {MaxRecursion})"))
{ command.CommandText += $" OPTION(MAXRECURSION {MaxRecursion})"; }
return base.ReaderExecutingAsync(command, eventData, result, cancellationToken);
}
When not checking if the SQL starts with SELECT (command.CommandText.StartsWith("SELECT")) I sure get an exception on update statements (e.g. context.SaveChanges()). Is there a neater solution for checking the query type? Like an enum on the Command or similar?
Thank you!

Not supported right now, feature request on Github created:
https://github.com/dotnet/efcore/issues/23719

Related

How do you evaluate boolean and then execute statements in java 8 with Optional?

How can I refactor this snippet in Java 8 Optional? Can I use map with OrElseThrow for this? What is a cleaner way of writing this code?
public void updateMyDao(MyObj objToUpdate) {
if(myOptional.isPresent()) {
MyDao oq = myOptional.get();
if(!oq.getReferenceId().equals(objToUpdate.getId()))
throw new RuntimeException("Bad Move!");
oq.setAttribute(objToUpdate.getAttribute());
.....
.....
} else {
throw new RuntimeException("Entity was not found");
}
}
IMHO there are three reasonable scenarios for Optional:
Do something if there is a value
Provide a default value otherwise
The absence of a value is an error condition, in which case you want to fail fast
Your scenario falls into the last category so I would simply write:
MyDao oq = myOptional.orElseThrow(() -> new RuntimeException("Entity was not found");
if(!oq.getReferenceId().equals(objToUpdate.getId()))
throw new RuntimeException("Bad Move!");
oq.setAttribute(objToUpdate.getAttribute());
Of course, it's appealing to use methods like ifPresent, filter or map, but in your case, why would you want to continue when the application is in a faulty state. Now if you wouldn't throw an exception if the entity wasn't found, then the situation would be different.
Something like oq.checkMove(objToUpdate.getId()) could make sense. That would eliminate the if and make the code more expressive.
Don't know where did you create the Optional object. But your current code can be shortened using orElseThrow():
MyDao oq = myOptional.orElseThrow(() -> new RuntimeException("Entity was not found"));
if(!oq.getReferenceId().equals(objToUpdate.getId()))
throw new RuntimeException("Bad Move!");
oq.setAttribute(objToUpdate.getAttribute());

How do I do an EF Database.ExecuteSQLCommand async?

Here's the code that I am using:
public async Task<IHttpActionResult> NewTopicTests([FromBody] NewTopicTestsDTO testSpec)
{
var sql = #"dbo.sp_new_topic_tests #Chunk";
SqlParameter[] parameters = new SqlParameter[]
{
new SqlParameter("#Chunk", testSpec.Chunk)
};
int result = db.Database.ExecuteSqlCommand(sql, parameters);
await db.SaveChangesAsync();
return Ok();
}
Can someone confirm if this is the correct way to do this using async? In particular do I need to do this:
int result = db.Database.ExecuteSqlCommand(sql, parameters);
await db.SaveChangesAsync();
Note that the code works however I am having a problem with my application where it suddenly stops without any error message. I am looking into every possible problem.
What's being saved here ? I think there is no need to call save changes here.
Remove save changes and you will see the same behavior, because any changes you've made in your stored procedure, are not tracked by entity framework context.
And you can rewrite your code as following:
int result = await db.Database.ExecuteSqlCommandAsync(sql, parameters);
Have you checked every where to find the reason of your problem ? Windows Error Log etc ?
Go to Debug menu of Visual Studio IDE, and open Exceptions, then check both 'throw' and 'user_unhandled' for 'Common Language Runtime Exceptions' and test your code again.

SQL statement's placeholders that is not replaced leads to "Cannot update '#columnName'; field not updateable"

I'm writing some code updating database with a SQL statement that has some placeholders . But it doesn't seem to update these placeholders.
I got the following error:
Cannot update '#columnName'; field not updateable
Here is the method:
public void updateDoctorTableField(string columnName, string newValue, string vendorNumber) {
sqlStatement = "update Doctor set #columnName = #newValue where `VENDOR #` = #vendorNumber;";
try {
_command = new OleDbCommand(sqlStatement, _connection);
_command.Parameters.Add("#columnName", OleDbType.WChar).Value = columnName;
_command.Parameters.Add("#newValue", OleDbType.WChar).Value = newValue;
_command.Parameters.Add("#vendorNumber", OleDbType.WChar).Value = vendorNumber;
_command.ExecuteNonQuery();
} catch (Exception ex) {
processExeption(ex);
} finally {
_connection.Close();
}
}
Not all parts of the query are parameterisable.
You can't parametrise the name of the column. This needs to be specified explicitly in your query text.
If this is sent via user input you need to take care against SQL Injection. In fact in any event it would be best to check it against a whitelist of known valid column names.
The reason the language does not allow for parameters for things like table names, column names and such is exactly the same reason why your C# program does not allow for substitution of variables in the code. Basically your question can be rephrased like this in a C# program:
class MyClass
{
int x;
float y;
string z;
void DoSomething(string variableName)
{
this.#variable = ...
}
}
MyCLass my = new MyClass();
my.DoSomething("x"); // expect this to manuipulate my.x
my.DoSomething("y"); // expect this to manuipulate my.y
my.DoSomething("z"); // expect this to manuipulate my.z
This obviously won't compile, because the compiler cannot generate the code. Same for T-SQL: the compiler cannot generate the code to locate the column "#columnName" in your case. And just as in C# you would use reflection to do this kind of tricks, in T-SQL you would use dynamic SQL to achieve the same.
You can (and should) use the QUOTENAME function when building your dynamic SQL to guard against SQL injection.

Is there anything wrong with this database class's execute query function?

So I have this old code being used, that runs simple ExecuteNonQuery command for database calls. I'm using DbConnection, DbTransaction and other System.Data.Common commands.
I seem to get a lot of Null Reference errors whenever I use the function in certain parts of the project, though it seems fine in other parts. I think it has to do with opening connections manually or some problem with calling it, but I'm wondering if the function itself is badly designed originally (shouldn't there be a way to fix any problems in the way it is called?)
I feel when transactions are involved, these null reference errors come up more often, I think the error I get is null exception at "_command = _db.GetStoredProcCommand(storedProcedure);" inside the following function. But that stored procedure does exist, so it makes no sense.
public List<OutputParameter> execute(String storedProcedure, StoredProcedureParameter[] sqlParameters)
{
try
{
List<OutputParameter> outputParameters = new List<OutputParameter>();
_command = _db.GetStoredProcCommand(storedProcedure);
for (int x = 0; x < sqlParameters.GetLength(0); x++)
{
if (sqlParameters[x] != null)
{
StoredProcedureParameter sqlParameter = sqlParameters[x];
String param = sqlParameter.ParameterName;
DbType dbType = sqlParameter.DbType;
object value = sqlParameter.Value;
if (sqlParameter.IsOutputParam)
{
_db.AddOutParameter(_command, param, dbType, 32);
OutputParameter outputParameter = new OutputParameter(param);
outputParameters.Add(outputParameter);
}
else
_db.AddInParameter(_command, param, dbType, value);
}
}
if (_transaction == null)
_db.ExecuteNonQuery(_command);
else
_db.ExecuteNonQuery(_command, _transaction);
foreach (OutputParameter op in outputParameters)
{
op.ParameterValue = _db.GetParameterValue(_command, op.ParameterName);
}
return outputParameters;
}
catch (SqlException sqle)
{
throw new DataAccessException(sqle.ToString());
}
catch (Exception e)
{
throw new DataAccessException(e.ToString());
}
}
Your _command variable appears to be a field and as such a shared member.
As such your code is very susceptible to multithreading issues (if two functions call this class with different stored procedures, what happens?).
A Command should also be closed and disposed of properly, which is not happening in your code, not explicitly anyways.
If you are getting a null reference exception in the line _command = _db.GetStoredProcCommand(storedProcedure); then the only thing that can be null there is _db. The storedProcedure is just a parameter and _command could happily be null without a problem.
Since you aren't actually doing anything in the code to make sure that _db exists and is valid, open, etc. then this is most likely the problem.

Debug Mode: See what is in SqlCommand

I'd like to see what is about to be sent to the SQL Server from my SqlCommand before SQLCmd.ExecuteNonQuery() runs.
I'm trying to debug because I am receiving the following error: System.FormatException: Failed to convert parameter value from a String to a Int32.
Normally, I would use SQL Server Profiler to view what is being sent to SQL Server, but my statement is not making it that far.
Is there a way to determine what it is trying to convert? I am having problems determining which parameter is causing the error.
There's nothing that can quickly visualize it for you, but you can browse through the object and get at the internal list of parameters and view their names and values individually.
It will likely be faster just to write something that you can pass the command into and it will print out the name, DbType, Value.ToString() and Value.GetType().Name for each parameter.
void PrintCommand(DbCommand command)
{
Console.WriteLine("CommandType: {0}", command.CommandType);
Console.WriteLine("CommandText: {0}", command.CommandText);
foreach(var parameter in command.Parameters)
{
Console.WriteLine(" Parameter {0}, {1}: \"{2}\" ({3})",
parameter.ParameterName,
parameter.DbType,
parameter.Value,
parameter.Value.GetType().Name);
}
}
Try the immediate window:
? SQLCmd.Parameters["#MyParam"].Value

Resources