Created
August 29, 2018 01:46
-
-
Save maca134/26845631810b29b5e46cc8b5a0d5c366 to your computer and use it in GitHub Desktop.
SQLite table sql from POCO
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
namespace SQLiteTableBuilder | |
{ | |
[Table] | |
public class Group | |
{ | |
[Column(PrimaryKey = true, AutoIncrement = true)] | |
public int Id { get; set; } | |
[Column(Unique = true)] | |
public string Name { get; set; } | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System.Data.SQLite; | |
using System.IO; | |
namespace SQLiteTableBuilder | |
{ | |
internal class Program | |
{ | |
private static void Main(string[] args) | |
{ | |
const string dbFilePath = "./app.db"; | |
if (!File.Exists(dbFilePath)) SQLiteConnection.CreateFile(dbFilePath); | |
using (var dbConnection = new SQLiteConnection($"Data Source={dbFilePath};Version=3;")) | |
{ | |
dbConnection.Open(); | |
var tables = new[] | |
{ | |
new TableBuilder<Group>().ToSql(), | |
new TableBuilder<User>().ToSql() | |
}; | |
foreach (var sql in tables) | |
{ | |
var cmd = dbConnection.CreateCommand(); | |
cmd.CommandText = sql; | |
cmd.ExecuteNonQuery(); | |
} | |
} | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using System.Reflection; | |
using System.Text; | |
using System.Text.RegularExpressions; | |
namespace SQLiteTableBuilder | |
{ | |
public enum CollateType | |
{ | |
Binary, | |
Nocase, | |
Rtrim | |
} | |
public enum ForeignKeyOnClause | |
{ | |
SetNull, | |
SetDefault, | |
Cascade, | |
Restrict, | |
NoAction | |
} | |
[AttributeUsage(AttributeTargets.Property)] | |
public class ColumnAttribute : Attribute | |
{ | |
public string Name { get; set; } | |
public bool PrimaryKey { get; set; } | |
public bool AutoIncrement { get; set; } | |
public bool NotNull { get; set; } | |
public bool Unique { get; set; } | |
public string Default { get; set; } | |
public CollateType Collate { get; set; } = CollateType.Binary; | |
} | |
[AttributeUsage(AttributeTargets.Property)] | |
public class ForeignKeyAttribute : Attribute | |
{ | |
public Type Entity { get; set; } | |
public string Property { get; set; } | |
public ForeignKeyOnClause Delete { get; set; } = ForeignKeyOnClause.Restrict; | |
public ForeignKeyOnClause Update { get; set; } = ForeignKeyOnClause.Restrict; | |
} | |
[AttributeUsage(AttributeTargets.Property)] | |
public class IndexAttribute : Attribute | |
{ | |
public string[] OtherColumns { get; set; } | |
public bool Unique { get; set; } | |
} | |
[AttributeUsage(AttributeTargets.Class)] | |
public class TableAttribute : Attribute | |
{ | |
public string TableName { get; set; } | |
public TableAttribute() { } | |
public TableAttribute(string tableName) | |
{ | |
TableName = tableName; | |
} | |
} | |
public class TableBuilder<T> where T : new() | |
{ | |
private enum DataType | |
{ | |
Integer, | |
Text, | |
Blob, | |
Real, | |
Numeric | |
} | |
private class ForeignKey | |
{ | |
public string TableName { get; set; } | |
public string ColumnName { get; set; } | |
public ForeignKeyOnClause Delete { get; set; } | |
public ForeignKeyOnClause Update { get; set; } | |
} | |
private class Column | |
{ | |
public PropertyInfo PropertyInfo { get; } | |
public DataType DataType { get; } | |
public string Name { get; } | |
public bool PrimaryKey { get; } | |
public bool AutoIncrement { get; } | |
public bool NotNull { get; } | |
public bool Unique { get; } | |
public string Default { get; } | |
public CollateType Collate { get; } | |
public ForeignKey ForeignKey { get; } | |
public Column(PropertyInfo property) | |
{ | |
PropertyInfo = property; | |
DataType = TypeToDataType(PropertyInfo.PropertyType); | |
var columnAttribute = (ColumnAttribute)property.GetCustomAttribute(typeof(ColumnAttribute)); | |
PrimaryKey = columnAttribute.PrimaryKey; | |
AutoIncrement = columnAttribute.AutoIncrement; | |
NotNull = columnAttribute.NotNull; | |
Unique = columnAttribute.Unique; | |
Name = columnAttribute.Name ?? PropertyInfo.Name.ToLowerInvariant(); | |
Collate = columnAttribute.Collate; | |
Default = columnAttribute.Default; | |
var fkAttribute = (ForeignKeyAttribute)property.GetCustomAttribute(typeof(ForeignKeyAttribute)); | |
if (fkAttribute != null) | |
{ | |
ForeignKey = new ForeignKey | |
{ | |
TableName = GetTableName(fkAttribute.Entity), | |
ColumnName = fkAttribute.Property, | |
Delete = fkAttribute.Delete, | |
Update = fkAttribute.Update | |
}; | |
} | |
} | |
} | |
private class Index | |
{ | |
public Column[] Columns { get; } | |
public bool Unique { get; } | |
public Index(Column[] columns, bool unique) | |
{ | |
Columns = columns; | |
Unique = unique; | |
} | |
} | |
private class Table | |
{ | |
public string Name { get; } | |
public Column[] Columns { get; } | |
public Index[] Indices { get; } | |
public Table() | |
{ | |
var type = typeof(T); | |
Name = GetTableName(type); | |
Columns = type.GetProperties(BindingFlags.Instance | BindingFlags.Public) | |
.Where(property => property.GetCustomAttribute(typeof(ColumnAttribute)) != null) | |
.Select(property => new Column(property)).ToArray(); | |
Indices = Columns.Select(column => | |
{ | |
var index = (IndexAttribute)column.PropertyInfo.GetCustomAttribute(typeof(IndexAttribute)); | |
if (index == null) return null; | |
var columns = new List<Column> { column }; | |
columns.AddRange(index.OtherColumns.Select(name => Columns.First(col => col.Name == name.ToLowerInvariant()))); | |
return new Index(columns.ToArray(), index.Unique); | |
}).Where(i => i != null).ToArray(); | |
} | |
} | |
private readonly Table _table; | |
public TableBuilder() | |
{ | |
_table = new Table(); | |
} | |
public string ToSql() | |
{ | |
var tableSql = new StringBuilder(); | |
tableSql.Append("BEGIN;\n"); | |
tableSql.Append("CREATE TABLE IF NOT EXISTS "); | |
tableSql.Append($"\"{_table.Name}\" (\n"); | |
tableSql.Append(string.Join(",\n", _table.Columns.Select(column => | |
{ | |
var columnSql = new StringBuilder(); | |
columnSql.Append($"\"{column.Name}\" {column.DataType.ToString().ToUpperInvariant()}"); | |
if ( | |
!column.PrimaryKey && | |
!column.AutoIncrement && | |
!column.NotNull && | |
!column.Unique && | |
column.Default == null && | |
column.Collate == CollateType.Binary && | |
column.ForeignKey == null | |
) | |
{ | |
return columnSql.ToString(); | |
} | |
if (column.PrimaryKey) | |
{ | |
columnSql.Append(" PRIMARY KEY"); | |
if (column.AutoIncrement) | |
columnSql.Append(" AUTOINCREMENT"); | |
} | |
if (column.NotNull) | |
columnSql.Append(" NOT NULL"); | |
if (column.Unique) | |
columnSql.Append($" CONSTRAINT \"U_{column.Name}\" UNIQUE"); | |
if (column.Default != null) | |
columnSql.Append($" DEFAULT {column.Default}"); | |
if (column.Collate != CollateType.Binary) | |
columnSql.Append($" COLLATE {column.Collate.ToString().ToUpperInvariant()}"); | |
if (column.ForeignKey != null) | |
{ | |
var tableName = column.ForeignKey.TableName; | |
columnSql.Append($" CONSTRAINT \"FK_{column.Name}_{tableName}\" REFERENCES \"{tableName}\"(\"{column.ForeignKey.ColumnName}\")"); | |
columnSql.Append($" ON UPDATE {CamelCaseToSpaces(column.ForeignKey.Update.ToString()).ToUpperInvariant()}"); | |
columnSql.Append($" ON DELETE {CamelCaseToSpaces(column.ForeignKey.Delete.ToString()).ToUpperInvariant()}"); | |
} | |
return columnSql.ToString(); | |
}))); | |
tableSql.Append("\n);\n"); | |
tableSql.Append(string.Join("", _table.Indices.Select(index => | |
{ | |
var indexSql = new StringBuilder(); | |
indexSql.Append("CREATE "); | |
if (index.Unique) | |
indexSql.Append("UNIQUE "); | |
indexSql.Append($"INDEX IF NOT EXISTS \"I_{string.Join("_", index.Columns.Select(c => c.Name))}\""); | |
indexSql.Append($" ON \"{_table.Name}\" (\"{string.Join("\",\"", index.Columns.Select(c => c.Name))}\");\n"); | |
return indexSql.ToString(); | |
}))); | |
tableSql.Append("COMMIT;\n"); | |
return tableSql.ToString(); | |
} | |
private static DataType TypeToDataType(Type clrType) | |
{ | |
if (clrType == typeof(bool) || | |
clrType == typeof(byte) || | |
clrType == typeof(ushort) || | |
clrType == typeof(sbyte) || | |
clrType == typeof(short) || | |
clrType == typeof(int) || | |
clrType == typeof(uint) || | |
clrType == typeof(long) || | |
clrType == typeof(TimeSpan) || | |
clrType == typeof(DateTimeOffset) || | |
clrType.GetTypeInfo().IsEnum) | |
{ | |
return DataType.Integer; | |
} | |
if (clrType == typeof(float) || | |
clrType == typeof(double) || | |
clrType == typeof(decimal)) | |
{ | |
return DataType.Real; | |
} | |
if (clrType == typeof(string) || | |
clrType == typeof(StringBuilder) || | |
clrType == typeof(Uri) || | |
clrType == typeof(UriBuilder) || | |
clrType == typeof(Guid)) | |
{ | |
return DataType.Text; | |
} | |
if (clrType == typeof(DateTime)) | |
{ | |
return DataType.Numeric; | |
} | |
if (clrType == typeof(byte[])) | |
{ | |
return DataType.Blob; | |
} | |
throw new NotSupportedException("Don't know about " + clrType); | |
} | |
private static string GetTableName(MemberInfo table) | |
{ | |
var tableAttr = (TableAttribute)table.GetCustomAttribute(typeof(TableAttribute)); | |
if (tableAttr == null) | |
throw new Exception("class needs table attribute"); | |
return tableAttr.TableName ?? table.Name.ToLowerInvariant(); | |
} | |
private static string CamelCaseToSpaces(string input) | |
{ | |
return Regex.Replace(input, @"(\B[A-Z]+?(?=[A-Z][^A-Z])|\B[A-Z]+?(?=[^A-Z]))", " $1"); | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
namespace SQLiteTableBuilder | |
{ | |
[Table(Name = "user")] | |
public class User | |
{ | |
[Column(PrimaryKey = true, AutoIncrement = true)] | |
public int Id { get; set; } | |
[Column(NotNull = true), ForeignKey(Entity = typeof(Group), Property = nameof(Group.Id))] | |
public long GroupId { get; set; } | |
[Column, Index(Unique = true, OtherColumns = new[] { nameof(LastName) })] | |
public string FirstName { get; set; } | |
[Column] | |
public string LastName { get; set; } | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment