Как эффективно запрашивать идентификационные данные в ASP.Net Core? - PullRequest
0 голосов
/ 15 января 2019

Фон

У меня есть сайт, написанный на ASP.NET Core v2.1.1.

У меня есть пользовательский идентификационный класс:

public class FooIdentityUser : IdentityUser<string>, IIdentityModel
{
    [MaxLength(50)]
    public string FirstName { get; set; }
    [MaxLength(50)]
    public string LastName { get; set; }

    public string FullName => $"{FirstName} {LastName}";

    public bool FooBool { get; set; }
}

и пользовательский класс роли идентификации:

public class FooIdentityRole : IdentityRole<string>
{

}

На что я тогда ссылаюсь в dbcontext:

public class FooIdentityDbContext : IdentityDbContext<FooIdentityUser,FooIdentityRole,string>
{
    public FooIdentityDbContext(DbContextOptions<FooIdentityDbContext> options)
        : base(options)
    {
    }
}

Требование

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

В частности:

  • Я хочу предоставить список пользователей, которые находятся в роли foo
  • И / или я хочу перечислить всех пользователей, для которых для FooBool установлено значение true
  • И / или я хочу запросить адрес электронной почты, имя и фамилию
  • и / или проводить сортировку

Вопрос

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

Подходы / Исследования

Из того, что я вижу, есть два подхода к этому:

Подход 1

Поскольку я хочу составить список пользователей специально для роли пользователя на основе представления, я вижу, что диспетчер пользователей предоставляет метод для этого:

_userManager.GetUsersInRoleAsync(fooRoleName)

Проблема, с которой я столкнулся, заключается в том, что он возвращает IList, поэтому, в то время как он вернет всех пользователей с этой ролью, если я захочу сделать запрос по FooBool и / или FirstName, LastName или Email Address, ему нужно будет циклически проходить по списку. отфильтровать их, что было бы неэффективно, если бы было 10 тысяч или 100 тысяч пользователей?

В идеале это вернет IQueryable, поэтому он не попадет в базу данных, пока не будет применен мой метод where и order by, но я не могу найти способ сделать это?

Подход 2

Другой способ - запросить контекст напрямую через мой общий репозиторий.

public class GenericIdentityRepository<TModel> : IIdentityRepository<TModel> where TModel : class, IIdentityModel
{
    private readonly ILogger _logger;
    public FooIdentityDbContext Context { get; set; }
    private readonly DbSet<TModel> _dbSet;

    public GenericIdentityRepository(FooIdentityDbContext dbContext, ILogger<GenericIdentityRepository<TModel>> logger)
    {
        Context = dbContext;
        _logger = logger;
        _dbSet = Context.Set<TModel>();
    }

    public IQueryable<TModel> GetAll()
    {
        _logger.LogDebug("GetAll " + typeof(TModel));
        IQueryable<TModel> query = _dbSet;
        return query;
    }

    public IQueryable<TModel> GetAllNoTracking()
    {
        _logger.LogDebug("GetAllNotTracking " + typeof(TModel));
        IQueryable<TModel> query = GetAll().AsNoTracking();
        return query;
    }
}

Я хотел посмотреть, смогу ли я что-нибудь сделать, создав пользовательские классы для userrole, а затем используя linq, чтобы дать мне IQueryable?

public class FooIdentityUserRole : IdentityUserRole<string>
{
    public virtual FooIdentityUser User { get; set; }
    public virtual FooIdentityRole Role { get; set; }
}

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

1 Ответ

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

Я предлагаю использовать FooIdentityDbContext непосредственно в ваших контроллерах и просто запрашивать данные так, как вы хотите. Я не знаю, как вы могли бы достичь того, что вы хотите, используя класс UserManager. Может быть, но если честно, я бы не стал смешивать вещи. UserManager более полезен, когда вы имеете дело с одним пользователем и хотите что-то с ним сделать, например AddToRoleAsync или ChangePasswordAsync.

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

До фактического ответа: вы уже правильно настроили объекты, поэтому теперь просто введите FooIdentityDbContext и начните запрашивать. Примерно так:

public class HomeController : Controller
{
    private readonly FooIdentityDbContext_dbContext;

    public HomeController(FooIdentityDbContext dbContext)
    {
        _dbContext = dbContext ?? throw new ArgumentNullException(nameof(dbContext));
    }

    public async Task<IActionResult> UserList(string roleName, bool fooBool, string firstName)
    {
        // You are interested in Users on Roles, so it's easier to start on the UserRoles table
        var usersInRole = _dbContext.UserRoles.Select(userRole => userRole);

        // filter only users on role first
        if (!string.IsNullOrWhiteSpace(roleName))
        {
            usersInRole = usersInRole.Where(ur => ur.Role.Name == roleName);
        }

        // then filter by fooBool
        usersInRole = usersInRole.Where(ur => ur.User.FooBool == fooBool);

        // then filter by user firstname or whatever field you need
        if (!string.IsNullOrWhiteSpace(firstName))
        {
            usersInRole = usersInRole.Where(ur => ur.User.FirstName.StartsWith(firstName));
        }

        // finally materialize the query, sorting by FirstName ascending
        // It's a common good practice to not return your entities and select only what's necessary for the view.
        var filteredUsers = await usersInRole.Select(ur => new UserListViewModel
        {
            Id = ur.UserId,
            Email = ur.User.Email,
            FooBool = ur.User.FooBool,
            FirstName = ur.User.FirstName
        }).OrderBy(u => u.FirstName).ToListAsync();

        return View("UserListNew", filteredUsers);
    }
}

Бонус : Я читал книгу EF Core в действии *1018* Джона Смита, и это здорово. Я настоятельно рекомендую прочитать его, если вы хотите продолжать использовать EF Core в своих проектах. Он полон хороших советов и примеров из реального мира.

...