I know there are several question similar to mine.
butI don\'t think both of above
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.