Как стать автором
Обновить

Комментарии 30

Какой-то у вас, мягко говоря, странный репозиторий который наружу и ef'ный контекст отдает и валидацию делает. А должен абстрагировать разработчика от конкретной технологии хранения данных и предоставлять доступ к данным как к обычной коллекции.
Контекст базы данных наружу не отдается, отдается только его статические поле, которое хранит текущего пользователя, хотя конечно получить доступ к нему можно, пжл поправьте меня если я не прав.
Валидация объектов тесно связана с контекстом, поэтому я посчитал разумным реализовать валидацию в базовом классе репозитория.
Абстрагирует от технологии абстрагирования технологии хранения данных :)
Это вообще не репозиторий, а шлюз к таблице (table gateway).
Не очень понятно зачем создавать интерфейс ILogger, если уже есть реализованное событие SavingChanges у ObjectContext. А DbContext реализует IObjectContextAdapter с пропертей ObjectContext.
Спасибо за комментарий, я о таком событии не знал, при этом согласно MSDN и приведенному примеру, большой разницы с тем что сделал я нет.
Да вы точно так же опрашиваете чендж трекер. Просто то как вы вызываете логгер мне не очень понравилось.

Посмотрите мой следующий коментарий, в случае если бы вы использовали селф-трекинг то можно было бы опрашивать объекты когда они атачатся в контекст.
Кроме того, мне кажется тредование «реализовать репозиторий» очень странное. Вот требование уметь сохранять всего один объект оно понятней. Фактически оно говорит о том что у вас должен быть реализован паттерн UnitOfWork, и то что должна быть возможность иметь несколько UnitOfWork для одного пользователя одновременно, а он у вас хоть и реализован фактически, но обозван контекстом.

Хотя наверное правильным решением для вашей задачи было бы использование не POCO, а Self-Tracking Entites. В этом случае требование было бы реализованно естественно с технологией. В контекст бы атачились только те объекты, которые надо в текущий момент сохранить.
Соглашусь с первой частью вашего комментария, я действительно не упомянул название паттерна UnitOfWork, в остальном дело большей частью в дело в использованной терминологии.

По второй части вашего комментария:
Если я конечно вас правильно понял, то вы предлагаете отключить Entity Tracking у контекста и всем объектам, которые нужно сохранить вручную задавать их статус. Это действительно правильный и работающий механизм, но проблема в том, насколько я помню, в этом случае свойствам объектов не проставляются флаг «property.IsModified», а это необходимо для логирования.
Есть T4 шаблоны для EF которые генерируют энтитя у которых свой чендж-тракинг. Для всех пропертей по отдельности. И все можно прологировать.
msdn

Заметьте, это решение сейчас помечено как not-recomended, хотя оно и удобнее для n-tier.
Entity Framework и есть самый обычный кусок работы (unit of work). Вы производите работу над объектами, потом сохраняете результат этой работы — именно этим EF и занимается.
1. Все изменения данных должны логироваться, включая информацию о том какой именно пользователь это сделал
— SavingChanges в DbContext

2. Использование паттерна «Репозиторий»
— а чем DbSet — не репозиторий? И зачем сводить всю хитрую систему с IQueryable, ради которой весь EF затевался, к Find/GetAll?

3. Контроль над изменением объектов, то есть если мы хотим обновить в базе данных только один объект, то должен именно один объект.
— можно делать через ObjectContext.Attach/Detach и notracking-запросы: msdn.microsoft.com/en-us/library/bb896271(v=vs.110).aspx
1. В принципе, разницы между тем как был вызван метод для логирования нет.
2. Вам ничего не мешает возвращать IQueryable
3. При отключенном трекинге у свойств объектов флаг IsModified не проставляется, а это нужно для того чтобы сохранять в историю только измененные свойства.

Одними no-tracking запросами не отделаться, если хочется изменения в каждом проперти по отдельности трекать.

Фактически контекст это UoW, поэтому он пересоздается на каждый чих, а репозиторий можно реализовать как слой с кэшированием, правда тут про это сказано не было.
Небольшой комментарий на тему логирования каждого чиха.
Проект для которого был написан код использует примерно 150 сотрудников компании и именно тот факт что все логируется, уже несколько раз очень сильно выручал в разборках типа «Какой гад опять это переделал???», а также восстановить исходное состояние записи до редактирования не обращаясь к бэкапам и услугам разработчика/админа.
Я не говорю что логгирование не нужно. Это нормальное требование для маломальски используемой системы.

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

через поля connection string вполне можно передать в БД текущего пользователя, тогда логирование «кто именно внес изменения» становится возможным на уровне БД.
При этом можно логировать даже ситуацию, когда кто-то полез менять БД руками :)
Да именно про это я и говорю. Так например шарепоинт настраивается для интранета.
Тот самый случай когда комментарии под постом не намного уступают по полезности самой статье :)
Мое гугление адекватного манула результатов не дало.
Добавьте пжл для истории линк на how to.
Спасибо.
С линком сложно :)
В списке параметров connection string есть, например, Application Name. При открытии соединения с БД мы в параметре ApplicationName передавали служебную мета-информацию (в частности, имя авторизованного пользователя).Эти данные потом не проблема прочитать в триггере БД (SELECT APP_NAME()).

Хотя vittore, скорее всего, имел ввиду чуть-чуть другое (авторизация в приложении и БД под одной — доменной — учеткой)
Номер раз — имерсонализация
Номер два — sspi
Номер три — пример

Хотя в простом случае вы можете руками насоздавать пользователей в БД, замапить их на пользователей приложения.
Исользуя код из примера вы будете обращаться к базе используя учетную запись пользователя в AD, и логирование будет производится штатными средствами sql server
А какие есть штатные средства в sql server для логирования (точнее — аудита)?
Если используется Entity Framework, все это выглядит в разы гемморойнее, и чужероднее EF.

Да, если аудит будет жить прямо в сервере — оно будет шустрее. Но EF — уже сам по себе очень тормозная штука, если уж его взяли, то в производительность упираться не расчитывают.
Это очень дорого в поддержке и развертывании. Чтобы работала имперсонация, особенно через несколько компьютеров, нужны настойки делегации на уровне домена (и все равно будет очень хрупко). Сделать логгироование на уровне сервисного слоя в итоге оказывается дешевле.
У меня есть несколько идеи для будущих статей на тему программирования, поэтому у меня большая просьба тем кто поставил минусы прокомментировать что именно не понравилось. Я надеюсь, ваша конструктивная критика поможет мне улучшить качество.
Спасибо!
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории