Как получить список IdentityUsers и их роли одновременно? - PullRequest
0 голосов
/ 06 сентября 2018

Модель сообщения чата:

public class ChatMessage
{
    [Key] public long Id { get; set; }

    [Required] public string Content { get; set; }
    [Required] public DateTime TimePosted { get; set; }

    public string AuthorId { get; set; }
    [Required, ForeignKey("authorId")] public ApplicationUser Author { get; set; }
}

Теперь я хочу получить список сообщений чата за определенный день, зная, находится ли их автор в роли «Голос»:

var messages = await dbContext.ChatMessages.AsNoTracking()
    .Where(chm => chm.TimePosted.Date == someDateTime.Date).OrderBy(chm => chm.TimePosted)
    .ToListAsync();

var result = new List<Tuple<ChatMessage, bool>> { };
foreach(var m in messages)
    result.Add(Tuple.Create(m, await userManager.IsInRoleAsync(Author, "Voice")));

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

Я делаю это неправильно? Если да, то как мне это сделать?

Ответы [ 3 ]

0 голосов
/ 06 сентября 2018

Это то, что у меня сейчас есть. UNTESTED, но, похоже, не выдает ошибок компиляции:

string voiceRoleId = (await roleManager.FindByNameAsync("Voice")).Id;
var result = await dbContext.ChatMessages.AsNoTracking()
    .Where(chm => chm.TimePosted.Date == someDateTime.Date).OrderBy(chm => chm.TimePosted)
    .GroupJoin(
        dbContext.UserRoles.Where(ur => ur.RoleId == voiceRoleId),
        chm => chm.AuthorId, r => r.UserId,
        (chm, rs) => new { Message = chm, IsVoice = rs.Any() }
    ).ToListAsync();

Это ... несколько удивительно. Я думал, что задача будет проще, чем 8 строк кода.

0 голосов
/ 17 сентября 2018

Правильный подход, кажется, исходит из комментария *1001* @Kirk Larkin, который я преобразовываю в ответ, потому что он более заметен, чем комментарий, и потому что комментарии могут исчезнуть в любое время.

Он связал эту страницу документации: Перенос аутентификации и удостоверения в ASP.NET Core 2.0 # Добавление свойств навигации IdentityUser POCO , в котором говорится:

Базовые навигационные свойства Entity Framework (EF) базы IdentityUser POCO (Простой старый объект CLR) был удален. Если твой 1.x проект использовал эти свойства, вручную добавьте их обратно в проект 2.0: C #

/// <summary>
/// Navigation property for the roles this user belongs to.
/// </summary>
public virtual ICollection<IdentityUserRole<int>> Roles { get; } = new List<IdentityUserRole<int>>();

/// <summary>
/// Navigation property for the claims this user possesses.
/// </summary>
public virtual ICollection<IdentityUserClaim<int>> Claims { get; } = new List<IdentityUserClaim<int>>();

/// <summary>
/// Navigation property for this users login accounts.
/// </summary>
public virtual ICollection<IdentityUserLogin<int>> Logins { get; } = new List<IdentityUserLogin<int>>();

Чтобы предотвратить дублирование внешних ключей при запуске EF Core Migrations, добавьте следующее к вашему классу IdentityDbContext метод OnModelCreating (после base.OnModelCreating (); вызов): C #

protected override void OnModelCreating(ModelBuilder builder)
{
    base.OnModelCreating(builder);
    // Customize the ASP.NET Core Identity model and override the defaults if needed.
    // For example, you can rename the ASP.NET Core Identity table names and more.
    // Add your customizations after calling base.OnModelCreating(builder);

    builder.Entity<ApplicationUser>()
        .HasMany(e => e.Claims)
        .WithOne()
        .HasForeignKey(e => e.UserId)
        .IsRequired()
        .OnDelete(DeleteBehavior.Cascade);

    builder.Entity<ApplicationUser>()
        .HasMany(e => e.Logins)
        .WithOne()
        .HasForeignKey(e => e.UserId)
        .IsRequired()
        .OnDelete(DeleteBehavior.Cascade);

    builder.Entity<ApplicationUser>()
        .HasMany(e => e.Roles)
        .WithOne()
        .HasForeignKey(e => e.UserId)
        .IsRequired()
        .OnDelete(DeleteBehavior.Cascade);
}

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

var usersAndRoles = await dbContext.ChatMessages.AsNoTracking()
    .Include(msg => msg.Author).ThenInclude(author => author.Roles)
    .Select(msg => new { Message = msg, IsVoice = msg.Author.Roles.Contains("Voice") })
    .ToListAsync();
0 голосов
/ 06 сентября 2018

Вы должны иметь возможность Include таблиц ApplicationUser и Claims и делать все это в запросе.

Я не полностью протестировал это, но что-то вроде:

dbContext.ChatMessages
    .Include(i => i.Author)
    .Include(i => i.Author.Claims)
    .Where(w => w.Author.Claims.Any(a => a.ClaimType == ClaimTypes.Role 
                                         && a.ClaimValue == "Voice"));
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...