phnx47 / dapper-repositories

CRUD for Dapper

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

SQLite UPDATE statement gives error: Result: near ".": syntax error. Maybe prefixed columns are not supported

vesko-k opened this issue · comments

Hi, at first I would to thank you for the library. It helped me a lot.
I have one question. In the SQL generator for the UPDATE statements you prefix columns with tablename. I'm using SQLite 3 and even in DBBrowser for SQLite it seems that database does not support prefixed columns for UPDATE statements.
I can create own SQLGenerator inheriting yours and to override the GetUpdate method and to clean the prefixes if the Prvoder is SQLite, but I'm not sure if I'm right.

Can you provide the generated query result?

I've already made custom SqlGenerator. Just removed prefixed with table name fields on update statement, when provider is SqLite. See GetFieldsUpdate.

  public class PrefixedSqlGenerator<TEntity> : SqlGenerator<TEntity> where TEntity : class
    {
        private static SqlProvider ResolveProvider(ShellSettings shellSettings)
        {
            switch (shellSettings["DatabaseProvider"])
            {
                case "SqlConnection":
                    return SqlProvider.MSSQL;
                case "Sqlite":
                    return SqlProvider.SQLite;
                case "MySql":
                    return SqlProvider.MySQL;
                case "Postgres":
                    return SqlProvider.PostgreSQL;
                default:
                    return SqlProvider.MSSQL;
            }
        }

        public PrefixedSqlGenerator(string tablePrefix, ShellSettings shellSettings)
            : base(ResolveProvider(shellSettings))
        {
            TableName = $"{tablePrefix}{TableName}";
        }

        #region SQLite UPDATE workaround
        private string CallableAppendJoinToUpdate<TBase>(TBase entity, SqlQuery originalBuilder, params Expression<Func<TEntity, object>>[] includes)
        {
            var mi = GetType().GetMethod("AppendJoinToUpdate", BindingFlags.NonPublic | BindingFlags.Instance);
            var gm = mi.MakeGenericMethod(typeof(TBase));
            return gm.Invoke(this, new object[] { entity, originalBuilder, includes }) as string;
        }

        public override SqlQuery GetUpdate(TEntity entity, params Expression<Func<TEntity, object>>[] includes)
        {
            if (Provider != SqlProvider.SQLite)
                return base.GetUpdate(entity, includes);

            var properties = SqlProperties.Where(p =>
                !KeySqlProperties.Any(k => k.PropertyName.Equals(p.PropertyName, StringComparison.OrdinalIgnoreCase)) && !p.IgnoreUpdate).ToArray();
            
            if (!properties.Any())
                throw new ArgumentException("Can't update without [Key]");

            if (HasUpdatedAt)
            {
                var attribute = UpdatedAtProperty.GetCustomAttribute<UpdatedAtAttribute>();
                var offset = attribute.TimeKind == DateTimeKind.Local
                    ? new DateTimeOffset(DateTime.Now)
                    : new DateTimeOffset(DateTime.UtcNow);
                if (attribute.OffSet != 0)
                {
                    offset = offset.ToOffset(TimeSpan.FromHours(attribute.OffSet));
                }

                UpdatedAtProperty.SetValue(entity, offset.DateTime);
            }

            var query = new SqlQuery();

            query.SqlBuilder
                .Append("UPDATE ")
                .Append(TableName)
                .Append(" ");

            if (includes?.Length > 0)
            {
                var joinsBuilder = CallableAppendJoinToUpdate(entity, query, includes);
                query.SqlBuilder.Append("SET ");
                query.SqlBuilder.Append(GetFieldsUpdate(TableName, properties, UseQuotationMarks));
                query.SqlBuilder.Append(joinsBuilder);
            }
            else
            {
                query.SqlBuilder.Append("SET ");
                query.SqlBuilder.Append(GetFieldsUpdate(TableName, properties, UseQuotationMarks));
            }

            query.SqlBuilder.Append(" WHERE ");

            query.SqlBuilder.Append(string.Join(" AND ", KeySqlProperties.Where(p => !p.IgnoreUpdate)
                .Select(p => $"{TableName}.{p.ColumnName} = @{entity.GetType().Name}{p.PropertyName}")));
            
            if (query.Param == null || !(query.Param is Dictionary<string,object> parameters))
                parameters = new Dictionary<string, object>();

            foreach (var metadata in properties.Concat(KeySqlProperties))
                parameters.Add($"{entity.GetType().Name}{metadata.PropertyName}", entity.GetType().GetProperty(metadata.PropertyName).GetValue(entity, null));
            
            query.SetParam(parameters);

            return query;
        }

        private string GetFieldsUpdate(string tableName, IEnumerable<SqlPropertyMetadata> properties, bool useMarks)
        {
            //return String.Join(", ", properties
            //    .Select(p => $"{tableName}.{(useMarks ? p.ColumnName : p.CleanColumnName)} = @{p.PropertyInfo.ReflectedType.Name}{p.PropertyName}"));
            return String.Join(", ", properties
                .Select(p => $"{(useMarks ? p.ColumnName : p.CleanColumnName)} = @{p.PropertyInfo.ReflectedType.Name}{p.PropertyName}"));
        }
        #endregion
    }

Same happened on Postgres and this fix also works for me.

@harshitgindra Thank you!

Published in 1.16.1