如何在ASP.Net Core中有效地查询身份数据?

时间:2019-01-15 10:58:42

标签: c# linq asp.net-core asp.net-core-mvc asp.net-identity

背景

我有一个用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上进行查询,则需要循环浏览列表过滤掉这些,如果有成千上万的用户或成千上万的用户,那将是低效的?

理想情况下,这将返回一个IQueryable,因此在应用了我的位置和顺序之前,它不会打入数据库,但是我找不到解决方法?

方法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,我需要这样做。

2 个答案:

答案 0 :(得分:1)

我的建议是直接在您的控制器中使用FooIdentityDbContext并以所需的方式查询数据。我不知道您可以使用UserManager类来实现所需的方法。也许有,但老实说,我不会混在一起。当您与单个用户打交道并想使用诸如UserManagerAddToRoleAsync之类的东西时,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);
    }
}

奖金:我一直在阅读乔恩·史密斯(Jon Smith)的EF Core in Action书,这太好了。如果您想在项目中继续使用EF Core,我强烈建议您阅读它。它包含许多不错的技巧和现实示例。

答案 1 :(得分:0)

使用.Users。

await _userManager.Users.Where(w => w.LastChangeUserId == null).ToListAsync();