When using MiniProfilter to wrap DbConnection, receive error Procedure or function 'X' expects parameter '@Y', which was not supplied

0

I am adding MiniProfiler to our existing custom ORM which is based on ADO.Net. This link indicates that I just need to wrap the DbConnection with a new instance of ProfiledDbConnection but when I do that all or most of the queries performed through this connection fail with the message below (or similar, not everything is ExecuteScalar). When the DbConnection is no longer wrapped then there is no longer a problem. Is there perhaps an additional step I need to perform for MiniProfiler to work with ADO.Net?

System.Data.SqlClient.SqlException (0x80131904): Procedure or function 'GetFeature' expects parameter '@Addon', which was not supplied.
   at System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction)
   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.SqlDataReader.TryConsumeMetaData()
   at System.Data.SqlClient.SqlDataReader.get_MetaData()
   at System.Data.SqlClient.SqlCommand.FinishExecuteReader(SqlDataReader ds, RunBehavior runBehavior, String resetOptionsString, Boolean isInternal, Boolean forDescribeParameterEncryption, Boolean shouldCacheForAlwaysEncrypted)
   at System.Data.SqlClient.SqlCommand.RunExecuteReaderTds(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, Boolean async, Int32 timeout, Task& task, Boolean asyncWrite, Boolean inRetry, SqlDataReader ds, Boolean describeParameterEncryptionRequest)
   at System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method, TaskCompletionSource`1 completion, Int32 timeout, Task& task, Boolean& usedCache, Boolean asyncWrite, Boolean inRetry)
   at System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method)
   at System.Data.SqlClient.SqlCommand.ExecuteScalar()
   at StackExchange.Profiling.Data.ProfiledDbCommand.ExecuteScalar()

and another example (there are many)

Procedure or function 'CampaignInProgress' expects parameter '@CampaignId', which was not supplied.|System.Data.SqlClient.SqlException (0x80131904): Procedure or function 'CampaignContactsInProgressGet' expects parameter '@CampaignId', which was not supplied.
   at System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction)
   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.SqlDataReader.TryConsumeMetaData()
   at System.Data.SqlClient.SqlDataReader.get_MetaData()
   at System.Data.SqlClient.SqlCommand.FinishExecuteReader(SqlDataReader ds, RunBehavior runBehavior, String resetOptionsString, Boolean isInternal, Boolean forDescribeParameterEncryption, Boolean shouldCacheForAlwaysEncrypted)
   at System.Data.SqlClient.SqlCommand.RunExecuteReaderTds(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, Boolean async, Int32 timeout, Task& task, Boolean asyncWrite, Boolean inRetry, SqlDataReader ds, Boolean describeParameterEncryptionRequest)
   at System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method, TaskCompletionSource`1 completion, Int32 timeout, Task& task, Boolean& usedCache, Boolean asyncWrite, Boolean inRetry)
   at System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method)
   at System.Data.SqlClient.SqlCommand.ExecuteReader(CommandBehavior behavior, String method)
   at System.Data.SqlClient.SqlCommand.ExecuteDbDataReader(CommandBehavior behavior)
   at System.Data.Common.DbCommand.ExecuteReader(CommandBehavior behavior)
   at StackExchange.Profiling.Data.ProfiledDbCommand.ExecuteDbDataReader(CommandBehavior behavior)
   at System.Data.Common.DbCommand.ExecuteReader(CommandBehavior behavior)

Edit:

CampaignInProgress and GetFeature are stored procedures. I would like to re-iterate that this is a custom 3rd party ORM which I do not have much detail on. Most of the code shown below is from R# decompilation.

You call the method like:

var parms = new List<object>
{
    new GenericParameter(parameterName: "@CampaignId", dbType: DbType.Int64, value: campaignId)
};

using (var ctx = new DbContext(connStr))
{
    ctx.FillWithSp(result, "dbo.CampaignInProgress", parms.ToArray());
}

DbContext looks like and was where I added the MiniProfiler wrapper:

public DbContext(string connectionStr) : base(ConnectionManager.GetConnection(connectionStr)) { }

Next it passes through like so:

return Adapter<TDC>().FillWithSP(collection, storedProcedure, parameters);

Then:

public int FillWithSP(
      ICollection<T> collection,
      string storedProcName,
      params object[] parameters)
    {
      if (storedProcName == null)
        throw new ArgumentNullException(nameof (storedProcName));
      return this.FillInternal(collection, storedProcName, FillMethod.StoredProcedure, (IList) parameters);
    }

Then

private int FillInternal(
      ICollection<T> collection,
      string text,
      FillMethod method,
      IList parameters)
    {
      if (collection == null)
        throw new ArgumentNullException(nameof (collection));
      if (this.Connection == null)
        throw new InvalidOperationException("must set connection before calling Fill()");
      using (DbDataReader reader = this.ExecuteReaderInternal(this.PrepCommandForSelect(text, method, parameters), CommandBehavior.SingleResult))
        return this.PopulateCollection(collection, reader);
    }

PrepCommandForSelect:

private DbCommand PrepCommandForSelect(
      string text,
      FillMethod method,
      IList parameters)
    {
      DbCommand command = (DbCommand) null;
      switch (method)
      {
          // Other cases not relevant
          case FillMethod.StoredProcedure:
          command = this.Connection.CreateCommand();
          command.CommandText = text;
          command.CommandType = CommandType.StoredProcedure;
          DataManager.AddParamsToCommand(command, this.SqlDialect, parameters, this.ParameterPrefix);
          break;
      }
      return command;
    }

AddParamsToCommand:

public static void AddParamsToCommand(
      DbCommand command,
      SqlDialect dialect,
      IList parameters,
      char placeholderPrefix)
    {
      if (command == null)
        throw new ArgumentNullException(nameof (command));
      if (parameters == null)
        return;
      for (int index = 0; index < parameters.Count; ++index)
      {
        if (parameters[index] is DbParameter)
          command.Parameters.Add(parameters[index]);
        else if (parameters[index] is GenericParameter)
        {
          GenericParameter parameter = (GenericParameter) parameters[index];
          if (parameter.ParameterName == null && command.CommandType == CommandType.Text)
            // Not relevant
          else
            parameter.AddToCommand(command, dialect);
        }
        else if (command.CommandType == CommandType.Text)
        {
          // Not relevant
        }
        else
        {
          // Not relevant
        }
      }
    }

AddToCommand

public void AddToCommand(DbCommand command, SqlDialect dialect)
    {
      if (command == null)
        throw new ArgumentNullException(nameof (command));
      if (dialect == null)
        throw new ArgumentNullException(nameof (dialect));
      this._nativeParam = command.CreateParameter();
      if (this._parameterName != null || dialect.UseNamedParameters || command.CommandType == CommandType.StoredProcedure)
        this._nativeParam.ParameterName = dialect.FormatParameterName(this._parameterName, command.CommandType);
      this._nativeParam.Direction = this._direction;
      if (this._dbType != DbType.Object)
      {
        if (this._nativeParam is SqlParameter && this._dbType == DbType.Time)
          ((SqlParameter) this._nativeParam).SqlDbType = SqlDbType.Time;
        else
          this._nativeParam.DbType = this._dbType;
      }
      if (this._size != 0)
        this._nativeParam.Size = this._size;
      this._nativeParam.SourceColumn = this._sourceColumn;
      this._nativeParam.Value = this._value ?? (object) DBNull.Value;
      command.Parameters.Add((object) this._nativeParam);
    }

FormatParameterName

public override string FormatParameterName(string name, CommandType commandType)
    {
      if (string.IsNullOrEmpty(name))
        throw new ArgumentNullException(nameof (name));
      return (name[0] == '@' ? name : "@" + name).Replace(' ', '_');
    }

Then back to FillInternal to method ExecuteReaderInternal

private DbDataReader ExecuteReaderInternal(
      DbCommand command,
      CommandBehavior behavior)
    {
      this.SetCommandTimeout((IDbCommand) command);
      this.EnrollInTransaction(command);
      this.DoBeforeExecuteCommand(command);
      return command.ExecuteReader(behavior); // System.Data.Common
    }

At this point you can pick up from the stack trace. I am not seeing anything that would cause the parameter to be named incorrectly. Keep in mind this code works when it is not wrapped in the MiniProfiler.

c#
asp.net-mvc
mvc-mini-profiler
asked on Stack Overflow Aug 8, 2020 by Thomas927 • edited Aug 9, 2020 by Thomas927

0 Answers

Nobody has answered this question yet.


User contributions licensed under CC BY-SA 3.0