80LevelElf about IT Записи Мои проекты Обо мне
Скажите, часто ли у вас возникала такая ситуация: - Какая-то часто используемая сущность хранится в таблице (_какой сюрприз!_) - Меняется это сущность из многих и многих мест. Например, в одном месте мы обновляем название компании, в другом - её рейтинг, в третьем - название и адрес, в четвертом... Как делать такой слой хранения данных? Много маленьких методов? Как-то проверять данные на null? А если нам **реально** нужно изменить данные на null? - А еще бывают случаи, когда при сложной логике вы **заранее не знаете, что именно обновится**, а дергать миллион маленьких методов или как-то еще сильно смешивать слои логики и хранения данных не хочется. - А еще бывают микросервисы, которые по-хорошему должны относиться друг к другу как к **черному ящику**, который и упасть может. В таком случае вы получите наполовину обновленные данные. Короче, я думаю вы узнали себя и то неприятное чувство, когда приходится идти на стремный компромисс. Я на работе предложил попробовать одну маленькую прослоечку. Вроде внедрили в некоторых местах и пока полет нормальный, так что расскажу о ней и вам. public class Updation<T> { public Updation(T value) { Value = value; } public T Value { get; set; } public static implicit operator Updation<T> (T value) { return new Updation<T> ( value); } public static implicit operator T(Updation<T> Updation) { return Updation.Value; } } Вот такая вот маленькая прослочека. Работает она по следующей логике: - Если нужно обновить данные, то присваиваете новое значение. Даже, если новое значение null. - Если данные обновлять не нужно, то просто присвойте самому Updation значение null И маленький пример: public class CompanyUpdation { public Updation<string> Name {get;set;} public Updation<double> Rating {get;set;} public Updation<string> Address {get;set;} } // Где-то в коде var updateCompany = new CompanyUpdation { Name = "new name", Rating = 5.0, } Данный объект обновления будет обозначать, что вы хотите обновить имя и рейтинг компании, но не хотите трогать адрес (его значение останется старым). ### Как реализовать это в своем DAL? - Если вы используете какие-то ORM-ки для генерации SQL типа Entity, Linq2Sql, Linq2Db - то все просто. Маленький if **в одной функции обновления** типа: ``` if (companyUpdation.Name != null) query.Set(i => i.Name = companyUpdation.Name.Value); ``` - Если вы генерируете SQL прямо в системе, то проблем опять же возникнуть не должно. Более того, вы можете сделать так, что любой Updation класс будет преобразовываться в SQL с помощью щепотки фантазии и рефлексии. - А вот если вы используете хранимые процедуры (stored procedures), то остается только страдать. ### Какие есть неочевидные плюсы такого подхода? - Он выдает очень красивый JSON. Например запрос на обновление имени компании будет выглядеть так: { "Name": { "Value": "new name" } } На мой взгляд, очень недурно и не вызывает лишних вопросов. - Updation умеет работать с Nullable типами **из коробки**: double? rating = MaybeChangeRating(); // другая запись Nullable<double> var updateCompany = new CompanyUpdation { Rating = rating }; // Если rating равен 1.0, то мы получим updateCompany.Rating равный Updation(1.0) // Если rating равен null, то мы получим updateCompany.Rating равный null (то есть "без обновления") - Метод подходит как для обновления базы данных, так и для того, чтобы быть вынутым снаружу REST API. Короче, мы написали аналог [GraphQL](http://graphql.org/learn/) для бедных. Кстати почитайте что это - может быть GraphQl окажется для вашей системы куда более интересным выходом из положения (особенно если вам нужен в первую очередь именно умный REST API). У себя мы используем такой подход как для передачи данных между микросервисами, так и для обновления данных в базе.
(04.04.2018)

blog comments powered by Disqus