Truecolors Ответов: 1

Как оптимизировать загрузку 10 строк в datatable?


Я работаю над обработкой данных на стороне сервера, которая получает около 1000 поддельных / общих пользовательских данных из базы данных и загружает 10 строк на страницу. То, как я написал свой код прямо сейчас, занимает не менее 2 минут, чтобы просто показать первые 10 строк.

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

Что я уже пробовал:

private async Task<IEnumerable<UserViewModel>> GetUserData(ApplicationDbContext db)
{
    var users = db.Users;
    var list = await GetUsers(db, users);
    var vmlist = list.Select(item => new UserViewModel()
    {
        ID = item.ID,
        Username = item.Username,
        FirstName = item.FirstName,
        LastName = item.LastName,
        Email = item.Email,
        TaxID = item.TaxID,
        TaxIdHash = item.TaxIdHash,
        IV = item.IV,
        TaxIDEncrypted = item.TaxIDEncrypted,
        //RoleList = new List<string>(),
        //Roles = "",

        // This is the part giving me issues
        RoleList = RoleRepository.GetRolesByUserId(UserManager, item.ID, false),
        Roles = string.Join(",", RoleRepository.GetRolesByUserId(UserManager, item.ID, false).ToArray()),
    });

    return vmlist;
 }


Я вызываю метод GetUserData в этом методе UserGrid.

public async Task<ActionResult> UserGrid(DataTablesViewModel param)
{
    using (var db = new Infrastructure.Data.ApplicationDbContext())
    {
        db.Database.Log = s => System.Diagnostics.Debug.WriteLine(s);

    var data = await GetUserData(db);

    IQueryable<UserViewModel> filteredUsers = data.AsQueryable();        

    List<UserViewModel> displayedTasks = filteredUsers.Skip(param.iDisplayStart).Take(param.iDisplayLength).ToList();

    int count = filteredUsers.Count();
    foreach (var item in displayedTasks){
        if (!String.IsNullOrEmpty(item.TaxID) && item.IV != null && item.TaxIDEncrypted != null)
        {
            if (item.RoleList.Any(x => x == UserRoles.Applicant))
            {
                string ssndecrypt = GenericHelper.DecryptPortalTaxID(item.IV, item.TaxIDEncrypted);
                string ssn = (ssndecrypt.Length == 11 ? ssndecrypt.Substring(7, 4) : ssndecrypt);
                item.TaxID = (ssndecrypt.Length == 11 ? String.Join("", item.TaxID.Substring(0, 7), ssn) : ssn);}
        }
        else
        {
            item.TaxID = String.Empty;
        }
        item.Delete = item.RoleList.Intersect(UserRoles.CanDelete).Count() > 0 ?
            "<a class=\"delete\"  data-url=\"" + Url.Action("DeleteUser", "Users") + "\" data-userid=\"" + item.ID + "\"data-username=\"" + item.Username + "\" href=\"#\">Delete User</a>"
            : String.Empty;
    }

    var result = GridHelper.GetData(displayedTasks);
    var json = Json(new
    {
        sEcho = param.sEcho,
        iTotalRecords = count,
        iTotalDisplayRecords = count,
        aaData = result
    },
JsonRequestBehavior.AllowGet);
    return await Task.FromResult(json);
}


Мне понадобится список ролей, потому что я использую его позже для расшифровки зашифрованного SSN и отображения его последних 4 номеров.

Это UserViewModel, если это необходимо.

public UserViewModel(IList<string> roles, ApplicationUser user)
        {
            ID = user.Id;
            Username = user.UserName;
            Email = user.Email;
            RoleList = roles.ToList(); ;
            Roles = string.Join(",", RoleList.ToArray());
            IsActive = user.IsActive;

            AllowRoleChanges = !RoleList.Intersect(UserRoles.FinalConsumers).Any();
            AssignableRoles = HousingServices.Core.Models.UserRoles.CanCreate.Select(x => new SelectListItem { Text = x, Value = x }).ToList();
            if (RoleList.Contains(UserRoles.Caseworker))
            {
                AssignableRoles.Add(new SelectListItem { Value = UserRoles.Caseworker, Text = UserRoles.Caseworker });
            }
            RoleIDs = RoleList.Select(x => x).ToList();
          ..........

        }
        
    }


private async Task<IEnumerable<dynamic>> GetUsers(ApplicationDbContext db, IDbSet<ApplicationUser> users)
        {
            IQueryable<ApplicationUser> userlist = users;
          <pre>var hsuserlist = userlist.Join(db.EliteHousingServicesUsers, u => u.Id, hu => hu.UserId, (u, hu) => new
            {
                ID = u.Id,
                Username = u.UserName,
                FirstName = hu.FirstName,
                LastName = hu.LastName,
                Email = u.Email,
                TaxID = "XXX-XX-XXXX",
                Active = (u.IsActive ? "Yes" : "No"),
                TaxIdHash = hu.TaxIDHash,
                IV = hu.IV,
                TaxIDEncrypted = hu.TaxIDEncrypted,
                IsActive = u.IsActive,
                Status = (u.AccessFailedCount >= MaxFailedAccessAttemptsBeforeLockout ?
                "<a class=\"unlock\" data-url=\"" + unlockUrl + "\" data-userid=\"" + u.Id + "\"data-username=\"" + u.UserName + "\" href=\"#\">Unlock</a>"
                : "<a class=\"reset\" data-url=\"" + resetUrl + "\" data-userid=\"" + u.Id + "\"data-username=\"" + u.UserName + "\" href=\"#\">Reset Password</a>"),
            });

1 Ответов

Рейтинг:
0

Dave Kreskowiak

Ну, я не знаю, какое отношение роли имеют к вашему коду, но вы можете сэкономить массу времени, немного реструктурировав свою базу данных. SSN должен быть расшифрован, чтобы получить последние 4 цифры, наименее защищенную часть строки. Так что не расшифровывайте его. Сохраните последние 4 в виде отдельного столбца и извлеките его вместо полного SSN. Вы можете пропустить всю операцию расшифровки, сэкономив себе массу времени.


[no name]

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

PIEBALDconsult

Если вы разработчик, вы можете делать все, что угодно.

[no name]

Я имел в виду, что не могу изменить эту часть проекта. И я не "Дев", как говорится... просто стажер.

Dave Kreskowiak

Хорошо, тогда закомментируйте эти три строки кода, которые расшифровывают SSN,и поместите фиктивные данные, просто простую строку, в элемент.Таксид. Вы намеренно пропускаете этап расшифровки для проверки производительности.

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

[no name]

Проблема в том, что он работает медленно, заключается не в расшифровке. Она заключается в получении ролей для каждой пользовательской части.

Dave Kreskowiak

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

    if (item.RoleList.Any(x => x == UserRoles.Applicant))

Вы загрузили все возможные роли в контекст до того, как они были загружены? Включили ли вы роли с загруженными вами элементами или они лениво загружены, когда вы нажмете эту кнопку if заявление? Если они лениво загружены, то это круговая поездка в базу данных для каждого элемента в вашем цикле.


Возвращаясь к вашему методу GetUserData, у вас есть второй вызов базы данных для GetRolesByUserId. Это еще одна поездка туда и обратно в базу данных. Она должна быть .Включить в исходный запрос. Это может быть запрос отложенного выполнения, который выполняется, когда список необходим в вашем цикле, а не когда вы возвращаете список vmUsers.

У меня такое чувство, что вы не знакомы с тем, как EF выполняет ленивую загрузку и отложенное выполнение запросов. Это может быть то, что отключает ваш код, поскольку он, вероятно, попадает в базу данных несколько раз на протяжении всего кода цикла.

[no name]

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

Dave Kreskowiak

Да, роли есть в базе данных. Проблема в том, что они не загружаются в ваш код, за исключением тех случаев, когда это необходимо. Вот в чем проблема производительности. Ваш код загружает только роли, связанные с одним item только в случае необходимости. Это приводит к еще одному обходу базы данных по каждому пункту. Вам нужно переписать этот код, чтобы загрузить пользователей и их роли в один запрос. Вот где это .Include входит в ваш метод GetUsers.

https://msdn.microsoft.com/en-us/library/jj574232(v=против 113). aspx

[no name]

В моем методе GetUsers я сделал то же самое .Включить для ролей пользователей, как это,

var hsuserlist = userlist.Join(db.EliteHousingServicesUsers.Include(r => r.User.Roles.Select(p => p.RoleId)), u => u.Id, hu => hu.UserId, (u, hu) => new

и попытался вызвать его в методе GetUserData следующим образом
Roles = string.Join(",", item.Roles).ToString()


Он быстро загружает таблицу, но все еще не показывает роли? А в столбце роли он показывает что-то вроде System.Data.Entity.Полномочия.IdentityUserRole _ и куча букв и цифр. Я не знаю, что происходит?

Dave Kreskowiak

Вы получаете прокси-объекты, потому что он не загрузил роли.

Include должен содержать только ассоциацию, а не конкретный элемент из таблицы.

Если у вас есть таблица пользователей, в которой каждый пользователь имеет набор ролей, членами которых он является, вы можете вернуть всю таблицу пользователей со всеми их ролями в одном запросе:

    var users = myContext.Users.Include(x => x.Roles);

Для одного пользователя:
    var user = myContext.Users.Include(x => x.Roles).Where(x => x.UserId == idToFind).SingleOrDefault();

[no name]

Я пытаюсь создать метод GetRolesByUserId, чтобы получить список имен ролей для каждого пользователя (RoleList), и поэтому мне не нужно использовать метод RoleRepository.

Вы можете помочь мне с частью запроса? Пожалуйста, скромная просьба.

У меня есть 2 таблицы, таблица IdentityUserRole, которая имеет UserId и RoleId,
а затем таблица IdentityRoles, которая имеет RoleId и RoleName.

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

public static IEnumerable<string> GetRolesByUserId(string userId)
        {
            ApplicationDbContext db = new ApplicationDbContext();

            return db.Set<IdentityUserRole>().Where(r => r.UserId.Contains(userId)).Select(x => x.RoleId);
        }


Я просто хочу, чтобы список ролей был заполнен.

Dave Kreskowiak

Есть проблема с тем, что вы делаете только на этих двух строках кода.

Вы возвращаете отложенный запрос, а не список элементов. Запрос еще не запущен, пока вызывающий не начнет манипулировать объектами, которые этот запрос должен вернуть. Проблема в том, что ваш контекст выпадает из области видимости и будет удален, оставив сиротой ваш возвращенный запрос. Когда вы перейдете к использованию результата запроса, он потерпит неудачу, потому что контекст, из которого он пришел, больше не существует.

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

Обычно я использую класс "сервисы", некоторые люди называют его" репозиторием", который имеет дело с определенной таблицей базы данных или набором таблиц, определенных бизнес-правилами. Этот класс обслуживания является одноразовым и поддерживает экземпляр класса DbContext, с которым он имеет дело. Все методы получения/вставки/обновления / удаления объектов находятся в этом классе и используют один и тот же объект DbContext, предоставленный классом. В методе класса Dispose экземпляр DbContext удаляется, очищая ресурсы.

Прочтите это -> https://docs.microsoft.com/en-us/aspnet/mvc/overview/older-versions/getting-started-with-ef-5-using-mvc-4/implementing-the-repository-and-unit-of-work-patterns-in-an-asp-net-mvc-application

Он немного устарел, но все еще демонстрирует концепцию.