Выбранная вами абстракция может в итоге не покрыть всех требований к хранению данных, и вам придется с этим жить.
Абстракцию я не выбираю, а проектирую. А вот СУБД мы выбираем. Это не просто игра слов — это огромная разница. Вы не можете вмешаться в работу СУБД, но можете вмешаться в абстракцию, если потребуется. А потребуется это в случае плохого проектирования или серьезного изменения требований. В большинстве случаев она будет просто расширяться.
Нет. Вы исходите из того, что, что удастся сменить БД без смены абстрации (сюрприз: нет, не удастся).
Удастся, если абстракция хорошо проработана. предположим, у вас есть сущность, поля которой не все фиксированные. Часть из них управляются администратором системы через конфиг.
Так вот, ваша доменная сущность должна выглядеть, так, как она бы выглядела, если бы сущность просто хранилась в памяти ну и чтобы в логике было удобно с ней работать. И абстрактный репозиторий будет работать с ней (именно с доменным типом). А ее модель БД уже может зависеть от конкретного выбора СУБД. если это PostgreSQL с его JsonB полями, то весь ваш набор динамических полей ляжет в одно единственное строковое поле и данные там будут в синтаксисе Json. Если же ваша СУБД такую динамику не поддерживает, вам, возможно, надо будет создавать отдельные таблицы для значений под каждый тип данных. То есть ваш репозиторий под капотом может содержать адаптер. Но какое дело слою логики до этого? А может вы под такую сущность решите использовать NoSQL. Опять же это будут детали реализации.
Я об этом и говорю: вы постулируете, что можно сделать идеальную неизменную абстракцию. Но на самом деле — нельзя.
Мы вообще живем не в идеальном мире. Если вы где то ошиблись при проектировании, у вас есть возможность расширить вашу абстракцию, добавив в нее новый метод. Старый будем считать устаревшим, пометим так, как этого требует ваш ЯП и в дальнейшем постепенно выпилим.
Они всегда просачиваются.
Это от недостатка фантазии, смекалки и переизбытка опыта проектирования через БД.
Однажды группе художников дали задание нарисовать каких либо невиданных монстров. Они нарисовали всякого, кто на что горазд. Но у всех монстров были в том или ином виде ноги/руки/лапы/крылья/хвосты и рты/пасти.
Дадада, моему слою логики совершенно не обязательно знать, можно ли в один запрос найти все issues, которые используют вот такой файл в файловом хранилище, или надо перебрать все issue, и для каждого запросить файл.
Конечно не обязательно. Ваш абстрактный репозиторий будет в слое логики, а вот его реализация в инфраструктуре. Так что да, ваш репозиторий будет либо принимать очень универсальные фильтры, либо иметь конкретный метод Issue[] GetIssuesThatContainsFile(file). А вот в реализации он уже может хоть через 1 запрос делать, хоть через пачку.
И чем это отличается от «лучше не менять БД, и проектировать систему таким образом, чтобы менять БД не требовалось»?
Тем что выбранная вами БД может в итоге не покрыть всех требований к хранению данных и вам придется с этим жить.
Так я же ровно про это и говорю: подход «давайте спрячем БД за абстракцией» создает иллюзию, что сменить БД будет дешево, хотя на самом деле — не будет.
Нет, в случае с абстракцией сменить БД будет дешевле, потому что у вас будет N абстрактных методов работы с данными и каждый метод будет использован в логике M раз. Итого, с абстракцией вам придется менять реализацию N методов, а без нее — N*M.
… только в случае с абстракциями мне, возможно, понадобится менять еще и абстракции. Так зачем мне этот дополнительный уровень геморроя?
Вряд ли. Если логика вашего приложения обращается к абстракции для сохранения или получения каких либо данных — это не изменится. Главное, чтобы особенности хранения не просачивались наружу.
Поймите, ваши данные не обязаны храниться в одном месте. Они вообще могут храниться и даже не в БД, а в стороннем сервисе. Вы можете использовать сущность Issue, которую будете брать/создавать в Redmine и ваш слой логики не обязан об этом знать. То же касается файлов. Хотите — храните в БД, хотите — на диске, хотите — в стороннем файловом хранилище. Вам будет намного проще управляться с этим, когда это будет в инфраструктуре, а не в логике.
Верно. Поэтому лучше ее не менять. И проектировать систему таким образом, чтобы менять ее не требовалось, но была возможность расширить или добавить еще одну абстракцию.
А вообще, проблема изменения требований есть в любом подходе. Ну или скажите, при каком это будет дешево? Пусть вы все еще работаете отталкиваясь от БД. Вам в любом случае придется менять схему БД, поддерживать ее в приложении и переписывать логику. По объему выглядит как и в случае с абстракциями.
И если например бизнес логика навязывает один способ хранения данных как подходящий
Подождите, вы же в статье пишете «БД — это детали реализации».
Так в том и дело, что мы в конце (ну или на каком то этапе), основываясь на сформированном бизнес слое можем понять, что с реляцией нам работать не удобно или наоборот. Так что да, БД — это детали реализации. В противном случае, вы в какой то момент понимаете, что реляционная БД, которую вы выбрали на этапе проектирования, оказывается неприменима в данном проекте (или применима с костылями). И что тогда? Говорить заказчику, что «вот это вот» требование мы не сможем реализовать в полной мере? Или реализуем, но оно будет жутко тормозить?
Резюмирую. Работаем с данными через абстракцию. Реализуем исходя из наших потребностей. У нас вообще в конечном счете может получиться комбинированное хранилище (реляция, документ, граф, файловая система).
Ну данная статья не про прекрасное. Инженеры допускают те или иные ошибки постоянно. Но если код хоть как то работает и не крашится в позитивных кейсах, то пусть пока будет так, со временем появится понимание внутреннего устройства и это пройдет. Старый код постепенно будет улучшаться, а новый будет писаться изначально лучше. Только вот прежде чем код будет хоть как то работать, надо его хоть как то написать. Для этого и нужна была статья. Просто помочь подключить, настроить, завести.
Спасибо за разъяснение. Контекст базы не надо регистрировать как синглтон. По умолчанию он регистрируется как скопд. И это оптимально. В редких случаях можно как трансиент.
Это кратко, без обоснования.
Имхо, когда читал статью, думал какой-то очередной студент получил инвайт. Получив первый работающий проект, пошёл учить школьников.
Поработав с молодыми разработчиками, которые только пробуют на вкус EF, я выделил ряд проблем, с которыми они сталкиваются. Встретившись с ним в бою, они получают много разрозненной информации и я попытался точечно заткнуть дыры, чтобы сформировать в их головах полноценный граф зависимостей и параллельно перевернуть взгляд на построение запросов.
Эта статья не нацелена на старт с нуля, для этого есть метанит и профессор веб.
Эта статья не нацелена на профессионалов. Для этого будут следующие статьи. Хотя, наверное не будут. Уже камнями закидали :)
Эта статья нацелена на тех, кто столкнулся с EF на уже действующем проекте.
То есть было бы интересно получить фидбек именно от этих людей. Вместо этого я нахватал минусов от гуру, которые должны были покинуть статью сразу после
Пришел человек на проект, впервые увидел там EF, подивился такому чуду, научился пользоваться, решил использовать в личных целях. Создал новый проект,… А дальше что делать?
Ну или дочитать, но при этом четко понять, для кого это написано. А то получается минусов накидали, а цель не поняли :)
Но это, конечно, моя вина. Я забыл, что технари прямолинейны как прямая линия и писать между строк не стоит.
Интересно, как так выходит, что люди начинают работать с EF Core так, как Вы описываете в статье в начале?
Ведь это же банальные основы работы с EF Core, и не узнав их, к работе не приступить.
Еще как приступить. Вы приходите на проект молодым разработчиком, про EF только слышали, вам дают задачу и вы ее выполняете. Где-то спрашиваете, где-то смотрите аналоги. Ну и так как с LINQ вы уже знакомы, а большинство запросов все таки простые, у вас все получается и вы думаете, что освоили EF. Но как только надо поднять все с нуля или выполнить сложный запрос, начинаются проблемы.
И их узнать нужно через гугл. А автор статьи против гугла, что ли? Не понял этого момента.
Не против. Просто гуглить можно до опупения, если не знать что искать. Поэтому я подсказываю, как и что нагуглить.
Transient же не означает, что сервис нужно обязательно создавать
Наверное да. Но я чет уже боюсь делать поспешные выводы, как следует не обсосав это со всех сторон.
Да, так можно, но это не будет работать в за пределами ASP.NET
а мне и не надо. Самое главное здесь — IDecorator, а фабрика — это уже инфраструктура. Для нового типа приложения фабрику можно и переопределить. Можно же будет скоупами вручную управлять. Во всяком случае я сейчас не могу угадать применение скоупов в другом типе приложения. Оно может быть различным. Соответственно фабрика должна будет это все учесть.
то есть для тестов или фоновых задач придётся прикручивать дальнейшие костыли.
Не костыли, а новые архитектурные решения :)
С тестами проблем нет и не предвидится. В юнит тестах я просто мокаю NextDelegate. Слава богу это свойство с публичным сеттером. В юнит тестах вообще DI не нужен я считаю. Единственная неприятность, что не имея аргументов конструктора, я не узнаю сразу, что мне нужно эту фабрику мокать. Но щито поделать.
В интеграционных тестах у меня поднимается сервер на основе приложения с возможностью переопределить стартап и еще кое что, после чего на основе него же поднимается клиент. А значит идут полноценные запросы, а значит у меня есть HttpContext.
А для фоновых задач да, придется немного запотеть. В первую очередь перед началом фоновой задачи надо будет создать скоуп. Что делать в фабрике, пока не придумал. Надо что-то сделать после проверки контекста на null (если он null).
И вообще, не просто так доступ к HttpContext в Core запрятали в интерфейс.
Если бы его хотели запрятать, запрятали бы подальше. Вообще, особых предостережений по его использованию я не нашел. Пока что я вижу причину выноса его в интерфейс для возможности подменить реализацию, а так же из-за вариативности возвращаемых значений.
Вы же сами сказали, что без декораторов-синглтонов вы как-то всё это время обходились, так что ничего вы не потеряете.
но не без декораторов скопед над трансиент сервисами. Такое уже используется и рефакторить не хочется.
Зато код всех существующих декораторов с Next будет заметно проще, чем с NextFactory. Ну вот сравните:
Верно, но ответ на прошлое утверждение не позволяет этого.
Кроме того, даже с NextFactory вы не сможете «восстановить» созданный ASP.NET скоуп, который «потеряется» при вызове синглтона что бы вы ни делали.
Я не уверен на все 100, но можно попробовать через рутовый провайдер получить IHttpContextAccessor (который, кстати, синглтон) вытянуть текущий контекст и из него получить провайдер текущего запроса, который, если я все правильно понял, порожден из текущего скоупа запроса.
Вообще то нет. Если вам потребовалось менять контейнер, скорее всего, вы хотите пользоваться расширенным функционалом. И не так то просто взять и заменить многочисленные регистрации и целые фабрики сервисов на синтаксис нового контейнера.
Позднее заменю NextDelegate на NextFactory и создам полноценную фабрику для следующего декоратора/сервиса.
Если заменю просто на Next, потеряю возможность понижать время жизни.
Да и не переписать его нужно, а дописать. Причем немного.
Зачем столько негатива в комментариях? Вы помогли разобраться в некоторых вещах, и за это большое вам спасибо. Вы и на SO мне немало помогали. Я много в чем сумел разобраться на старте своей карьеры, благодаря вашим и еще кое-чьим ответам. Более того, научился разбираться самостоятельно. Не надо сейчас это портить.
Экспертное мнение принесет больше пользы и меньше отторжения, если оно будет без эмоций, преувеличений и содержать только факты.
Конечно, вы сейчас активно выискиваете недостатки.
Это вы, кстати, делаете.
Но в вашем подходе их и искать не надо, они сразу на виду.
И дальше в OnInvoke
if (instance is IAspectLoggable loggable) { loggable.logger.log(...); }
Скажите, если ваш сервис ILoggable, IAuditable, IЕщеЧтоНибудь, то зачем вам все эти навороты? Делайте просто явный код, это будет проще и читаемо. И никакой АОП тут не нужен.
АОП-подход для инфраструктурного кода, который принципиально не зависит от сигнатур функций.
Я не вижу в аббревиатуре АОП букву И(инфраструктура). Следовательно использую понятие аспектов на всю катушку. Я не вижу причин, по которым я не могу связать аспекты с предметной областью. Точно так же как я связываю с предметкой объекты.
т.е. где-то в web.config можно давать разные уровни логирования для разных классов
Веб конфиг вообще не должен ничего знать про ваши классы. Как и администратор системы, который настраивает логирование.
Я ведь писал в статье, что хочу как разработчик и как проектировщик. А вы пытаетесь мне сказать что я не это хочу. И что я вовсе не это хочу.
Попытайтесь понять, что все эти декораторы находятся как бы сбоку и отдельно разруливается порядок их выполнения. То есть, когда я пишу БЛ, я не хочу знать, что чего то там еще будет логироваться. А завтра еще навешают замеры скорости. А послезавтра… И самое главное, все эти декораторы можно писать одновременно, не боясь напороться на конфликты при слиянии. И за все это приходится платить парой лишних методов в декораторах, которые просто указывают на следующий слой и ничего не делают.
И еще, я не знаю, кто именно меня минусует. Если вдруг это вы, пожалуйста не делайте это. Такими вещами должен заниматься сторонний наблюдатель. Я ведь не совсем откровенную чушь пишу, я надеюсь :)
Отлично. После этого я меняю определение метода на такое
int test(int a, string c, string b = null)
и по запаре или так как я впервые вижу этот проект забываю изменить код АОПовской функции. Никакие связи никогда не приведут меня в OnInvoke. В вашем примере, конечно, это ничего не испортит, но так ведь никто не логирует. У нас же бизнес задача, и лог должен быть соответствующий, каждый параметр несет в себе какую-то информацию, а не просто голый текст и числа. То есть у нас будет логирование через индексацию (p[0], p[1] вместо foreach). Вместо параметра b в p[1] теперь упадет c, который имеет тот же тип данных. Логи мы обычно читаем, когда что-то идет не так и спустя полгода (или сколько там ваше приложение работает без сбоев) вы обнаружите, что логи то не информативные. И, казалось бы, что тут такого. Меняю OnInvoke, перезапускаю, вижу верные логи, профит, ничего страшного. Но что если это не просто логи, а аудит безопасности? И залезли вы в них, чтобы узнать кто из пользователей мог слить инсайдерскую информацию.
Кстати, я видел реализацию АОП и поинтереснее, у которой проблем с типизацией не было. Она требует определять сквозные методы с теми же параметрами, что и основной метод. В этом случае вы получите исключение уже при запуске приложения.
Да абсолютно неважно. Пусть будет 3 метода и 5 классов, или 1 метод, но 15 классов — бойлерплейта столько же.
Важно. 3 из 5 классов вам вообще не придется оборачивать, там не нужно никакого логирования. Зато нужна какая-нибудь другая обертка. В случае с 1 же классом у вас было бы 2 обертки, у которых действительно очень много неиспользуемых методов.
А если будет 15 классов по 1 методу, так тут вообще все точечно. Вы ведь точно знаете, какие методы чем должны быть обернуты, так что декораторов будет ровно столько, сколько необходимо.
Да и к тому же, неиспользуемые методы дают разве что визуальный мусор. Они не внесут никаких непоняток.Разработчик увидит, что обертка для транзакций не открывает транзакцию на чтение данных из БД и у него не возникнет вопросов.
Так же, они не создадут проблем безопасности, которые я описал выше.
А как обстоят дела с DI в классы, где будут ваши методы АОПовские?
Тут AOP вообще никак не влияет. Если инъекция через поля-свойства, код не меняется, если через конструктор, все аргументы прозрачно пробрасываются (и то, если захотим оборачивать конструктор, а вообще конструктор как правило не оборачивается, нет смысла)
Что значит прозрачно пробрасываются? Вот вы в предложенном методе просто пишите в консоль. Измените метод так, чтобы лог писался через ILogger.Log. И покажите, откуда вы возьмете экземпляр логгера.
Верно. Но в большинстве случаев, если у вас такой большой сервис, его лучше декомпозировать, так как он выполняет слишком много задач. God сервис такой получается.
А с AOP можно сдедать универсальный логгер, который можно навесить абсолютно к любому сервису одной строчкой (атрибут на класс-реализацию сервиса)
Да да, и при этом надо позаботиться, чтобы в ваш сквозной метод пришли все необходимые параметры. Будем принимать массив и разбирать по ходу? Явно нет. Создадим метод, который будет 1 в 1 как требуемый? Ну так вам придется поддерживать сразу в нескольких местах. При изменении определения метода, все сквозные надо будет так же не забыть поменять, а если забудете, то узнаете об этом в рантайме и если повезет, то сразу. Причем еще потребуется магия с инжектом в IL. Да еще и с отладкой бывают интересные штуки. А как обстоят дела с DI в классы, где будут ваши методы АОПовские?
Кстати да. Хоть в нем и 5 строк полезного кода, надо его добавить.
Не менять NextDelegate после создания объекта. И использовать строго провайдер самого объекта для резолва его зависимостей, а не какой-то внешний.
Но тогда возникнет описанная вами ранее проблема. Провайдер для синглтона будет из рута.
А ведь именно синглтонами было обосновано такое странное решение с NextDelegate. Для Scoped и Transient-обёрток достаточно простого Next.
Да, интересная ситуация. Это потому, что необходимый вспомогательный функционал с долгой инициализацией, зареганный как синглтон, подключается через конструктор, а не как декоратор. :D
В таком случае у вас в каждом из синглтонов — гонка
Да, я пока все это писал, уже понял, что гонка.
Это настолько глупое решение, что я даже не мог предположить что его кто-то сделает.
Воу воу, палехче. Тут не надо ничего предполагать, код то перед глазами. Оказалось, в него надо было посмотреть чуть внимательнее и мне и вам. В итоге, в результате дискуссии, был найден изъян и это замечательно. Подумаю, как устранить гонку, главное, что она локализована.
Сочувствую.
Слава богу, никто до сих пор не регал синглтон декораторы. А понижение со скоупд на трансиент не грозит гонкой в данном случае. :)
Удастся, если абстракция хорошо проработана. предположим, у вас есть сущность, поля которой не все фиксированные. Часть из них управляются администратором системы через конфиг.
Так вот, ваша доменная сущность должна выглядеть, так, как она бы выглядела, если бы сущность просто хранилась в памяти ну и чтобы в логике было удобно с ней работать. И абстрактный репозиторий будет работать с ней (именно с доменным типом). А ее модель БД уже может зависеть от конкретного выбора СУБД. если это PostgreSQL с его JsonB полями, то весь ваш набор динамических полей ляжет в одно единственное строковое поле и данные там будут в синтаксисе Json. Если же ваша СУБД такую динамику не поддерживает, вам, возможно, надо будет создавать отдельные таблицы для значений под каждый тип данных. То есть ваш репозиторий под капотом может содержать адаптер. Но какое дело слою логики до этого? А может вы под такую сущность решите использовать NoSQL. Опять же это будут детали реализации.
Мы вообще живем не в идеальном мире. Если вы где то ошиблись при проектировании, у вас есть возможность расширить вашу абстракцию, добавив в нее новый метод. Старый будем считать устаревшим, пометим так, как этого требует ваш ЯП и в дальнейшем постепенно выпилим.
Это от недостатка фантазии, смекалки и переизбытка опыта проектирования через БД.
Однажды группе художников дали задание нарисовать каких либо невиданных монстров. Они нарисовали всякого, кто на что горазд. Но у всех монстров были в том или ином виде ноги/руки/лапы/крылья/хвосты и рты/пасти.
Конечно не обязательно. Ваш абстрактный репозиторий будет в слое логики, а вот его реализация в инфраструктуре. Так что да, ваш репозиторий будет либо принимать очень универсальные фильтры, либо иметь конкретный метод Issue[] GetIssuesThatContainsFile(file). А вот в реализации он уже может хоть через 1 запрос делать, хоть через пачку.
Нет, в случае с абстракцией сменить БД будет дешевле, потому что у вас будет N абстрактных методов работы с данными и каждый метод будет использован в логике M раз. Итого, с абстракцией вам придется менять реализацию N методов, а без нее — N*M.
Вряд ли. Если логика вашего приложения обращается к абстракции для сохранения или получения каких либо данных — это не изменится. Главное, чтобы особенности хранения не просачивались наружу.
Поймите, ваши данные не обязаны храниться в одном месте. Они вообще могут храниться и даже не в БД, а в стороннем сервисе. Вы можете использовать сущность Issue, которую будете брать/создавать в Redmine и ваш слой логики не обязан об этом знать. То же касается файлов. Хотите — храните в БД, хотите — на диске, хотите — в стороннем файловом хранилище. Вам будет намного проще управляться с этим, когда это будет в инфраструктуре, а не в логике.
А вообще, проблема изменения требований есть в любом подходе. Ну или скажите, при каком это будет дешево? Пусть вы все еще работаете отталкиваясь от БД. Вам в любом случае придется менять схему БД, поддерживать ее в приложении и переписывать логику. По объему выглядит как и в случае с абстракциями.
Резюмирую. Работаем с данными через абстракцию. Реализуем исходя из наших потребностей. У нас вообще в конечном счете может получиться комбинированное хранилище (реляция, документ, граф, файловая система).
Это кратко, без обоснования.
Все почти так и было xD
Эта статья не нацелена на старт с нуля, для этого есть метанит и профессор веб.
Эта статья не нацелена на профессионалов. Для этого будут следующие статьи. Хотя, наверное не будут. Уже камнями закидали :)
Эта статья нацелена на тех, кто столкнулся с EF на уже действующем проекте.
То есть было бы интересно получить фидбек именно от этих людей. Вместо этого я нахватал минусов от гуру, которые должны были покинуть статью сразу после
Ну или дочитать, но при этом четко понять, для кого это написано. А то получается минусов накидали, а цель не поняли :)
Но это, конечно, моя вина. Я забыл, что технари прямолинейны как прямая линия и писать между строк не стоит.
Еще как приступить. Вы приходите на проект молодым разработчиком, про EF только слышали, вам дают задачу и вы ее выполняете. Где-то спрашиваете, где-то смотрите аналоги. Ну и так как с LINQ вы уже знакомы, а большинство запросов все таки простые, у вас все получается и вы думаете, что освоили EF. Но как только надо поднять все с нуля или выполнить сложный запрос, начинаются проблемы.
Не против. Просто гуглить можно до опупения, если не знать что искать. Поэтому я подсказываю, как и что нагуглить.
а мне и не надо. Самое главное здесь — IDecorator, а фабрика — это уже инфраструктура. Для нового типа приложения фабрику можно и переопределить. Можно же будет скоупами вручную управлять. Во всяком случае я сейчас не могу угадать применение скоупов в другом типе приложения. Оно может быть различным. Соответственно фабрика должна будет это все учесть.
Не костыли, а новые архитектурные решения :)
С тестами проблем нет и не предвидится. В юнит тестах я просто мокаю NextDelegate. Слава богу это свойство с публичным сеттером. В юнит тестах вообще DI не нужен я считаю. Единственная неприятность, что не имея аргументов конструктора, я не узнаю сразу, что мне нужно эту фабрику мокать. Но щито поделать.
В интеграционных тестах у меня поднимается сервер на основе приложения с возможностью переопределить стартап и еще кое что, после чего на основе него же поднимается клиент. А значит идут полноценные запросы, а значит у меня есть HttpContext.
А для фоновых задач да, придется немного запотеть. В первую очередь перед началом фоновой задачи надо будет создать скоуп. Что делать в фабрике, пока не придумал. Надо что-то сделать после проверки контекста на null (если он null).
Если бы его хотели запрятать, запрятали бы подальше. Вообще, особых предостережений по его использованию я не нашел. Пока что я вижу причину выноса его в интерфейс для возможности подменить реализацию, а так же из-за вариативности возвращаемых значений.
Верно, но ответ на прошлое утверждение не позволяет этого.
Я не уверен на все 100, но можно попробовать через рутовый провайдер получить IHttpContextAccessor (который, кстати, синглтон) вытянуть текущий контекст и из него получить провайдер текущего запроса, который, если я все правильно понял, порожден из текущего скоупа запроса.
Но, в свое оправдание могу сказать, что когда я искал инфу о том, как писать логи, аудиты и другие сквозные шутчки, я попадал на АОП.
Еще хочу заметить, что мой подход не требует менять текущую библиотеку IoC.
Позднее заменю NextDelegate на NextFactory и создам полноценную фабрику для следующего декоратора/сервиса.
Если заменю просто на Next, потеряю возможность понижать время жизни.
Да и не переписать его нужно, а дописать. Причем немного.
Зачем столько негатива в комментариях? Вы помогли разобраться в некоторых вещах, и за это большое вам спасибо. Вы и на SO мне немало помогали. Я много в чем сумел разобраться на старте своей карьеры, благодаря вашим и еще кое-чьим ответам. Более того, научился разбираться самостоятельно. Не надо сейчас это портить.
Экспертное мнение принесет больше пользы и меньше отторжения, если оно будет без эмоций, преувеличений и содержать только факты.
Но в вашем подходе их и искать не надо, они сразу на виду.
Скажите, если ваш сервис ILoggable, IAuditable, IЕщеЧтоНибудь, то зачем вам все эти навороты? Делайте просто явный код, это будет проще и читаемо. И никакой АОП тут не нужен.
Я не вижу в аббревиатуре АОП букву И(инфраструктура). Следовательно использую понятие аспектов на всю катушку. Я не вижу причин, по которым я не могу связать аспекты с предметной областью. Точно так же как я связываю с предметкой объекты.
Веб конфиг вообще не должен ничего знать про ваши классы. Как и администратор системы, который настраивает логирование.
Я ведь писал в статье, что хочу как разработчик и как проектировщик. А вы пытаетесь мне сказать что я не это хочу. И что я вовсе не это хочу.
Попытайтесь понять, что все эти декораторы находятся как бы сбоку и отдельно разруливается порядок их выполнения. То есть, когда я пишу БЛ, я не хочу знать, что чего то там еще будет логироваться. А завтра еще навешают замеры скорости. А послезавтра… И самое главное, все эти декораторы можно писать одновременно, не боясь напороться на конфликты при слиянии. И за все это приходится платить парой лишних методов в декораторах, которые просто указывают на следующий слой и ничего не делают.
И еще, я не знаю, кто именно меня минусует. Если вдруг это вы, пожалуйста не делайте это. Такими вещами должен заниматься сторонний наблюдатель. Я ведь не совсем откровенную чушь пишу, я надеюсь :)
Отлично. После этого я меняю определение метода на такое
и по запаре или так как я впервые вижу этот проект забываю изменить код АОПовской функции. Никакие связи никогда не приведут меня в OnInvoke. В вашем примере, конечно, это ничего не испортит, но так ведь никто не логирует. У нас же бизнес задача, и лог должен быть соответствующий, каждый параметр несет в себе какую-то информацию, а не просто голый текст и числа. То есть у нас будет логирование через индексацию (p[0], p[1] вместо foreach). Вместо параметра b в p[1] теперь упадет c, который имеет тот же тип данных. Логи мы обычно читаем, когда что-то идет не так и спустя полгода (или сколько там ваше приложение работает без сбоев) вы обнаружите, что логи то не информативные. И, казалось бы, что тут такого. Меняю OnInvoke, перезапускаю, вижу верные логи, профит, ничего страшного. Но что если это не просто логи, а аудит безопасности? И залезли вы в них, чтобы узнать кто из пользователей мог слить инсайдерскую информацию.
Кстати, я видел реализацию АОП и поинтереснее, у которой проблем с типизацией не было. Она требует определять сквозные методы с теми же параметрами, что и основной метод. В этом случае вы получите исключение уже при запуске приложения.
Важно. 3 из 5 классов вам вообще не придется оборачивать, там не нужно никакого логирования. Зато нужна какая-нибудь другая обертка. В случае с 1 же классом у вас было бы 2 обертки, у которых действительно очень много неиспользуемых методов.
А если будет 15 классов по 1 методу, так тут вообще все точечно. Вы ведь точно знаете, какие методы чем должны быть обернуты, так что декораторов будет ровно столько, сколько необходимо.
Да и к тому же, неиспользуемые методы дают разве что визуальный мусор. Они не внесут никаких непоняток.Разработчик увидит, что обертка для транзакций не открывает транзакцию на чтение данных из БД и у него не возникнет вопросов.
Так же, они не создадут проблем безопасности, которые я описал выше.
Что значит прозрачно пробрасываются? Вот вы в предложенном методе просто пишите в консоль. Измените метод так, чтобы лог писался через ILogger.Log. И покажите, откуда вы возьмете экземпляр логгера.
Да да, и при этом надо позаботиться, чтобы в ваш сквозной метод пришли все необходимые параметры. Будем принимать массив и разбирать по ходу? Явно нет. Создадим метод, который будет 1 в 1 как требуемый? Ну так вам придется поддерживать сразу в нескольких местах. При изменении определения метода, все сквозные надо будет так же не забыть поменять, а если забудете, то узнаете об этом в рантайме и если повезет, то сразу. Причем еще потребуется магия с инжектом в IL. Да еще и с отладкой бывают интересные штуки. А как обстоят дела с DI в классы, где будут ваши методы АОПовские?
Но тогда возникнет описанная вами ранее проблема. Провайдер для синглтона будет из рута.
Да, интересная ситуация. Это потому, что необходимый вспомогательный функционал с долгой инициализацией, зареганный как синглтон, подключается через конструктор, а не как декоратор. :D
Воу воу, палехче. Тут не надо ничего предполагать, код то перед глазами. Оказалось, в него надо было посмотреть чуть внимательнее и мне и вам. В итоге, в результате дискуссии, был найден изъян и это замечательно. Подумаю, как устранить гонку, главное, что она локализована.
Слава богу, никто до сих пор не регал синглтон декораторы. А понижение со скоупд на трансиент не грозит гонкой в данном случае. :)