Подтвердить что ты не робот

Разделение проблем и n-уровневая архитектура в ASP.NET 5/ASP.NET Core 1

Я изо всех сил пытаюсь найти изящный способ сохранить мой слой DAL отдельно от моего уровня MVC/UI в ASP.NET 5 благодаря новой встроенной инъекции зависимостей, которую я хочу использовать.

Например, у меня есть проект ASP.NET 5, проект бизнес-уровня и проект доступа к данным, в котором у меня есть другой код Entity Framework, такой как сущности и контексты. в ASP.NET 5, чтобы настроить контекст и настроить таргетинг на базу данных, основная документация предполагает, что я делаю что-то подобное в своем классе StartUp.cs

services.AddEntityFramework()
    .AddSqlServer()
    .AddDbContext<BookContext>(options =>
    {
        options.UseSqlServer(Configuration.Get("Data:ConnectionString"));
    });

Это означает, что теперь мне приходится ссылаться на мой DAL в том, что в основном является моим слоем пользовательского интерфейса, что в течение многих лет всегда было плохим практикой в ​​соответствии с различными экспертами и сообщениями в блоге.

Один из способов, с которым я работал, - создать два новых проекта: проект CompositeRoot, содержащий классы factory для создания моих бизнес-классов, которые затем обращаются к DAL, а также проект Utilities с классом Configuration, в котором a ConnectionString, которое я могу передать в свой контекст, я затем использую встроенный DI, чтобы связать все и не ссылаться на мой DAL в моем слое пользовательского интерфейса. Но я столкнулся с проблемами с последней версией Entity Framework (бета-версия 7), так как теперь не представляется возможным указать строку подключения либо в конструкторе контекста, либо в переопределяемом методе OnConfiguration. Кроме того, вся документация до сих пор, похоже, не заботится об этом смешении проблем вообще. Так ли это сейчас мы делаем? Доверяйте, что разработчики не будут делать "плохие" вещи, например, ссылки на классы DAL в пользовательском интерфейсе? Или существует шаблон, который люди используют для хранения вещей SOLID с помощью этой новой встроенной DI/конфигурации для ASP.NET 5?

4b9b3361

Ответ 1

Если вы попросите 10 гражданских архитекторов построить вам мост, вы получите 10 разных архитектур. Не один будет таким же, и никто не будет лучше другого.

Независимо от того, сколько наилучших методов и шаблонов дизайна они применяют, каждый архитектор будет оправдывать свою идею. Некоторые из них будут чрезмерно усердными, в то время как другие будут держать их простыми и выполнять свою работу. Среди прочего, бюджет, сроки поставки и мастерство будут напрямую влиять на тип архитектуры, которую вы решаете.

То же правило применяется к архитекторам программного обеспечения.

Я видел, что моя справедливая доля архитекторов, имеющих лучшие намерения в мире, только осознала, что уровень пользовательского интерфейса имеет зависимость от DAL. Возможно, причина в том, что:

  • Они не думали об этом
  • Они не заботятся об этом
  • Это облегчает DI, так как вы видите каждый слой, который в свою очередь делает это легко сопоставить интерфейсы с их реализацией.

В MVC 5 у меня были следующие слои:

-Contoso.Core (Class Library)
-Contoso.Data (Class Library)
-Contoso.Service (Class Library)
-Contoso.Web (asp.net MVC 5)
-Contoso.Web.Configuration (Class Library)

Уровень Web.Configuration имел зависимость от Core, Data и Service. Этот слой - это то место, где я бы настроил свой материал DI.

У слоя Web не было зависимости от уровня Data, и чтобы начать загрузку, я использовал пакет Nutet Bootstrapper.

Возможно, вы могли бы достичь чего-то подобного с ASP.NET 5

Мне нравится Microsoft (или кто-либо) создать рабочий шаблон проекта, который больше ориентирован на выборки уровня предприятия с использованием развязанного подхода или даже что-то вроде примера архитектуры Onion.

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

Не стесняйтесь делиться своими выводами, как мне любопытно узнать, как вам удалось заставить его работать.

Ответ 2

Это действительно серьезная проблема?

Уровень UI будет зависеть от вашего уровня данных, и где-нибудь будет какая-то ссылка.

В StartUp.cs добавляются различные службы и конфигурация, поэтому большинство этих ссылок находятся в одном месте.

Ответ 3

Вы можете обойти это, используя Locator.

Например, у вас есть интерфейс в проекте root/core/abstractions:

public interface IServiceConfiguration
{
    void ConfigureServices(IServiceCollection services, IConfigurationRoot configuration);
}

В вашем StartUp.cs найдите все типы, реализующие IServiceConfiguration, и используйте их для регистрации внешних служб.

public void ConfigureServices(IServiceCollection services)
{
    var currentAssembly = typeof(Startup).Assembly;

    // Limit to project assemblies
    var @namespace = currentAssembly.FullName.Split(',')[0].Split('.')[0];
    var assemblies = currentAssembly.GetReferencedAssemblies()
                            .Where(a => a.Name.StartsWith(@namespace, StringComparison.Ordinal))
                            .Select(a => Assembly.Load(a));

    foreach (var assembly in assemblies)
    {
        // Assembly.ExportedTypes is not supported in dnxcore50 so you need to take it off your frameworks node in project.json.
        foreach (var config in assembly.ExportedTypes.Where(t => typeof(IServiceConfiguration).IsAssignableFrom(t) && !t.IsAbstract))
        {
            ((IServiceConfiguration)Activator.CreateInstance(config)).ConfigureServices(services, Configuration);
        }
    }

    // More service registrations..
}

И в вашем DAL вы можете добавить класс, который будет регистрировать EF:

public sealed class EntityFrameworkServiceConfiguration : IServiceConfiguration
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddEntityFramework()
                    .AddSqlServer()
                    .AddDbContext<BookContext>(options =>
                    {
                        options.UseSqlServer(Configuration.Get("Data:ConnectionString"));
                    });
    }
} 

Тем не менее, не всем нужен такой развязке. В некотором смысле, имеет смысл просто регистрировать все в StartUp.cs. В любом случае вы бы указали на свой DAL в веб-проекте (если все не происходит через динамическое обнаружение), а это значит, что он уже знает что-то о вашем DAL.

Ответ 4

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

Добавьте новый ClassLibraryPackage

Измените файл project.json

{
  "version": "1.0.0-*",
  "description": ":)",
  "authors": [ "JJVC" ],
  "tags": [ "" ],
  "projectUrl": "JJVCblog.com",
  "licenseUrl": "",

  "frameworks": {
    "dnx451": {
      "frameworkAssemblies": {
      },
      "dependencies": {
        "EntityFramework.Commands": "7.0.0-rc1-final"
      }
    }
  },

  "commands": {
    "ef": "EntityFramework.Commands"
  },

  "dependencies": {
    "EntityFramework.MicrosoftSqlServer": "7.0.0-rc1-final",
    "Microsoft.AspNet.Identity": "3.0.0-rc1-final",
    "Microsoft.AspNet.Identity.EntityFramework": "3.0.0-rc1-final"
  }
}

Сделайте свой код в первую очередь

    public class ApplicationDbContext : IdentityDbContext<ApplicationUser, ApplicationRole,string>
        {

            protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
            {

                optionsBuilder.UseSqlServer(@"server=localhost;Database=Whatever;Integrated Security=True;MultipleActiveResultSets=True;Connect Timeout=60;");
            }
            DbSet<ForgotPasswordResetHistory> ForgotPasswordResetHistory { get; set; }

            protected override void OnModelCreating(ModelBuilder builder)
            {

                base.OnModelCreating(builder);
                base.OnModelCreating(builder);

            }



        }
 public class ForgotPasswordResetHistory
    {
        [Key]
        public int ForgotPasswordResetHistoryId { get; set; }
        public bool UserTriedToResetPassword { get; set; }
        public DateTime LinkExpirationDate { get; set; }
        public string SecretCodeForTheLink { get; set; }
        public DateTime PasswordRecoveryRequestDate { get; set; }
        public ApplicationUser ApplicationUser { get; set; }
        public int Test { get; set; }
    }

Запустите команду cmd в контексте вашей папки. восстановление dnu dnx ef migration anyurmigrationnameis Обновление базы данных dnx ed

Теперь перейдите в свой "веб-проект" и ссылку на ваш db. В вашем веб-проекте "project.json" вы должны увидеть, что он вызывается в зависимостях.

 "ConsoleApp2.Database": "1.0.0-*"

Удачи!

Ответ 5

Я столкнулся с той же проблемой при работе над демо для MVC 6 сегодня. после некоторых исследований/проб и ошибок, когда я удалил

"EntityFramework.SqlServer": "7.0.0-beta8"

из файла project.json все работает отлично со мной.

Ответ 6

Недавно я был на Global Labure Bootcamp 2016 года, и примерный проект, над которым мы работали (чисто с точки зрения Azure), имеет такое разделение проблем, которые изложены очень хорошо. Я не потратил много времени на анализ проекта, но если кто-то захочет посмотреть и посмотреть, подходит ли это подходящий раскол N-Tier, пожалуйста, отправьте его обратно. Ссылка http://opsgilitytraining.blob.core.windows.net/public/modern-cloud-apps-student.zip. Разделения распределяются следующим образом:

  • Contoso.Apps.SportsLeague.Web Приложение электронной коммерции Contoso Sports League
  • Contoso.Apps.SportsLeague.Admin приложение для администрирования call-центра Contoso Sports League
  • Contoso.Apps.SportsLeague.WorkerRole Обрабатывает генерацию генерации для заказов
  • Contoso.Apps.SportsLeague.Data​​strong > Уровень данных
  • Contoso.Apps.SportsLeague.Offers API для возврата списка доступных продуктов
  • API Contoso.Apps.PaymentGateway для обработки платежей

Ответ 7

Причина, по которой есть ссылка на слой данных из уровня пользовательского интерфейса, заключается в том, что нам нужно зарегистрировать DbContext (если вы используете EF) в контейнере IoC.

Вы можете обойти эту ссылку на проект/пакет, используя MEF (Manage Extensibility Framework). Проверьте реализацию MEF в .NET Core, так как он изменился с предыдущей .NET Framework.

В MEF вы можете сделать это по соглашению или по конфигурации.

Ответ 8

вы можете реализовать методы расширения для IServiceCollection на уровне вашего бизнеса и на вашем уровне доступа к данным.

то в Web Startup вы можете вызвать методы расширения на бизнес-уровне, которые, в свою очередь, вызовут методы расширения на вашем уровне данных

чтобы этот код мог находиться в расширении (-ях) уровня данных:

services.AddEntityFramework()
                    .AddSqlServer()
                    .AddDbContext<BookContext>(options =>
                    {
                       options.UseSqlServer(Configuration.Get("Data:ConnectionString"));
                   });

который будет разделен на вещи, и веб-серверу требуется только использование операторов в файле запуска для ссылок на бизнес-уровень