• Я не умею готовить репозиторий или он просто не очень?

    Valeriy1991
    @Valeriy1991
    Разработчик .NET C# (ASP.NET MVC) в Alfa-B, Moscow
    Коллеги, добрый день!

    Почему никто не упомянул про паттерн UnitOfWork? Паттерн репозиторий становится очень удобным в использовании, если:
    1. это паттерн GenericRepository;
    2. при необходимости можно добавить конкретные репозитории (я их называю ConcreteRepository;
    3. ну и конечно же, если всем этим управляет UnitOfWork;


    При использовании такой связки у Вас:
    • если много сущностей и есть GenericRepository, то вам не нужно плодить репозиторий на каждую сущность - достаточно сделать так: var unit = UnitOfWork.Repository<UserLog>();
    • пункт выше автоматически решает проблему "как выбрать данные из 2-3-4-... таблиц" - с репозиториями вы работаете как с DbSet в EF (кстати, сам DbContext из EF, по сути, реализует паттерн UnitOfWork и GenericRepository);
    • автоматически решается вопрос расширения вашего репозитория методами на каждый чих (т.е. надо получить список логов для конкретного пользователя - добавляем новый метод GetLogsByUserId в репозиторий) - не нужно накручивать репозиторий новыми методами, достаточно сделать так: var unit = UnitOfWork.Repository<UserLog>().GetQuery(e => e.UserId == targetUserId) или var unit = UnitOfWork.Repository<UserLog>().AsQuaryableQuery(e => e.UserId == targetUserId) (методы GetQuery и AsQuaryableQuery - это методы у сущности UnitOfWork, которые возвращают IEnumerable или IQueryable соответственно);
    • если методы SaveChanges/Commit реализованы в UnitOfWork (и UnitOfWork управляет транзакциями БД), то у вас решается проблема консистентности данных - UnitOfWork либо подтверждает все изменения (Commit), либо откатывает в случае ошибок (Rollback);
    Или мне спецальный класс завести в которомом сразу будут все репозитории(как dbContext прям)?

    Правильно мыслите - этот специальный класс и есть UnitOfWork. Он "прям как dbContext", только таким образом вы абстрагируетесь от всяких EF, NHibernate и прочих ORM. В итоге вашей бизнес-логике по барабану, какую ORM вы используете.

    Если мне нужен в 1 контроллере/сервисе не 1 репозиторий? мне по 1 их подключать? может мне 15 надо.

    Достаточно подключить 1 UnitOfWork (и лучше это делать через внедрение зависимостей - DI - DependencyInjection с использованием IoC-контейнера, например, Autofac, Ninject, Unity. Autofac лично мне нравится больше хотя бы потому, что у него самая адекватная и понятная документация, чем у Ninject и уж тем более Unity [нет, это не тема для холивара "кто лучше: Autofac/Ninject/Unity/... - это всё выводы из моего личного опыта]).

    И главный вопрос. Нужно ли вообще применять этот паттерн если ты не используешь юнит тестирование, либо не используешь его для покрытия 100% всего кода а только отдельные хелперы? Ведь для перехода на другую бд достаточно будет сменить провайдер если используешь ef, и если вы используете орм врядли вы полностью от неё откажетесь. Зачем писать кучу кода "наперед" с принципом "а вот вдруг мы поменяем бд/ос", да это скорей всего уменьшит изменения кода по сравнению с полной переписыванием проекта. Но если ты уверен что ос и бд не поменяется?

    В любом enterprise-проекте надо думать наперед, даже если это проект "маленький интернет-магазинчик". Потому что через год этот маленький интернет-магазинчик может превратиться в нормальный такой бизнес с очень высокими оборотами и прибылью, и тогда, если у Вас не будет хорошей абстрагированной архитектуры, если у вас не будет модульности, то любая новая задача будет реализовываться со все большим количеством костылей, и любая новая задача будет приводить к неустойчивости системы и повышению степени регресса (проверено!). А вот если вы как архитектор вложите при разработке в вашу систему модульность и абстрагирование компонентов, то вы тем самым уже упростите себе жизнь в будущем, а это означает, что через какое-то время любая задача по изменению системы будет решаться быстрее, надежнее, а значит, вы сможете брать как разработчик за это больше денег (потому что стоимость новых изменений будет гораздо ниже). А если вы еще и юнит-тестирование будете использовать при разработке, то тем самым вы снижаете риск регресса в поведении вашей системы, что опять же, снижает стоимость разработки лично для вас. Поверьте, когда система имеет хотя бы модульность и абстрагирование компонентов друг от друга (т.е. какой-либо компонент зависит от абстракции - в виде интерфейса или абстрактного класса, а не от реализации - в виде конкретного класса), то работа с такой системой будет доставлять удовольствие.

    СУБД менять - это не такая распространенная практика, а вот от ORM (и от EF в частности) отказываться в пользу производительности - это реальный кейс с ростом нагрузки.

    Толстый Лорри - абсолютно с Вами согласен по этому поводу.

    Михаил очень в тему упомянул принцип единственной ответственности. 1 компонент - 1 ответственность. По себе заметил - если следовать хотя бы этому принципу, то уже автоматически система становится модульной. А раз есть модули, то можно снизить степень связанности компонентов между собой, используя абстракции (интерфейсы и абстрактные классы). А раз есть абстрагирование между компонентами, то юнит-тестирование будет только в радость. Причем компонентом может выступать как класс, так и целый проект. Например, в отдельный проект можно вынести какой-нибудь модуль системы, и затем этот проект можно подключить в другую систему при разработке. Таким образом повышается степень повторного использования кода (зачем 2 раза делать одно и то же?).

    Михаил еще также указал в комментарии, в каких случаях репозиторий можно не использовать. Добавлю: в не-enterprise-решениях. Например, вы делаете какой-нибудь простенький сайт для себя. Или для друга. Или просто для изучения новой технологии.
    Ответ написан
    8 комментариев
  • Я не умею готовить репозиторий или он просто не очень?

    @Sing303
    1. Писать свои методы до бесконечности (Таких методов на самом деле будет не много)
    2. Тоже писать свой метод, помещать в зависимости от того, из какой таблицы выборка (В вашем случае logs)
    3. Подключать по 1, если вам надо 15, то подключать 15 по одному, но тут скорее всего у вас явное нарушение принципа единственной ответственности. Проблем быть не должно, есть IoC контейнеры, фасад, если нужно.
    4. Lazy loading используют только там, где не критична производительность, во всех остальных случаях его отключают и получают все данные сразу
    5. Этот паттерн в первую очередь применяется для устранения дублирования кода и инкапсуляции, что является базой в ООП. Если нужен "чистый" и гибкий код, нет ничего сложного в реализации и поддержки репозитория, это очень простой паттерн, а если вам кажется сложным, значит вы его не поняли.
    Ответ написан
    3 комментария
  • Я не умею готовить репозиторий или он просто не очень?

    @kttotto
    пофиг на чем писать
    В принципе пост выше ответил, но я тоже добавлю.

    Если Вы каждый запрос будете закрывать ToList(), то действительно, на каждый такое действие будет запрос в бд. Если используете LINQ, даже если будет несколько строк кода, сформируется один запрос к бд. И репозитории чаще оборачивают в один сервис, централизованный доступ к данным и работают уже с ним.

    Users.Find(id).Logs.Where(х => x.Date == date) или
    Logs.Where(x => x.UserId == userId && x.Date == date)

    В любом случае будет один запрос.

    И под каждый сложный запрос (часто востребованный) добавляют метод в интерфейс.

    Надо-не надо, это личное дело, если проектом только один человек занимается. Если его надо поддерживать, развивать, то приходится думать о гибкости. И не всегда меняют провайдера, могут уйти в чистый ado.net, если orm перестает тянуть какие то запросы. Поэтому без репозитория тут проблемно.
    Ответ написан
    Комментировать
  • Я не умею готовить репозиторий или он просто не очень?

    Думаю если небольшой, новый проект в котором все данные лежат в базе, которая создается из EF Code First, то использовать репозиторий особой нужды нет. Но например если сегодня я беру данные из базы, а завтра планирую получать от api или какой нибудь NoSQL к которой нет ORM ? А может сущности, которыми оперирует моё приложение ну совсем не похожи на те что лежат в базе или "собираются" из нескольких источников ?
    Не надо рассматривать репозиторий как обертку над EF.

    Теперь пара замечаний:

    вот так
    public IEnumerable<Student> GetLogs()
    {
          return context.Logs;
    }


    будет возвращен IQueryable и

    GetLogs().Where(...)

    будет ограничивать выборку с сервера.

    Я бы с удовольствием посмотрел на страницу, где выводятся ВСЕ логи всех админов.
    Но если она все же реально нужна я бы формировал её на клиенте так: получил AJAX запросом всех админов для каждого из них параллельно или последовательно запросил топ 100 мессаджей из логов (при необходимости следующие 100). Как следствие отталкиваться надо от UX тогда не будет желания писать репозиторий, который возвращает ВСЕ записи.

    ЗЫ Пока ORM обеспечивает 100% данных и хватает мощности DB сервера все окей и без репозиториев, но при высокой нагрузке архитектура в корне другая и JOINы там выполняют отнюдь не DB сервера.
    Ответ написан
    Комментировать
  • Я не умею готовить репозиторий или он просто не очень?

    @Free_ze
    Пишу комментарии в комментарии, а не в ответы
    Желательно почитать по поводу работы EF и IQuerable в частности. С помощью этого интерфейса и LINQ вы сможете формировать запросы (которые рендерятся в нативный SQL), а не просто фильтровать коллекции.
    private IQuerable<Student> GetLogs()
    {
        return context.Logs;
    }   
    public IEnumerable<Student> GetLogsForStudent(int id, DateTime from, DateTime to)
    {
        return GetLogs().Where(x => x.Id == id
                                    && x.Date >= from
                                    && x.Date <= to)
                        .ToList();
    }


    Все используют lazy loading?

    Зависит от количества данных и критичности по скорости.

    Если мне нужен в 1 контроллере/сервисе не 1 репозиторий? мне по 1 их подключать?

    Да, по одному. А можно в сервис или UnitOfWork объединять.

    Изачем он для юнит тестов если можно спокойно moqнуть весь dbcontext?

    dbcontext мокать нет смысла хотя бы потому, что он гвоздями к EF прибит. СУБД менять - это не такая распространенная практика, а вот от ORM (и от EF в частности) отказываться в пользу производительности - это реальный кейс с ростом нагрузки.
    Ответ написан
    Комментировать
  • Entity Framework не сохраняет связанные данные?

    Include() ?
    А точнее, так:
    context.Subdivs.Include(i=>i.Realtor).First(s=>s.Id == id):
    Ответ написан
  • Заняты ли потоки при использовании async/await?

    AlekseyNemiro
    @AlekseyNemiro
    full-stack developer
    https://msdn.microsoft.com/ru-ru/library/hh156528.aspx
    Выражение await не блокирует поток, в котором оно выполняется. Вместо этого оно указывает компилятору объявить оставшуюся часть асинхронного метода как продолжение ожидаемой задачи. Управление затем возвращается методу, вызвавшему асинхронный метод. Когда задача завершается, она вызывает свое продолжение и возобновляет выполнение асинхронного метода с того места, где она была прервана.

    Метод Task.Run ставит в очередь заданную задачу для запуска в пуле потоков.

    Т.е. в первом случае, у вас просто будет добавлена задача в очередь на выполнение и продолжено выполнение программы (без ожидания, программа может завершить работу раньше, чем созданная задача). А во втором, текущий поток будет ожидать завершения выполнения асинхронной операции. Основной поток при использовании await заморожен не будет.

    // без ожидания
    Console.WriteLine(DateTime.Now);
    
    Task.Run(() => { Thread.Sleep(5000); });
    
    // этот код будет выполнен сразу
    Console.WriteLine(DateTime.Now);
    
    // ожидание с await
    Console.WriteLine(DateTime.Now);
    
    await Task.Run(() => { Thread.Sleep(5000); });
    
    // этот код будет выполнен, только после завершения выполнения задачи
    // текущий (вызывающий) поток не будет приостановлен
    // (например, в Windows Form это будет хорошо видно)
    Console.WriteLine(DateTime.Now);

    Если сделать задачу и вызвать метод Wait, то основной поток будет приостановлен, пока не завершится задача.

    Console.WriteLine(DateTime.Now);
    
    var t = Task.Run(() => { Thread.Sleep(5000); });
    t.Wait(); // ожидание выполнения задачи
    
    // этот код будет выполнен, только после завершения выполнения задачи
    // текущий (вызывающий) поток будет блокирован
    // (например, в Windows Form это будет хорошо видно)
    Console.WriteLine(DateTime.Now);

    74ad1b048cee439faab48113a61a7ec7.gif
    Ответ написан
    6 комментариев
  • Можно ли выполнять lock на list?

    impwx
    @impwx
    Разработчик
    Допустим, _inner используется у вас во многих местах, и есть некий публичный метод, который его возвращает. Тогда внешний код сможет вызвать этот метод, получить тот же экземпляр объекта _inner и поставить на него свой lock. Так можно случайно словить взаимоблокировку и повесить приложение.

    Подобные моменты бывает очень сложно отследить, потому объект может быть возвращен косвенно, по интерфейсу, или как-либо еще. Отдельное поле _lock - это просто правило хорошего тона, его создают для безопасности: очень маловероятно, что кому-то придет в голову использовать его за пределами инструкции lock и тем более вернуть из метода.

    Если у вас небольшой код и вы уверены, что знаете, что делаете - разницы нет.
    Ответ написан
    Комментировать
  • Одновременный вывод и ввод текста?

    Я так понимаю вывод происходит из другого потока ? Тогда нужно синхронизировать потоки, что то типа
    lock(некая глобальная переменная)
    {
          var top = Console.CursorTop;
          var left = Console.CursorLeft;
    
          Console.CursorTop = 0;
          Console.CursorLeft = 0;
                                  
          Console.Write(DateTime.Now.ToShortDateString());
    
          Console.CursorTop = top;
          Console.CursorLeft = left;
    }
    Ответ написан
    4 комментария
  • Как обойтись без библиотеки импорта?

    @MarkusD Куратор тега C++
    все время мелю чепуху :)
    Библиотека импорта для dll - это вспомогательный артефакт связывания основного образа программы с динамическим модулем.
    В общем - никак, раз уж явную загрузку тебе не предлагать. :)

    Иное дело - это твои хитромудреные манипуляции для установления зависимости между основным и динамическими модулями. Зачем тебе потребовалось явно указывать линковку с lib файлом?
    У каждого проекта студии есть список зависимостей (References). Если в проекте главного модуля программы установить зависимость от проекта динамической библиотеки, то msbuild все свяжет за тебя.

    А еще есть нестандартная но широко поддерживаемая #pragma comment(lib,"xxx.lib").
    Ответ написан
    Комментировать
  • Есть ли смысл хешировать уже захешированные пароли?

    Да, есть смысл для bcrypt как минимум. Задача bcrypt в том, чтобы замедлить процесс брутфорса и сделать его ресурсоемким и сложно реализуемым на специализированном оборудовании / GPU / ботнетах и т.п. (см. например openwall.info/wiki/john/GPU/bcrypt). Это сильно замедляет возможности подбора даже относительно слабых паролей.
    Есть еще более интересные алгоритмы, например Argon2 и yescrypt.

    Вводить разные хэши для разных пользователей не стоит, решение с "перехэшированием" старых хэшей вполне безопасно.
    Ответ написан
  • С помощью чего лучше рисовать обновляемые графики?

    petermzg
    @petermzg
    Самый лучший программист
    Custom control и на его канве отрисовываете. Это нормальное решение.
    Ответ написан
    Комментировать
  • Как понять книгу Рихтера, если ты новичок?

    AxisPod
    @AxisPod
    Я бы не советовал начинать с данной книги, много тонкостей, но при этом далеко не все базовые вещи есть. Начинайте с чего-нить попроще. Албахари к примеру или Шилдта. Вот как с ними разберетесь, тогда можно и за Рихтера браться. А смысла колоться и продолжать есть кактус я не вижу.
    Ответ написан
    Комментировать
  • Для чего нужно virtual/override?

    Deerenaros
    @Deerenaros
    Программист, математик, задрот и даже чуть инженер
    Ахтунг, товарищ. Перед тем как сомневаться в профессионализме своих коллег очень советую хотя бы немного сомневаться в собственном. Это как минимум изредка полезно. А теперь ноги в руки и вперёд читать про таблицы, виртуальность и методы. Это не просто полезно, но архиважно. Учитывая, собственно, вопрос.

    А вопрос состоял в следующем. Вот у нас есть объекты, друг от друга наследуются, имплементируются и всё круто-прекрасно, но блджад. Зачем они нужны-то? Нет, не агрегация данных, хотя и она тоже. Нет, не агрегация реализации, хотя и она тоже. И нет, нет, не просто потому что. Хотя и это тоже... А вот подумай о такой ситуации. Есть библиотека. Да не важно какая, ну пусть это будет UI/UX. Вот есть абстрактный (!) класс кнопка. Почему абстрактный? Да потому что любая кнопка - это кнопка. Это как бы класс над классом. Кнопка может быть красной, может быть кликабельной, может становится не кликабельной после нажатия, может открывать диалог выбора файлов, а может закрывать приложение к чертям. В общем, много чего может уметь делать кнопка. И вот эту кнопку хочется поставить на формочку. И сделал создатель библиотеки такой прекрасный метод у формы PutAnyButtonHere(Button btn, Point xy). Но... Чёрт, чувак, мы должны передать объект класса Button, а у нас какой-нибудь наследник MyBestButton : BestAbstractLibraryButton, который наследник той самой Button. И у Button декларирован такой прекрасный метод TimeWhenUserClickOnMe(Point xy, AnotherInfo somethinElse), собственно... Получается, что где-то в недрах той самой библиотеки, когда автор прекрасной UI/UX библиотеки вызывает этот самый метод он должен вызвать НАШУ реализацию, а не реализацию BestAbstractLibraryButton. И уж точно не несуществующую реализацию Button (ибо метод там чисто-виртуальный). Идея виртуальных методов изначально именно в том, что есть особая таблица, которая хранит реализации отдельно, объекты отдельно, но когда мы пытаемся вызывать у объекта виртуальный метод происходит такая магия, которую принято называть поздним связыванием.

    А ключевые слова это просто для того, чтобы программист читая чужие полотна кода хоть ну немного имел возможность хоть как-то их понять. Что конкретно хотел программист в конкретной ситуации. И, если в Java все методы по дефолту виртуальные и ничерта не понятно из чужих полотен, хотел здесь программист позднее связывание, или это просто агрегация функционала, то в шарпе данной проблемы "как бы нет", язык порой чрезвычайно многословен на, казалось, такие банальности. Что в итоге изредка, но экономит чёртову тучу времени.

    Сухой остаток. Если мы вызываем виртуальный метод у объекта класса наследника некоторого супер-класса имея ссылку типа супер-класса, то вызывается реализация метода наследника. Если мы вызываем не виртуальный метод у объекта класса наследника некоторого супер-класса имея ссылку типа супер-класса, то вызывается реализация метода супер-класса. В остальном поведение тривиальное.

    Отсебятина. Не понимая данных вещей невозможно говорить, что принципы объектно-ориентированного программирования были усвоены. Советую прочитать "Философия Java" (вообще там Java скорее как язык для примеров, в общем-то ООП оно и в Африке ООП). Ну и пытаться разобраться в точке зрения любого человека, особенно если его компетенция хоть чем-то подтверждена (например, успешным трудоустройством).
    Ответ написан
    Комментировать
  • Как понять книгу Рихтера, если ты новичок?

    ИМХО: То что вы читаете книги это прекрасно, но так как вы новичок, не нужно пока забивать себе голову этим Рихтером. Читай те пока общие вещи по ООП, принципам программирования, заглядывайтесь на паттерны, книги по рефакторингу и алгоритмам, смотри те как программируют другие люди, в будущем это принесет вам большую пользу.
    С# - это просто инструмент для выполнения какой-то вашей задачи, а книга Рихтера, как раз рассказывает об особенностях этого языка(описание этого инструмента), что толку от того если ты знаешь глубоко язык и все его нюансы и особенности, когда твой код при компиляции погибает в багах и через 2 месяца ты уже не понимаешь, что и как у тебя устроено. А добавление нового функционала приводит к аду из ошибок и кишков.
    Зная основы не трудно будет даже выбирать инструмент для решение ваших задач, будь то C# или какой-то другой язык.
    Ответ написан
    Комментировать
  • ASP.NET Custom Authentication Service + OWIN или ASP.NET Identity 2.0?

    artem_b89
    @artem_b89
    Сетевой бездельник
    1) Достаточно
    2) В Identity реализован базовый функционал работы с пользователями/ролями и т.д., возможность подключения авторизации через стороннии сервисы.
    Если нужна простейшая аутентификация/авторизация, то Identity в целом не нужен, достаточно будет найти в DAL пользователя с указанными логином и паролем и авторизовать его.
    Псевдокод:
    public User Validate(string login, string secret)
    {
      return DbContext.Users.FirstOrDefault(x=>x.login == login && secret == secret );
    }  
    public async void SignIn(User user)
    {
    	List<Claim> claims = new List<Claim>();	
    	claims.Add(new Claim(ClaimTypes.Name, user.Name));
    	ClaimsIdentity identity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);
    	ClaimsPrincipal principal = new ClaimsPrincipal(identity);
    	await HttpContext.Authentication.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, principal);
    }
    public async void SignOut()
    {
      await HttpContext.Authentication.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
    }

    Вся остальная логика работы с пользователями и ролями пишется достаточно легко. Вопрос только в том нужно ли городить свой велосипед.
    Ответ написан
    1 комментарий
  • Каким образом я могу использовать IoC контейнер IServiceProvider в .NET Core Class Library?

    yarosroman
    @yarosroman Куратор тега C#
    C# the best
    А причем тут IoC? Я думал всегда, что IoC наоборот, чтобы разорвать связь между уровнями.

    services.AddTransient, PersonService>(); эта строка не связь обеспечивает, она лишь говорит контейнеру, экземпляр какого конкретного класса создавать при запросе его из контейнера.

    если вам необходимо, чтобы в конструктор сервиса передавался контекст данных, то добавляем его в IoC контейнер
    services.AddTransient<DdContext>(),
    а в конструктор сервиса добавляем параметр, например,
    public PersonService(DbContext context)
    Те, когда вы запросите экземпляр IService, контейнер, скажем так, рекурсивно будет разрешать зависимости, те увидев зависимость сервиса от DbContext, и зная зарегистрированный класс, контейнер сначала создаст DbContext, затем создаст PersonService, передав в него созданный экземпляр DbContext.
    Ответ написан
    6 комментариев