Ответы пользователя по тегу C#
  • C#: Как правильно организовать сервисы в Business Logic Layer с помощью DI?

    Valeriy1991
    @Valeriy1991
    Разработчик .NET C# (ASP.NET MVC) в Alfa-B, Moscow
    1. BarService как бизнес-сервис не должен знать о другом бизнес-сервисе, т.е. о FooService. Если приходит понимание, что в BarService нужно вызывать метод из FooService, значит Ваш FooService должен стать компонентом более низкого уровня, чем бизнес-сервис. Например, FooManager (вспомните всякие ***Manager из ASP.NET Identity). Тогда этот FooManager можно будет использовать не только в BarService, но в каком-нибудь другом ***Service. Архитектурно будет выглядеть так: наверху ***Service (бизнес-логика), ниже - ***Manager (тоже бизнес-логика), еще ниже - ***Repository (а это уже DAL).
    2. Да, бывает необходимость расширить Generic-репозиторий конкретным репозиторием. Но тут надо очень хорошо подумать, почему мы это делаем. Возможно, мы пытаемся на конкретный репозиторий навесить часть бизнес-логики? Если так, то это уже не конкретный репозиторий и даже не компонент уровня DAL - это компонент уровня бизнес-логики. Ну да ладно, допустим, мы поняли, что нам-таки нужен IFooRepository. Я сталкивался с таким решением этой задачи: в IUnitOfWork добавляется новое свойство:
    public IFooRepository FooRepository {get; }
    И все. Инициализация выполняется внутри экземпляра UnitOfWork, например, так:
    public IFooRepository FooRepository {get; } = new FooRepository();

    а "наружу" нам доступен FooRepository:
    _uow.FooRepository.CallSomeMethod(...);
    В итоге в BarService Ваш код будет выглядеть примерно так:
    public void CreateBar(BarDTO bar) 
    {
        // Insert Bar entity
        
        _uow.FooRepository.CreateFoo(new FooDTO()); 
    
        _uow.Commit();
    }


    Но повторюсь. В Вашем примере у меня есть подозрения, что метод CreateFoo должен принадлежать не FooRepository, а какому-то компоненту уровня бизнес-логики (но в иерархии он не является бизнес-сервисом). Например, FooManager или FooCreator какой-нибудь (который отвечает только за создание объекта). Помните принцип SRP.
    Ответ написан
    Комментировать
  • Объясните как правильно применять паттерн Repository с Entity Framework?

    Valeriy1991
    @Valeriy1991
    Разработчик .NET C# (ASP.NET MVC) в Alfa-B, Moscow
    Добрый день! Думаю, очень похожий вопрос: Я не умею готовить репозиторий или он просто не очень?
    От себя. Лепить репозитории на каждую сущность выгодно, как мне кажется, только когда этих сущностей раз-два-три и закончились, а проект никогда не будет сопровождаться. В enterprise-решениях следование принципу "1 репозиторий на 1 сущность" приведет к сложностям сопровождения и масштабируемости приложения.
    Ответ написан
    Комментировать
  • Что не так с ASP.NET Identity в Onion архитектуре?

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

    Правильно ли я реализовал Repository + UnitOfWork

    Ну, насколько я знаю, существует множество реализаций UnitOfWork + Repository, и каждая их них обладает своими достоинствами и недостатками. У Вас все выглядит достаточно работоспособно и юнит-тестируемо.

    Правильно ли реализовано Dispose

    В принципе, да. Только вот private fields на то и private, чтобы их инкапсулировать. Считаю очень неправильным, что у вас переменная disposed имеет модификатор public. Еще видел где-то в сети, что рекомендуют явно присваивать в переменную db значение null после вызова db.Dispose():
    if (_db != null)
    {
        _db.Dispose();
        _db = null;
    }

    Не хочу показаться навязчивым или развивать тему холивара, но именовать private field через символ "_" в начале имени, на мой взгляд, все же удобнее. Т.е. вместо private DbContext db лучше написать private DbContext _db. Как мне кажется, визуально такой код другим людям читать легче.

    В каком слое по вашему должен находится ApplicationSignInManager, ведь он использует OWIN и работает с ApplicationUserManager

    Зависит от сложности Вашего приложения. Может быть такая ситуация, что MVC-приложений несколько, но они должны использовать один механизм авторизации/аутентификации. Тогда компонент ApplicationSignInManager можно вынести в отдельный проект. Если такой необходимости нет, то, на мой взгляд, можно спокойно оставить его в клиенте (MVC-app).

    Ввиду того что о базе и орм не знает ни один слой кроме DAL я решил вынести connectionString в константу, это нормальный подход?

    Нет. Строки подключения к БД лучше всего хранить как настроечные параметры. Для ASP.NET 5 - в web.config, например. Для ASP.NET Core - например, в файле appsettings.json.

    Есть смысл вынести сущности в отдельный проект и отделить их от аннотаций для бд?

    Однозначно. Потому что Ваши сущности представляют доменную модель. Выделение их в отдельный проект позволит сделать более модульной Вашу архитектуру, а следовательно, Вы сможете подрубать этот проект только там, где он нужен, не таща с собой всякие DbContext-ы, Repository, UnitOfWork-и и др.


    В примере в owin контекст засовывают ef контекст вот таким образом app.CreatePerOwinContext(ApplicationDbContext.Create); насколько это оправдано и стоит ли так делать?
    В реализации UserStore для EF очень странно ведут себя с dispose и у меня ощущения что некоторые данные висят в памяти постоянно, так ли это?

    Вот тут не подскажу. Буду рад также услышать компетентное мнение.

    Насколько вообще оправдан такой велосипед?

    Какой именно велосипед? :) Если собственная реализация всяких ***Store, то думаю, да, потому что иначе их развязать и расположить в разных слоях не получится (сам мучился этим вопросом, и в итоге, потратив тонну времени, последовал совету делать свои реализации ***Store, чтобы делать архитектуру более модульной и упрощающей юнит-тестирование).

    В свое время мне очень помогла вот эта серия статей (из 4 частей): techbrij.com/generic-repository-unit-of-work-entit...
    Ответ написан
    1 комментарий
  • Я не умею готовить репозиторий или он просто не очень?

    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 комментариев
  • Где должна находиться Domain Model?

    Valeriy1991
    @Valeriy1991
    Разработчик .NET C# (ASP.NET MVC) в Alfa-B, Moscow
    Добрый день! DomainModel можно располагать и в отдельном слое (это уже шаг в сторону "луковичной" архитектуры (onion). Например, если доменная модель сильно совпадает с моделями в DAL, то имеет смысл расположить ее в отдельном проекте VS, навесить атрибуты Column, Key, ForeignKey, NotMapped (и др. атрибуты из DataAnnotations) - это в случае, если у Вас EF CodeFirst (с EF Database first такое особо не прокатит - только через partial-классы, но, на мой взгляд, это "уродство"), после чего в слое DAL останется только функционал подключения к БД и выполнения запросов. Если доменная модель сильно различается от модели DAL, то можно написать всякие обертки, которые будут преобразовывать классы доменной модели в модели DAL и наоборот.

    Я бы советовал доменную модель выносить в отдельный проект. Почему? Потому что так ею проще пользоваться. Иначе Ваш бизнес- или DAL-слой превратится со временем в помойку: там будут и атрибуты, и исключения, да еще в каждом слое свои, и enum, и всякие сервисы/менеджеры/хелперы/... При разбиении решения на проекты, на мой взгляд, также следует придерживаться принципа единственной ответственности (S из SOLID). Каждый проект отвечает за что-то одно. Это позволяет спокойно подключать/отключать функциональности из других проектов (получается что-то вроде модульности решения) и избавляет от циклических зависимостей между проектами (когда у Вас есть проект MyApp.Common, который подключен в MyApp.BLL, но в слое MyApp.BLL есть функционал, который нужно использовать внутри проекта MyApp.Common => получается циклическая зависимость, и способ ее решения - перенести общий функционал в отдельный проект).
    Ответ написан
    Комментировать
  • C#. Где может быть полезна явная реализация интерфейса?

    Valeriy1991
    @Valeriy1991
    Разработчик .NET C# (ASP.NET MVC) в Alfa-B, Moscow
    Добрый день! Интерфейсы (как и абстрактные классы) позволяют (с точки зрения архитектуры) Вам разрабатывать слабо связанные приложения. Это, в свою очередь, приводит к тому, что: 1) внести изменения в код становится проще; 2) приложение не зависит от конкретных реализаций, а зависит от абстракций.
    Ответ написан
    2 комментария
  • Как легче освоить внедрение зависимостей, code-first, TDD и паттерны?

    Valeriy1991
    @Valeriy1991
    Разработчик .NET C# (ASP.NET MVC) в Alfa-B, Moscow
    Добрый вечер! Спасибо за приглашение.
    На мой взгляд, Вам следует придерживаться следующих приоритетов по изучению:
    1. внедрение зависимостей более важно из Вашего списка, т.к. относится к SOLID принципам;
    2. TDD - на мой взгляд, вещь более нужная для изучения, чем code-first или patterns. Сам не так давно начал разрабатывать по TDD. Это очень здорово, что изобрели такой подход. Экономит тонну времени на ручное тестирование, а также дает быстрое понимание, что и где случайно (или неслучайно) сломалось. Главное - понимать, что покрывать тестами;
    3. третьим в список я бы добавил AngularJS или KnockoutJS или BackboneJS - сам пока что не изучил их и не начал применять, но судя по их популярности и преимуществам - думаю, Вам стоит с ними ознакомиться;
    4. code-first или database-first - не так уж и важно на Вашем этапе понимания. Главное отличие подхода Code-first: 1) модель пишется вручную, в связи с чем не нужно постоянно отслеживать edmx-диаграмму (т.е. можно считать, что Ваша code-first модель всегда находится в актуальном состоянии); 2) поддерживает миграции БД. В Database-first Вам нужно самому отслеживать актуальность состояния Вашей edmx-модели - с этим тоже могут быть проблемы. Опять же - только с опытом. С другой стороны, Database-first позволяет наглядно видеть Вашу модель, а вот Code-first - нет;
    5. паттернами тоже можете пока что голову не забивать. Безусловно, это нужно, но понимание их придет только с опытом (признаюсь честно: я сам не до конца все паттерны знаю и понимаю). На мой взгляд, важно соблюдать 1 основной паттерн - 3-уровневая архитектура (клиентский слой, слой бизнес-логики, слой работы с данными).

    Теперь по MVC.
    1. Model - тут, в принципе, всё просто: модель данных. Здесь можно поспорить, что иметь в виду под Моделью: модель самих данных или модель представления. Лично я после опыта работы с шаблоном MVVM в WPF под моделью данных в терминах ASP.NET MVC понимаю модель представления, а под термином "модель данных" - саму доменную модель (EF code-first, например). Кто-то может сказать, что это "лишняя работа" - по упаковыванию модели данных из EF-объектов в объекты модели представления. Да, частично соглашусь. Но зато это дает некую гарантию безопасности, что случайно пользователь не поменяет важную часть модели данных (например, ID).
    2. Контроллер. Основная задача контроллера - сформировать данные для отображения и передать эти данные в представление. Т.е. нужно стремиться к тому, чтобы код метода действия в контроллере содержал минимум кода. В идеале - вызов метода из слоя бизнес-логики и передача полученных данных в представление. Если Вы видите, что метод действия в контроллере содержит какую-то бизнес-логику, то это сигнал к рефакторингу: Ваш метод действия слишком много знает. По опыту могу добавить, что в среднем код метода действия содержит от 2 до 20-30 строк кода (с учетом того, что скобки { и } расположены на отдельных строках).
    3. Представление. Тут тоже всё просто: отобразить данные (из модели представления). Ни в коем случае нельзя в представлении писать логику по работе с самими данными, например, так делать нельзя:
    <div id="account">
        @{
            using(var db = new MyEfDbContext()
            {
                var userAccount = db.Accounts.FirstOrDefault(e => e.Username == User.Identity.Username);
                if(userAccount != null)
                {
                    @:Имя: @userAccount.Name
                    @:Фамилия: @userAccount.LastName
                }
            }
        }
    </div>


    Если у Вас 3-уровневая архитектура, например, есть слои:
    1. MyApp.MVC - MVC-application
    2. MyApp.BL - слой бизнес-логики
    3. MyApp.DAL - слой работы с данными
    то в представлении (View) вызывать напрямую сервисы бизнес-логики тоже нельзя, особенно, если Вы используете DI-принцип (внедрение зависимостей) и IoC контейнер. Т.е. такой пример недопустим:
    <div id="account">
        @{
            var accountService = new MyApp.BL.AccountService();
            var userAccount = accountService.GetUserAccountByUsername(User.Identity.Name);
            if(userAccount != null)
            {
                @:Имя: @userAccount.Name
                @:Фамилия: @userAccount.LastName
            }
        }
    </div>

    Попробую донести мысль архитектора и разработчика Александра Шевчука (преподавателя с http://itvdn.com): "Одна из главных целей при разработке - стремиться к упрощению системы". Ослабление зависимостей позволяет нам упрощать систему благодаря тому, что изменение осуществляется только на 1 каком-то слое/уровне. Если Вы во View вынесете логику по работе с данными, а уж тем более, как в примерах выше, работу с EF-контекстом, то Вы усилите зависимость одного компонента системы (MVC-слоя) от другого (слоя работы с данными или слоя бизнес-логики). Усиление зависимостей приводит к бОльшему числу изменений, что в свою очередь сказывается на повышении расходов на эту систему. Ослабление зависимостей приводит к меньшему числу изменений (например, при переходе от EF к native SQL или NHibernate затрагивается только слой работы с данными, а слой MVC и бизнес-логики не меняются), а значит, к более раннему выпуску системы или очередного релиза, и как следствие, снижение расходов (не только денежных, но и других ресурсов) на разработку. TDD тоже можно отнести к практикам, которые снижают затраты ресурсов на содержание системы. Но это я ушел в глобальное...

    Правильным будет подход, при котором у Вас снижается зависимость компонентов системы друг от друга, в случае с ASP.NET MVC приложением, на мой взгляд, это когда:
    - View знает о модели (я по прежнему буду иметь в виду модель представления - ViewModel, которые объявлены либо в MVC-слое, либо в слое бизнес-логики);
    - контроллер знает о слое бизнес-логики, обращается к нему за выполнением операций и получением ViewModel'ей, после чего передает во View полученную ViewModel (ну или JSON-данные);
    - Model формируется по принципу, грубо говоря, почти на каждую View своя модель.

    Фразу "правильным будет подход" я обосновываю тем, что у такого подхода есть масса плюсов (которые очевидны опытным разработчикам, но могут быть не до конца или неправильно поняты менее опытными коллегами, а именно):
    + View ничего не знает о доменной модели (только о ViewModel), благодаря чему Вы можете спокойно менять свою доменную модель, не внося изменений во View (см. выше про ослабление зависимостей и снижение количества связей). Также Вы спокойно можете перейти от EF к NHibernate или к native SQL, или использовать и то, и другое - View об этом никогда не узнает;
    + контроллер (да и весь MVC-слой) знает только о существовании слоя бизнес-логики, но ничего не знает о слое работы с данными.
    + если на View делать отдельную ViewModel, то это позволяет более полноценно управлять тем, что нужно показать пользователю. Т.е. дает возможность большего контроля отображаемых данных, повышает безопасность Вашего приложения (пользователь, например, не сможет изменить ID просматриваемой записи, если этого ID нет вообще в модели представления).

    Ну а вообще все зависит от задачи/проекта: нужно ли применять разбивку на слои или использовать ViewModel'и вместо обычных доменных моделей - надо думать над каждой ситуацией отдельно.

    P.S. На мой взгляд, литературу выбрали правильно - я тоже начинал изучение MVC с нее. Понравилась тем, что дается сначала общее описание и работа с ASP.NET MVC на сквозном примере. А потом идет более глубокое погружение в ASP.NET MVC. По разработке могу посоветовать блог Александра Бындю: blog.byndyu.ru Как мне кажется, там очень хорошо некоторые моменты разжевываются, в том числе SOLID, TDD, шаблон Repository, UnitOfWork и др.
    Ответ написан
    2 комментария
  • Как защитить ASP.NET MVC приложение от администратора?

    Valeriy1991
    @Valeriy1991 Автор вопроса
    Разработчик .NET C# (ASP.NET MVC) в Alfa-B, Moscow
    Коллеги, в принципе, можно отнести это к решению вопроса - в Visual Studio при публикации ASP.NET приложения в настройках Publish'а есть такая опция - "Precompile during publish". При конфигурировании этой опции снимается галочка с "Allow precompiled site to be updatable" + можно поиграться с блоком "Merge option". Эта галочка способствует тому, что все View при публикации будут преобразованы в *.dll файлы, а блок "Merge option", насколько я понял, позволяет настроить различные способы этого преобразования View -> dll. Решение пока отмечать не буду - хотелось бы услышать еще варианты и "поиграться" с этими настройками публикации.
    Ответ написан
    Комментировать
  • Как зашифровать (обфускатор) код в c#?

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

    Как вариант - написать свой обфускатор (на полном серьезе). Например, строки можно кодировать в Юникод (вида \u0024), добавлять рандомно кучу комментариев, точек с запятыми, переименовать все переменные в переменные с бредовыми названиями, используя Guid.NewGuid(). Например:
    было:
    private string _appName = "MyApp";
    стало:
    private string 2D63C163DBAF484E8B2286DCD3BE3FD4= "\u0023\u0054"; // как-то так

    Можно вдобавок удалить все #region'ы и #endregion'ы, сделать так, чтобы всё содержимое *.cs файлов было в 1 строку (только это не работает, если у Вас есть директивы препроцессора: #if, #endif и др. - эти директивы обязательно должны быть на новой строке), удалить все нормальные комментарии (которые оставляли разработчики), добавлять какой-нибудь ненужный код в виде циклов.

    Удачи!
    Ответ написан
    6 комментариев
  • Правильное использование UnitOfWork в сервисах?

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

    По первому вопросу я бы отдал предпочтение using, потому как можно случайно забыть явно вызвать Dispose. А using, как известно, вызовет его сам.

    А вот по второму вопросу - боюсь, не смогу подсказать единственно верного решения. Я сам как-то столкнулся с такой проблемой, и долго думал, как ее решить. Особенно когда идет использование IoC-контейнеров. На одном из проектов компании, где сейчас работаю, используется такой подход: создается синглтон, у которого в качестве свойств перечислены интерфейсы, например:

    public sealed class Endpoint
        {
            ...
            // Закрытый конструктор
            private Endpoint()
            {
                Initialization();
            }
            ...
            
            // Реализация синглтона
            public static Endpoint Instance
            {
                get
                {
                    if (_instance == null)
                    {
                        lock (InstanceLocker)
                        {
                            if (_instance == null)
                                _instance = new Endpoint();
                        }
                    }
    
                    return _instance;
                }
            }
            ...
            public IMyService MyService { get; private set; }
            public ISomeService SomeService { get; private set; }
            ...


    И затем эта некая общая "точка доступа" используется следующим образом:

    var serviceResult = Endpoint.Instance.MyService.GetData();

    Т.к. это синглтон, то обращение Endpoint.Instance всегда создаст единственную реализацию вместе со всеми необходимыми сервисами.
    Из плюсов - доступ из любого места (в рамках разумного, конечно же) к любому сервису.
    Ответ написан
  • Что возвращать, Empty collection или null?

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

    Правильным будет следующий вариант:

    public IEnumerable<Adresses> GetAdresses()
            {
                try
                {
                     return dbContext.Adresses.ToList();
                }
                catch (Exception ex)
                {
                    // Логируем ошибку - что-то вроде:
                    //_loggerService.Error(ex);
                    throw;
                }
            }


    А снаружи, например, у Вас будет метод, который при ловле исключения будет выдавать что-то осознанное пользователю:
    public ActionResult Addresses()
            {
                try
                {
                     var addressList = _dataService.GetAdresses();
                     return View("_Addresses", addressList);
                }
                catch (Exception ex)
                {
                    TempData["Error"] = "Не удалось загрузить список адресов. Попробуйте позже."
                     return View("_Addresses");
                }
            }


    Во View я бы тоже советовал всегда делать проверку на null, потому что по независящим от Вас причинам Model спокойно может быть null. И лучше на клиенте такие ситуации обрабатывать, чтобы у Ваших пользователей не было ступора и они не перестали пользоваться Вашим веб-приложением.
    <div>
    @if(Model != null && Model.Any())
    {
       <div>
           @foreach(var address in Model)
           {
               @*Здесь вывод Ваших адресов*@
           }
       </div>
    }
    else
    {
        <div>
            @TempData["Error"]
        </div>
    }
    </div>
    Ответ написан
    Комментировать
  • Какая должна быть структура SQL запросов, учитывая текущего пользователя?

    Valeriy1991
    @Valeriy1991
    Разработчик .NET C# (ASP.NET MVC) в Alfa-B, Moscow
    Добрый день! Возможно, имеет смысл взять MembershipAPI из коробки и особо не париться с моделью пользователей? А мыслите, в принципе, в правильном направлении. Поддержу Станислав Макаров - если вы делаете update и delete по id записи, то и переписывать все эти запросы не надо. Возможно, использование ORM Вам упростит жизнь.
    Ответ написан
    1 комментарий
  • Авторизация для SignalR?

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

    Я, к сожалению, такую задачу не решал, но могу предположить, что логику авторизации в методе действия, который занимается авторизацией, можно просто продублировать в метод C# внутри хаба SignalR. Этот метод будет принимать, например, string username и string password, и дальше с ними работать.

    Плюс (Вы наверняка знаете) на www.asp.net есть статьи, посвященные модели безопасности SignalR и авторизации с помощью SignalR:

    www.asp.net/signalr/overview/security/introduction...
    www.asp.net/signalr/overview/security/hub-authorization
    www.asp.net/signalr/overview/security/persistent-c...

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

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

    Хотелось бы добавить про контексты EF: stackoverflow.com/questions/9415955/c-sharp-workin...

    Поясню: когда я впервые столкнулся с масштабным применением EF, в том проекте использовался класс, в котором в виде поля объявлялся контекст. Т.е. контекст EF существовал все время, пока существовал этот класс (DataService). Так вот: все было хорошо до тех пор, пока не пришлось заняться ускорением работы приложения. И обнаружилось, что такой подход практически не позволяет использовать библиотеку TPL (Task Parallel Library), т.к. постоянно возникали ошибки, связанные с тем, что "контекст уже открыт". Ни в коем случае не используйте контекст EF по паттерну "Одиночка" и в статическом классе - наберетесь больших проблем и замучаетесь исправлять ошибки. Исходя из своего опыта (а также опыта моих коллег и друзей), пока что самым удачным применением EF является (как и написано в статье) использование контекста с коротким жизненным циклом, т.е. написали метод (который, например, получает список городов из БД и который, соответственно, будет внутри слоя доступа к данным), внутри инициализируем контекст (лучше с помощью using), получаем данные, оборачиваем их в модель (если надо), закрываем контекст и возвращаем данные наверх.

    P.S. Хотелось бы также порекомендовать Вам НЕ работать с классами, генерируемыми EF, в слое бизнес-логики, а писать для этих классов обертки (модели). В одном из проектов, которыми я занимался, используется WPF + MVVM + IoC (Unity Container), и в нем же весь интерфейс биндится к классам EF... Поверьте, это ужасно. Лучше сразу проектируйте так, чтобы классы EF у Вас использовались только внутри слоя доступа к данным, а наверху (в бизнес логике) вся работа ведется с классами-обертками над классами EF (т.е. чтобы классы слоя бизнес-логики ничего не знали о классах EF). В этом еще и такой плюс, что если Вы по каким-либо причинам решите отказаться от EF и перейти, например, на NHibernate или Native Sql, то все изменения коснутся только слоя доступа к данным.
    Ответ написан
    Комментировать
  • View model как правильно организовать?

    Valeriy1991
    @Valeriy1991
    Разработчик .NET C# (ASP.NET MVC) в Alfa-B, Moscow
    Всем добрый день! Хм, а чем не угодило наследование? Сделайте базовую модель представления - BaseViewModel, засуньте туда все основные свойства. Если Вам одни свойства нужны для Add, а другие для Edit, так просто одни поместите в AddViewModel, а другие - в EditViewModel. При этом общие свойства типа Id, Name и др. Вам также будут доступны в обеих ViewModel, т.к. и AddViewModel, и EditViewModel будут унаследованы от BaseViewModel.

    В защиту такого решения могу сказать следующее:
    1. Все общие свойства вынесены отдельно. Они доступны в любой момент. В случае изменения общих свойств их можно редактировать в одном месте.
    2. Специальные свойства, нужные только для конкретной ситуации (Add, Edit, Delete, ...), можно вынести в специальную модель представления.

    Зачем Вам усложнять себе жизнь дублированием кусков кода? Я думаю, не нужно объяснять, чем это грозит в случае изменений либо масштабируемости приложения...

    По поводу выбора между моделью и представлением: лично я после разработки на WPF с использованием шаблона MVVM использую ViewModel и в MVC. Это позволяет еще больше отделить данные от представления, добавив, как это говорится по-научному, дополнительный уровень абстракции. Так что тут я Вас тоже полностью поддерживаю.

    Успехов!
    Ответ написан
    Комментировать
  • Как вы реализовываете авторизацию в ASP.net MVC?

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

    Использую стандартный Membership API. Честно говоря, пока что не замечал за ним никаких ошибок в работе/глюков или странного поведения. Я не анализировал sql-код, описанный в хранимых процедурах Membership API, и потому не могу рассуждать о его "монструозности", но лично для меня у него есть следующие плюсы:
    1) можно быстро развернуть с помощью утилиты aspnet_regsql
    2) можно с помощью той же утилиты настроить его установку таким образом, чтобы установить только то, что Вам нужно (либо всё, либо только профили, либо роли и т.д.)
    3) имеется много информации по работе с ним (чего не скажешь про Identity, во всяком случае, книжек с Identity, в которых подробно бы описывались принципы работы с ним, я не обнаружил)
    4) с ним довольно просто и легко работать в коде (подробно можно прочитать в книжках "Microsoft ASP .NET 4 с примерами на c# 2010 для профессионалов" и "ASP.NET MVC 3/4 Framework с примерами на C# для профессионалов", особенно в первой книге, где в основном рассказывается про ASP.NET WebForms, очень подробно описана авторизация, аутентификация, членство, роли, профили - да все, что есть в ASP.NET, поэтому эту книгу стоит почитать. Вторая книжка - уже чисто по архитектуре ASP.NET MVC, и там основ членства, авторизации, ролей и прочего уже нет.)

    Но есть и минусы:
    1) использует стандартную схему БД - "dbo". Чтобы подружить его с другими схемами, нужно писать свой, кастомный membership provider.
    2) в нескольких источниках слышал о том, что он, как и сказал Alexey_Gagarin, "моструозен", что код sql, используемый в хранимых процедурах, не оптимизирован. Повторюсь, я не проверял, но мне его функциональности пока что вполне достаточно.

    Успехов!
    Ответ написан
    3 комментария
  • Как реализовать роут в ASP.net MVC?

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

    Если я Вас правильно понял, то маршрут будет такой:
    routes.MapRoute(
    	name: "",
    	url: "site/{parametr}",
    	defaults: new { controller = "Home", action = "Index", parametr = "index" }
    );

    Если Вам необходимо, чтобы значение parametr было равно "index" при запросе главной страницы сайта (т.е. пользователь запрашивает, например, "yoursite.ru"), то самым первым маршрутом должен быть следующий маршрут:
    routes.MapRoute(
    	name: "",
    	url: "",
    	defaults: new { controller = "Home", action = "Index", parametr = "index" }
    );

    Заметьте, что url пустой. Это соответствует шаблону URL вида "/", поэтому такой маршрут будет работать для корневого URL всего сайта, т.е. для "yoursite.ru".

    Для такого маршрута Ваш метод действия должен выглядеть примерно так:
    public ActionResult Index(string parametr)
    {
    	if(parametr != null)
    	{
    		switch(parametr)
    		{
    			case "index":
    				return View();
    			default:
    				//return RedirectToAction("IndexOther", "Home");
    				//или
    				return View("IndexOther");
    		}
    	}	
    	// Здесь что-то происходит...
    	return View(); 
    }

    либо можно указать переменную parametr со значением по-умолчанию в самом методе действия:
    public ActionResult Index(string parametr = "index")
    {
    	switch(parametr)
    	{
    		case "index":
    			return View();
    		default:
    			//return RedirectToAction("IndexOther", "Home");
    			//или
    			return View("IndexOther");
    	}
    	// Здесь что-то происходит...
    	return View();
    }


    Честно скажу, я не проверял эти маршруты в, так сказать, рабочей среде. Если у Вас не получится, тогда сообщите об этом, сделаю тестовый проект, проверю.

    Надеюсь, смог Вам помочь.
    Успехов!
    Ответ написан
  • Как передать определенный параметр элемента в списке?

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

    1. Я, возможно, что-то немного не понял, но использовать тип string для поля Id - плохая практика. Используйте int вместо string.
    2. Называйте переменные правильно: если у Вас список ответов, то лучше вместо "Answer" назовите Answers" или "AnswerList" - старайтесь писать такой код, чтобы с ним после Вас мог легко работать другой программист.
    3. (на всякий случай) Для сообщений валидации можно использовать атрибуты (Required, MaxLength, ...) - я заметил, что в ValidationMessageFor Вы используете "" для текста сообщения валидации.
    4. В классе Answer у Вас есть свойство Chosen - если я правильно понимаю, это "Выбран", т.е. это логическая переменная, которая может быть либо "Да", либо "Нет". Зачем используете тип int, ведь есть тип bool?
    4. Не знаю, как у Вас, но не заполнив коллекцию Answer(s) в модели PollModel на сервере, Вы не сможете обращаться к ней через Single(), потому что в методе действия Create, который Вы привели, модель не заполняется, соответственно, коллекция Answer(s) равна null, в результате - ошибка, т.к. у null-объекта нельзя обращаться к свойствам. Поэтому Вам нужно как минимум сформировать модель на сервере, например, так:
    [HttpGet]
    public ActionResult Create()
    {
         List<Answer> answers = new List<Answer>();
         answers.Add(new Answer() { Id = 1, QuestionId = 1, Text = "Вопрос №1", Chosen = false });
         PollModel viewModel = new PollModel()
         {
              Id = "1", 
              UserIdCreated = "user1",
              Answers = answers
         };
         return View(viewModel);
    }

    5. Т.к. у Вас коллекция Answer(s), то для правильной привязки нужно правильно сформировать View, потому что привязка к коллекциям в ASP.NET MVC осуществляется особым образом - если посмотрите на html-код, то увидите, что тег input, в который нужно ввести текст, имеет атрибут name="Answers[0].Text и атрибут id="Answers_0__Text". Так работает связыватель модели (Model Binder) в ASP.NET MVC по-умолчанию .

    Рабочий пример - основан на шаблоне ASP.NET MVC 4 Internet Application в VS 2012:
    HomeController.cs:
    [HttpGet]
            public ActionResult Create()
            {
                List<Answer> answers = new List<Answer>();
                answers.Add(new Answer() { Id = 1, QuestionId = 1, Text = "Вопрос №1", Chosen = false });
                PollModel viewModel = new PollModel()
                {
                    Id = "1", 
                    UserIdCreated = "user1",
                    Answers = answers
                };
                return View(viewModel);
            }
    
            [HttpPost]
            public ActionResult Create(PollModel viewModel)
            {
                TempData["Message"] = String.Format("Текст Вашего вопроса - {0}", viewModel.Answers.First().Text);
                return View(viewModel);
            }

    Create.cshmtl:
    @using TosterQuestion101113_MVC.Models;
    @model PollModel
    
    @{
        ViewBag.Title = "Create";
        Layout = "~/Views/Shared/_Layout.cshtml";
    }
    
    <h2>Create</h2>
    <div class="form-group">
        @using (Html.BeginForm("Create", "Home", FormMethod.Post, null))
        {
            if (TempData["Message"] != null)
            {
                <div class="message">
                    @TempData["Message"]
                </div>
            }
            
            @Html.LabelFor(e => e.Answers, new { @class = "control-label col-md-2" })
            for (int i = 0; i < Model.Answers.Count(); i++)
            {
                <div class="col-md-10">
                @Html.EditorFor(e => e.Answers[i].Text, new { htmlAttributes = new { @class = "form-control" } })
                @Html.ValidationMessageFor(e => e.Answers.Single().Text, "", new { @class = "text-danger" })
            </div>
            }
            
            <div>
                <input type="submit" value="ОК!" />
            </div>
        }
    </div>


    Замечания:
    1. Свойство IEnumerable<Answer> Answer изменено на IList<Answer> Answers - потому, что Ilist предоставляет доступ к методу Count() для реализации перечисления в цикле
    2. Answer.Single().Text изменено на Answers[i].Text
    3. И вообще - у меня сложилось впечатление, что Вы начали недавно изучать ASP.NET MVC и в том числе не так давно программируете. Поэтому я вынужден настаивать: читайте от корки до корки книгу "ASP.NET MVC3 (либо 4) Framework с примерами на C# для профессионалов" - там всё просто отлично объяснен ASP.NET MVC.

    Надеюсь, я Вам немного помог.
    Успехов!
    Ответ написан
    Комментировать
  • Посоветуйте курсы по ASP.NET в Москве

    Valeriy1991
    @Valeriy1991
    Разработчик .NET C# (ASP.NET MVC) в Alfa-B, Moscow
    Добрый день! Отвечу несмотря на то, что вопрос задан в далеком 2012. В свое время посещал курс "Программирование на C#" в учебном центре "Специалист" при МГТУ им. Баумана - specialist.ru. Мне понравилось, поэтому могу посоветовать этот учебный центр. Список всех курсов по Visual Studio - www.specialist.ru/product/visual-studio-courses.
    Ответ написан
    Комментировать