Home > Blockchain >  How to write tests for Entity Framework Sql providers and access the generated Sql command
How to write tests for Entity Framework Sql providers and access the generated Sql command

Time:09-30

I have a dynamic-LINQ library that generates query expression for anyIQueryable support lists and EntityFramework... I want to have some tests to check the entity-framework SQL providers generated SQL queries. The main problem is I can not use the In-Memory provider because it doesn't show me the SQL outputs. how can I write these tests without having an actual database installed? Is there any package that I can use? (I need to test EF SqlServer and MySql providers).

CodePudding user response:

Use an Interceptor and suppress the actual execution, following this example from the docs:

public override ValueTask<InterceptionResult<DbDataReader>> ReaderExecutingAsync(
    DbCommand command,
    CommandEventData eventData,
    InterceptionResult<DbDataReader> result,
    CancellationToken cancellationToken = default)
{
    if (command.CommandText.StartsWith("-- Get_Daily_Message", StringComparison.Ordinal))
    {
        lock (_lock)
        {
            if (_message != null
                && DateTime.UtcNow < _queriedAt   new TimeSpan(0, 0, 10))
            {
                command.CommandText = "-- Get_Daily_Message: Skipping DB call; using cache.";
                result = InterceptionResult<DbDataReader>.SuppressWithResult(new CachedDailyMessageDataReader(_id, _message));
            }
        }
    }

    return new ValueTask<InterceptionResult<DbDataReader>>(result);
}

Interceptors - Before Execution

CodePudding user response:

Thanks to @David Browne - I ended up with the below code. just want to share it if anyone had a similar problem in the future.

// interceptors.cs
 public class SuppressConnectionInterceptor : DbConnectionInterceptor
   {
      public override ValueTask<InterceptionResult> ConnectionOpeningAsync(DbConnection connection, ConnectionEventData eventData,
         InterceptionResult result,
         CancellationToken cancellationToken = new())
      {
         result = InterceptionResult.Suppress();
         return base.ConnectionOpeningAsync(connection, eventData, result, cancellationToken);
      }

      public override InterceptionResult ConnectionOpening(DbConnection connection, ConnectionEventData eventData, InterceptionResult result)
      {
         result = InterceptionResult.Suppress();
         return base.ConnectionOpening(connection, eventData, result);
      }
   }

 public class SuppressCommandResultInterceptor : DbCommandInterceptor
   {
      public override InterceptionResult<DbDataReader> ReaderExecuting(
         DbCommand command,
         CommandEventData eventData,
         InterceptionResult<DbDataReader> result)
      {
         result = InterceptionResult<DbDataReader>.SuppressWithResult(new EmptyMessageDataReader());

         return result;
      }

      public override ValueTask<InterceptionResult<DbDataReader>> ReaderExecutingAsync(
         DbCommand command,
         CommandEventData eventData,
         InterceptionResult<DbDataReader> result,
         CancellationToken cancellationToken = default)
      {
         result = InterceptionResult<DbDataReader>.SuppressWithResult(new EmptyMessageDataReader());

         return new ValueTask<InterceptionResult<DbDataReader>>(result);
      }

   public class EmptyMessageDataReader : DbDataReader
   {
   
      private readonly List<User> _users = new List<User>();

      public EmptyMessageDataReader()
      {
      }

      public override int FieldCount
         => 0;

      public override int RecordsAffected
         => 0;

      public override bool HasRows
         => false;

      public override bool IsClosed
         => true;

      public override int Depth
         => 0;

      public override bool Read()
         => false;

      public override int GetInt32(int ordinal)
         => 0;

      public override bool IsDBNull(int ordinal)
         => false;

      public override string GetString(int ordinal)
         => "suppressed message";

      public override bool GetBoolean(int ordinal)
         => true;

      public override byte GetByte(int ordinal)
         => 0;

      public override long GetBytes(int ordinal, long dataOffset, byte[] buffer, int bufferOffset, int length)
         => 0;

      public override char GetChar(int ordinal)
         => '\0';

      public override long GetChars(int ordinal, long dataOffset, char[] buffer, int bufferOffset, int length)
         => 0;

      public override string GetDataTypeName(int ordinal)
         => string.Empty;

      public override DateTime GetDateTime(int ordinal)
         => DateTime.Now;

      public override decimal GetDecimal(int ordinal)
         => 0;

      public override double GetDouble(int ordinal)
         => 0;

      public override Type GetFieldType(int ordinal)
         => typeof(User);

      public override float GetFloat(int ordinal)
         => 0;

      public override Guid GetGuid(int ordinal)
         => Guid.Empty;

      public override short GetInt16(int ordinal)
         => 0;

      public override long GetInt64(int ordinal)
         => 0;

      public override string GetName(int ordinal)
         => "";

      public override int GetOrdinal(string name)
         => 0;

      public override object GetValue(int ordinal)
         => new object();

      public override int GetValues(object[] values)
         => 0;

      public override object this[int ordinal]
         => new object();

      public override object this[string name]
         => new object();

      public override bool NextResult()
         => false;

      public override IEnumerator GetEnumerator()
         => _users.GetEnumerator();
   }

  
   }
// DbContext configuration
   public class MyDbContext : DbContext
   {
      public DbSet<User> Users { get; set; }

      protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
      {
         optionsBuilder.UseSqlServer("Server=myServerAddress;Database=myDataBase;User Id=myUsername;Password=myPassword;");
         optionsBuilder.AddInterceptors(new SuppressCommandResultInterceptor());
         optionsBuilder.AddInterceptors(new SuppressConnectionInterceptor());
         base.OnConfiguring(optionsBuilder);
      }
   }

// access queryString in unit test
var actual = _dbContext.Users.Where(q => q.Name == "John").ToQueryString();
  • Related