• Как реализовать Fluent конструктор SQL запросов?

    могу предложить следующую реализацию:
    DynamicQuery
    public sealed class DynamicQuery
        {
            public static QueryResult GetDynamicQuery<T>(string tableName, Expression<Func<T, bool>> expression)
            {
                var queryProperties = new List<QueryParameter>();
                var body = (BinaryExpression)expression.Body;
                IDictionary<string, Object> expando = new ExpandoObject();
                var builder = new StringBuilder();
    
                WalkTree(body, ExpressionType.Default, ref queryProperties);
    
                // как вариант можно передавать набор полей или 
                // считывать их из атрибутов <T> передаваемого класса
                builder.Append("SELECT * FROM ");
                builder.Append(tableName);
                builder.Append(" WHERE ");
    
                for (int i = 0; i < queryProperties.Count(); i++)
                {
                    QueryParameter item = queryProperties[i];
    
                    if (!string.IsNullOrEmpty(item.LinkingOperator) && i > 0)
                    {
                        builder.Append(string.Format("{0} {1} {2} @{1} ", item.LinkingOperator, item.PropertyName, item.QueryOperator));
                    }
                    else
                    {
                        builder.Append(string.Format("{0} {1} @{0} ", item.PropertyName, item.QueryOperator));
                    }
                    expando[item.PropertyName] = item.PropertyValue;
                }
                return new QueryResult(builder.ToString().TrimEnd(), expando);
            }
    
            private static void WalkTree(BinaryExpression body, ExpressionType linkingType, ref List<QueryParameter> queryProperties)
            {
                if (body.NodeType != ExpressionType.AndAlso && body.NodeType != ExpressionType.OrElse)
                {
                    string propertyName = GetPropertyName(body);
                    dynamic propertyValue = body.Right;
                    string opr = GetOperator(body.NodeType);
                    string link = GetOperator(linkingType);
    
                    queryProperties.Add(new QueryParameter(link, propertyName, propertyValue, opr));
                }
                else
                {
                    WalkTree((BinaryExpression)body.Left, body.NodeType, ref queryProperties);
                    WalkTree((BinaryExpression)body.Right, body.NodeType, ref queryProperties);
                }
            }
    
            private static string GetPropertyName(BinaryExpression body)
            {
                string propertyName = body.Left.ToString().Split(new char[] { '.' })[1];
    
                if (body.Left.NodeType == ExpressionType.Convert)
                {
                    // hack to remove the trailing ) when convering.
                    propertyName = propertyName.Replace(")", string.Empty);
                }
                return propertyName;
            }
    
            private static string GetOperator(ExpressionType type)
            {
                switch (type)
                {
                    case ExpressionType.Equal:
                        return "=";
                    case ExpressionType.NotEqual:
                        return "!=";
                    case ExpressionType.LessThan:
                        return "<";
                    case ExpressionType.GreaterThan:
                        return ">";
                    case ExpressionType.AndAlso:
                    case ExpressionType.And:
                        return "AND";
                    case ExpressionType.Or:
                    case ExpressionType.OrElse:
                        return "OR";
                    case ExpressionType.Default:
                        return string.Empty;
                    default:
                        throw new NotImplementedException();
                }
            }
        }
    
        internal class QueryParameter
        {
            public string LinkingOperator { get; set; }
            public string PropertyName { get; set; }
            public object PropertyValue { get; set; }
            public string QueryOperator { get; set; }
    
            internal QueryParameter(string linkingOperator, string propertyName, object propertyValue, string queryOperator)
            {
                LinkingOperator = linkingOperator;
                PropertyName = propertyName;
                PropertyValue = propertyValue;
                QueryOperator = queryOperator;
            }
        }
    
        public class QueryResult
        {
            private readonly Tuple<string, dynamic> _result;
    
            public string Sql => _result.Item1;
    
            public dynamic Param => _result.Item2;
    
            public QueryResult(string sql, dynamic param)
            {
                _result = new Tuple<string, dynamic>(sql, param);
            }
        }



    пример использования:
    public class TestEntity
        {
            public int Id { get; set; }
            public string Name { get; set; }
            public string Caption { get; set; }
        }
    
    public virtual IEnumerable<T> FindById(int UserId)
    {
      IEnumerable<T> items;
      //возвращает QueryResult содержащий форматированный запрос вида:
      // SELECT * FROM testTable WHERE Id = @Id OR Name = @Name
      // и набор параметров 
      QueryResult dQuery = DynamicQuery.GetDynamicQuery<TestEntity>("testTable", t => t.Id == 23 || t.Name =="Test");
                using (IDbConnection cn = Connection)
                {
                    if (cn.State != ConnectionState.Open) cn.Open();
                    items = cn.Query<T>(dQuery.Sql, (object)dQuery.Param);
                }
      return items;
    }


    cn.Query<T> используется Dapper для получения данных в объект
    Ответ написан
    Комментировать
  • Почему использование триггера в mysql/oracle/mssql ... в web-программирование (и не только) считается признаком говнокода?

    igruschkafox
    @igruschkafox
    Специалист по сопровождению БД MS SQL
    потому что:

    - Трудно вносить изменения (изменилась процедура - а триггер на таблице работает по старой логике)
    - Трудно сопровождать (проапдейтил таблицу а сработал набор триггеров и в результате обновления справочника появляются различные документы)
    - логика системы должна быть в одном месте а не распихана по различным уголкам (обновление системы усложняется)
    - Лишние трудности при отладке (правильная работа процедуры или правильное обращение к базе данных вызывает ошибку - потому что триггер может не корректно отработать)
    - и все эти проблемы возводятся в квадрат когда один триггер вызывает срабатывание другого триггера на другой таблице!
    Ответ написан
    Комментировать
  • Перегрузки методов без дублирования кода в C#?

    Gorily
    @Gorily
    В случаях, когда код это позволяет, можно вынести основной функционал в один из методов (базовый для вас) и вызывать его из других перегрузок:
    string GenerateText(string text)
    {
     return GenerateText(text, "Без заголовка");
    }
    
    string GenerateText(string title, string text)
    {
     return title + Environment.NewLine + text;
    }

    Можно использовать необязательные аргументы, они как раз должны хорошо подойти в случае 1-3 различающихся параметров:
    string GenerateText(string title, string text = "Без заголовка")
    {
     return title + Environment.NewLine + text;
     }

    Никто не может запретить вам передавать вместо не используемых аргументов null и обрабатывать в коде:
    string GenerateText(string title, string text)
    {
     return title + Environment.NewLine + (text ?? "Без заголовка");
    }

    А в случае неопределённого числа однотипных аргументов используем params:
    string GenerateText(string title, params string[] text)
    {
     return title + Environment.NewLine + string.Join(Environment.NewLine, text);
    }

    Комбинируйте этот базовый набор и его должно быть вполне достаточно для аккуратного кода. И само собой, выделяйте повторяемый функционал в методы и классы для последующего повторного использования.
    Ответ написан
    Комментировать