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



背景

我有一个用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>
{
}

然后我在数据库上下文中引用:

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或电子邮件地址,它需要在列表中循环以过滤掉这些,如果有10万或100万的用户,这将是低效的?

理想情况下,这将返回一个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,我需要这样做。

我的建议是直接在控制器中使用FooIdentityDbContext,并按照您想要的方式查询数据。我不知道有什么方法可以使用UserManager类实现您想要的。也许有,但老实说,我不会把事情搞混。当您与单个用户打交道并想用它做一些事情(如AddToRoleAsyncChangePasswordAsync)时,UserManager更有用。

直接使用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核心在行动》一书,这本书很棒。如果你想在项目中继续使用EF Core,我强烈建议你阅读它。它充满了美好的提示和现实世界的例子。

使用.Users.

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

相关内容

  • 没有找到相关文章

最新更新