Данные пользователя в конструкторе моделей DbContext - PullRequest
0 голосов
/ 22 января 2019

Я новичок в идентификации бритвенных страниц / efcore / aspnet и пытаюсь это выяснить, но это меня бьет.

В основном я использую AspNet Identity для аутентификации и авторизации пользователей. Я расширил AspNetUsers дополнительным идентификатором OrganisationId, который является FK для организации; и добавил идентификатор в качестве претензии в хранилище идентификационных претензий. Это отлично работает.

Теперь мне нужно установить глобальный фильтр efcore на основе идентификатора организации аутентифицированного пользователя, чтобы он мог просматривать только данные, назначенные его организации.

Однако я не могу получить доступ к аутентифицированным данным пользователя в ModelBuilder.

public class SDMOxContext : IdentityDbContext<
        ApplicationUser, ApplicationRole, string,
        ApplicationUserClaim, ApplicationUserRole, ApplicationUserLogin,
        ApplicationRoleClaim, ApplicationUserToken>
    {

        public SDMOxContext(DbContextOptions<SDMOxContext> options)
            : base(options)
        { }

        protected override void OnModelCreating(ModelBuilder builder)
        {

            base.OnModelCreating(builder);
        // Set global filter so users can only see projects within their organisation.
        builder.Entity<Project>().HasQueryFilter(project => project.OrganisationId == 1);

    }

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

User.FindFirstValue("OrganisationId")

Однако пользователь не существует в текущем контексте.

Ответы [ 2 ]

0 голосов
/ 23 января 2019

Так что мне нужно применить фильтр запросов на более позднем этапе, т.е.после аутентификации пользователя?Любые указатели, с чего начать с подхода среднего уровня / логического уровня?

Конечно, это мнение об архитектуре, но я разделяю его следующим образом:

Уровень данных- ответственность этого уровня за доступ к ресурсам (обычно) за пределами исполняемого приложения.Это включает;Базы данных, файловый ввод-вывод, веб-API и т. Д.

Business / Logic-Tier - ответственность этого уровня (которая может быть разбита далее) должна проходить проверку подлинности, авторизацию, проверку и создание объектов , представляющихбизнес нуждается.Для создания этих объектов он может использовать один или несколько объектов доступа к данным (например, он может использовать IO DA для получения изображения из локальной файловой системы или хранилища Azure и базу данных DA для получения метаданных об этом изображении).

Presentation / Exposure-Tier - ответственность этого уровня заключается в том, чтобы обернуть и преобразовать объект в потребности потребителя (winforms, wpf, html, json, xml, двоичная сериализация и т. Д.).

Оставив логику внена уровне данных (даже в мультитенантных системах) вы получаете возможность доступа к данным во всех системах (и, поверьте мне, здесь можно заработать много денег).

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

Уровень данных

namespace ProjectsData
{
  public interface IProjectDA 
  { 
    IProjectDO GetProject(Guid projectId, Guid organizationId);
  }
  private class ProjectDA : DbContext, IProjectDA
  {
    public ProjectDA (...)
    public IEnumerable<ProjectDO> Projects { get; set; }
    protected override void OnModelCreating(ModelBuilder builder) {... }
    public IProjectDO GetProject(Guid projectId, Guid organizationId)
    {
      var result = Projects
        .FirstOrDefault(p => p.Id == projectId && OrganizationId = organizationId);
      return result;
    }
  }

  public interface IProjectDO{ ... }
  private class ProjectDO: IProjectDO
  {
    public Guid Id { get; set; }
    public Guid OrganizationId { get; set; }
    public Guid CategoryId { get; set; }
  }
}

Логика

namespace ProjectBusiness
{
  public interface IProjectBO { .. }
  public interface IOrganization 
  { 
    Guid OrganizationId { get; }
  }
  private class ProjectBA : IProjectBO
  {
    private readonly IProjectDA _projectDA;
    private readonly IIdentity _identity;
    private readonly IOrganization _organization;
    public  ProjectLogic(IProjectDA projectDA, 
      IIdentity identity,
      IOrganizationContext organizationContext)
    {
      _projectDA = projectDA;
      _identity = identity;
    }
    public IProjectBO GetProject(Guid id)
    {
      var do = _projectDA
        .GetProject(id, _organization);

      var result = map.To<ProjectBO>(do);

      return result;
    }
  }
  public interface IProjectBO { .. }
  private class ProjectBO 
  { 
    public Guid Id { get; set; }
    public Guid OrganizationId { get; set; }
    public Guid CategoryId { get; set; }
  }
}

Итакпри этих условиях уровень данных знает тип запроса, но не мультитенант .Это не ограничивает все запросы, основанные на чем-либо.Эта архитектура имеет ряд преимуществ.

Во-первых, в приведенном выше примере ваш продукт взлетает, и ваш руководитель хочет узнать, какие категории наиболее популярны.

namespace StatisticsBusiness
{
  public interface IStatisticsBO 
  {
    IEnumerable<ICategoryStatisticBO> CategoryStatistics { get; set; }
  }
  public interface ICategoryStaticBO
  {
    Guid CategoryId { get; }
    int ProjectCount { get; }
  }
  private class StatisticsBA : IStatisticsBO
  {
    private readonly IProjectDA _projectDA;
    private readonly IIdentity _identity;
    public  ProjectLogic(IProjectDA projectDA, 
      IIdentity identity)
    {
      _projectDA = projectDA;
      _identity = identity;
    }

    public IEnumerable<IProjectBO GetOrderedCategoryPopularity()
    {
      var dos = _projectDA
        .GetProjectCategoryCounts()

      var result = map.To<IEnumerable<IStatisticsBO>>(dos);

      return result;
    }
  }
  public interface IStatisticsBO{ .. }
  private class StatisticsBO 
  { 
    public Guid CategoryId { get; }
    public int ProjectCount { get; }
  }
}

Примечание. Некоторые люди предпочитают передавать выражение в качестве предиката.Оба имеют свои преимущества и недостатки.Если вы решите пойти по пути предиката, вам придется решить, используют ли все ваши типы доступа к данным предикаты или нет.Просто осознайте, что использование предикатов против IO или Web Api может потребовать больше усилий, чем оно того стоит.

Во-вторых, некоторые требования приводят к невозможности использования Entity Framework.Вы заменяете его Dapper или другой новой лучшей технологией / фреймворком.Все, что вам нужно, это создать новые классы I<whataver>DA, потому что логика потребления ничего не знает, кроме этих интерфейсов ( программирование с использованием интерфейса , L в принципах программирования SOLID и Я в принципах твердого программирования ).

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

0 голосов
/ 22 января 2019

Я предложу разложить решение на две части.

  1. Добавьте идентификатор организации в свой dbcontext, очень похожий на идентификатор арендатора в мультитенантной среде.См., Например, ссылку .

  2. Следующая задача - передать идентификатор организации в качестве параметра конструктору DbContext.Для этого вы можете создать фабрику для DbContext.Так как вы храните идентификатор организации в претензиях.Фабрика может получить доступ к тому же утверждению HttpContext и передать идентификатор организации в качестве параметра при запуске dbContext.

Это не идеально, но может дать вам отправную точку.

...