CRUD operations

Suilder does not have inbuilt CRUD operations, but the following example shows a simple implementation.

Note

We assumed that the primary key is a single column. To execute the queries we are using Dapper, but you can use any other library.

Warning

This example may not give you the best performance because it uses reflection to support nested types and name translations.

public class CrudExample
{
    private IDbConnection con;

    private ISqlBuilder sql;

    private IEngine engine;

    public CrudExample(IDbConnection con, ISqlBuilder sql, IEngine engine)
    {
        this.con = con;
        this.sql = sql;
        this.engine = engine;
    }

    public T Get<T>(int id) where T : new()
    {
        IAlias<T> alias = sql.Alias<T>();

        // Get table info
        ITableInfo tableInfo = engine.GetInfo<T>();
        string primaryKey = tableInfo.PrimaryKeys[0];

        // Select query
        IQuery query = sql.Query
            .Select(alias.All)
            .From(alias)
            .Where(alias[primaryKey].Eq(id));

        // Compile an execute
        QueryResult result = engine.Compile(query);

        // We can have multiple properties mapped to the same column
        ILookup<string, string> columnsLookup = tableInfo.ColumnNamesDic
            .ToLookup(x => x.Value.ToLower(), x => x.Key);

        // Map result
        return Map<T>(con.QuerySingleOrDefault(result.Sql, result.Parameters), columnsLookup);
    }

    public IEnumerable<T> GetAll<T>() where T : new()
    {
        IAlias<T> alias = sql.Alias<T>();

        // Get table info
        ITableInfo tableInfo = engine.GetInfo<T>();

        // Select query
        IQuery query = sql.Query
            .Select(alias.All)
            .From(alias);

        // Compile an execute
        QueryResult result = engine.Compile(query);

        // We can have multiple properties mapped to the same column
        ILookup<string, string> columnsLookup = tableInfo.ColumnNamesDic
            .ToLookup(x => x.Value.ToLower(), x => x.Key);

        // Map result
        return con.Query(result.Sql, result.Parameters).Cast<IDictionary<string, object>>()
            .Select(x => Map<T>(x, columnsLookup));
    }

    private T Map<T>(IDictionary<string, object> row, ILookup<string, string> columnsLookup)
        where T : new()
    {
        T entity = new T();
        foreach (var column in row)
        {
            foreach (var property in columnsLookup[column.Key.ToLower()])
            {
                SetValue(entity, property, column.Value);
            }
        }

        return entity;
    }

    public T Insert<T>(T entity)
    {
        IAlias<T> alias = sql.Alias<T>();

        // Get table info
        ITableInfo tableInfo = engine.GetInfo<T>();
        string primaryKey = tableInfo.PrimaryKeys[0];

        // Custom metadata
        bool autoincrement = tableInfo.GetMetadata<bool>(primaryKey, "Autoincrement");

        IInsert insert = sql.Insert().Into(alias);
        IValList values = sql.ValList;

        // We can have multiple properties mapped to the same column
        ISet<string> columnNamesAdded = new HashSet<string>();

        // Get columns
        foreach (string column in tableInfo.Columns)
        {
            // Skip primary key if is auto increment
            if (autoincrement && column == primaryKey)
                continue;

            // Skip duplicate property
            string columnName = tableInfo.GetColumnName(column);
            if (columnNamesAdded.Contains(columnName))
                continue;
            else
                columnNamesAdded.Add(columnName);

            // Add column and value
            insert.Add(alias[column]);
            values.Add(GetValue(entity, column));
        }

        // Insert query
        IQuery query = sql.Query.Insert(insert).Values(values);

        // Compile and execute
        QueryResult result = engine.Compile(query);
        con.Execute(result.Sql, result.Parameters);

        // Get primary key if is auto increment
        if (autoincrement)
        {
            query = sql.Query.Select(SqlFn.LastInsertId());

            // Compile and execute
            result = engine.Compile(query);
            int valuePK = con.ExecuteScalar<int>(result.Sql, result.Parameters);

            // Update property
            SetValue(entity, primaryKey, valuePK);
        }

        return entity;
    }

    public T Update<T>(T entity)
    {
        IAlias<T> alias = sql.Alias<T>();

        // Get table info
        ITableInfo tableInfo = engine.GetInfo<T>();
        string primaryKey = tableInfo.PrimaryKeys[0];

        // Update query
        IQuery query = sql.Query.Update()
            .From(alias)
            .Where(alias[primaryKey].Eq(GetValue(entity, primaryKey)));

        // We can have multiple properties mapped to the same column
        ISet<string> columnNamesAdded = new HashSet<string>();

        // Get columns
        foreach (string column in tableInfo.Columns)
        {
            // Skip primary key
            if (column == primaryKey)
                continue;

            // Skip duplicate property
            string columnName = tableInfo.GetColumnName(column);
            if (columnNamesAdded.Contains(columnName))
                continue;
            else
                columnNamesAdded.Add(columnName);

            // Set value
            query.Set(alias[column], GetValue(entity, column));
        }

        // Compile and execute
        QueryResult result = engine.Compile(query);
        con.Execute(result.Sql, result.Parameters);

        return entity;
    }

    public void Delete<T>(T entity)
    {
        IAlias<T> alias = sql.Alias<T>();

        // Get table info
        string primaryKey = engine.GetInfo<T>().PrimaryKeys[0];

        // Delete query
        IQuery query = sql.Query
            .Delete()
            .From(alias)
            .Where(alias[primaryKey].Eq(GetValue(entity, primaryKey)));

        // Compile and execute
        QueryResult result = engine.Compile(query);
        con.Execute(result.Sql, result.Parameters);
    }

    private object GetValue(object obj, string property)
    {
        foreach (string prop in property.Split('.'))
        {
            obj = obj.GetType().GetProperty(prop).GetValue(obj);

            if (obj == null)
                return null;
        }

        return obj;
    }

    private void SetValue(object obj, string property, object value)
    {
        string[] properties = property.Split('.');

        // Initialize nested objects
        for (int i = 0; i < properties.Length - 1; i++)
        {
            PropertyInfo propertyInfo = obj.GetType().GetProperty(properties[i]);

            object nestedObj = propertyInfo.GetValue(obj);

            if (nestedObj == null)
            {
                nestedObj = Activator.CreateInstance(propertyInfo.PropertyType);
                propertyInfo.SetValue(obj, nestedObj);
            }

            obj = nestedObj;
        }

        // Set value
        PropertyInfo propertyInfoValue = obj.GetType().GetProperty(properties[properties.Length - 1]);
        propertyInfoValue.SetValue(obj, Convert.ChangeType(value, propertyInfoValue.PropertyType));
    }
}