Unit Testing Dapper with Inline Queries

前端 未结 3 1253
深忆病人
深忆病人 2021-01-31 19:56

I know there are several question similar to mine.

  • Dapper: Unit Testing SQL Queries
  • Testing Dapper Queries

butI don\'t think both of above

3条回答
  •  长情又很酷
    2021-01-31 20:26

    I adapted what @Mikhail did because I had issues when adding the OrmLite packages.

    internal class InMemoryDatabase
    {
        private readonly IDbConnection _connection;
    
        public InMemoryDatabase()
        {
            _connection = new SQLiteConnection("Data Source=:memory:");
        }
    
        public IDbConnection OpenConnection()
        {
            if (_connection.State != ConnectionState.Open)
                _connection.Open();
            return _connection;
        }
    
        public void Insert(string tableName, IEnumerable items)
        {
            var con = OpenConnection();
    
            con.CreateTableIfNotExists(tableName);
            con.InsertAll(tableName, items);
        }
    }
    

    I've created a DbColumnAttribute so we can specify a specific column name for a classes property.

    public sealed class DbColumnAttribute : Attribute
    {
        public string Name { get; set; }
    
        public DbColumnAttribute(string name)
        {
            Name = name;
        }
    }
    

    I added some IDbConnection extensions for the CreateTableIfNotExists and InsertAll methods.

    This is very rough so I've not mapped types correctly

    internal static class DbConnectionExtensions
    {
        public static void CreateTableIfNotExists(this IDbConnection connection, string tableName)
        {
            var columns = GetColumnsForType();
            var fields = string.Join(", ", columns.Select(x => $"[{x.Item1}] TEXT"));
            var sql = $"CREATE TABLE IF NOT EXISTS [{tableName}] ({fields})";
    
            ExecuteNonQuery(sql, connection);
        }
    
        public static void Insert(this IDbConnection connection, string tableName, T item)
        {
            var properties = typeof(T)
                .GetProperties(BindingFlags.Public | BindingFlags.Instance)
                .ToDictionary(x => x.Name, y => y.GetValue(item, null));
            var fields = string.Join(", ", properties.Select(x => $"[{x.Key}]"));
            var values = string.Join(", ", properties.Select(x => EnsureSqlSafe(x.Value)));
            var sql = $"INSERT INTO [{tableName}] ({fields}) VALUES ({values})";
    
            ExecuteNonQuery(sql, connection);
        }
    
        public static void InsertAll(this IDbConnection connection, string tableName, IEnumerable items)
        {
            foreach (var item in items)
                Insert(connection, tableName, item);
        }
    
        private static IEnumerable> GetColumnsForType()
        {
            return from pinfo in typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance)
                let attribute = pinfo.GetCustomAttribute()
                let columnName = attribute?.Name ?? pinfo.Name
                select new Tuple(columnName, pinfo.PropertyType);
        }
    
        private static void ExecuteNonQuery(string commandText, IDbConnection connection)
        {
            using (var com = connection.CreateCommand())
            {
                com.CommandText = commandText;
                com.ExecuteNonQuery();
            }
        }
    
        private static string EnsureSqlSafe(object value)
        {
            return IsNumber(value)
                ? $"{value}"
                : $"'{value}'";
        }
    
        private static bool IsNumber(object value)
        {
            var s = value as string ?? "";
    
            // Make sure strings with padded 0's are not passed to the TryParse method.
            if (s.Length > 1 && s.StartsWith("0"))
                return false;
    
            return long.TryParse(s, out long l);
        }
    }
    

    You can still use it the same way as @Mikhail mentions in Step 3.

提交回复
热议问题