Как я могу минимизировать обращения к базе данных в этом методе

#c# #linq

#c# #linq

Вопрос:

Внутри сервиса у меня есть метод, который собирает данные из разных таблиц и объединяет их в один Dto, accountDto .

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

Первые два метода GetAccount и GetPerson просто получение данных из отдельных таблиц с помощью методов репозитория. Две таблицы связаны общим TrinId . Последний метод GetRoleDtos намного сложнее и включает в себя несколько объединений, как показано ниже. Этот последний бит данных связан AccountId .

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

     public AccountDto GetAccountDto(int accountId)
    {
        try
        {
            Account account = _unitOfWork.AccountRepository.GetAccount(accountId);

            AccountDto accountDto = account == null ? new AccountDto() : new AccountDto
            {
                AccountId = account.AccountId,
                UserName = account.UserName,
                TrinId = account.TrinId,
                LastName = account.LastName,
                FirstName = account.FirstName
            };

            Person person = _unitOfWork.AccountRepository.GetPerson(accountDto.TrinId);

        accountDto.Person = person == null ? new PersonDto() : new PersonDto
        {
            Id = person.Id,
            TrinId = person.TrinId,
            AccountId = person.AccountId,
            LastName = person.LastName,
            FirstName = person.FirstName,
            MiddleName = person.MiddleName,
            Gender = person.Gender,
            BirthDate = person.BirthDate
        };

        accountDto.Roles = _unitOfWork.AccountRepository.GetRoleDtos(accountId).ToList();

        return accountDto;
    }
    catch (Exception ex)
    {
        Log.Error(ex);
        throw;
    }
}
 

 public IQueryable<RoleDto> GetRoleDtos(int accountId)
    {
        var userModuleRoles = from account in _model.Accounts
                              join userRight in _model.UserRights on account.AccountId equals userRight.AccountId
                              join role in _model.Roles on userRight.RoleId equals role.Id
                              join organization in _model.Organizations on userRight.OrganizationId equals organization.Id
                              join module in _model.Modules on role.ModuleId equals module.Id
                              where (account.IsApproved == true amp;amp; userRight.Status == 1 amp;amp; account.AccountId == accountId)
                              select new RoleDto
                              {
                                  Id = role.Id,
                                  RoleName = role.Name,
                                  AccountId = account.AccountId,
                                  OrganizationId = organization.Id,
                                  OrganizationName = organization.Name,
                                  ModuleId = module.Id,
                                  ModuleName = module.Name
                              };

        return userModuleRoles;
    }
 

Здесь была моя первоначальная попытка выполнить запрос «все в одном». Это сработало, но у меня возникли проблемы с получением List<RoleDto> . Как я могу также получить список ролей Dto?

Запрос, который я использую сейчас, показан в GetRoleDtos методе, но его необходимо включить в этот запрос. У меня было одно частично работающее решение, но сейчас оно удалено. Я отказался от этого, потому что, когда не было записей RoleDto, весь запрос возвращал null. В этом случае мне все равно понадобится заполненный AccountDto с пустой коллекцией RoleDto . Я считаю, что мне нужно использовать DefaultIfEmpty для получения и очистки коллекции, но именно в этот момент я сдался, потому что запрос становился громоздким. 🙁

         return account;
    }

    public AccountDto GetAccountDto(int accountId)
    {
        return (from a in _model.Accounts
                       join p in _model.People on a.TrinId equals p.TrinId into perJoin
                       from per in perJoin.DefaultIfEmpty()
                       where a.AccountId == accountId
                       select new AccountDto
                       {
                           AccountId = a.AccountId,
                           UserName = a.UserName,
                           TrinId = a.TrinId,
                           LastName = a.LastName,
                           FirstName = a.FirstName,
                           Person = new PersonDto
                                {
                                    Id = per.Id,
                                    LastName = per.LastName,
                                    FirstName = per.FirstName,
                                    MiddleName = per.MiddleName,
                                    Gender = per.Gender,
                                    BirthDate = per.BirthDate
                                }
                       }).FirstOrDefault();
    }
 

Ответ №1:

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

 public class AccountInfo {
    public int AccountId { get; set; }
    public string UserName { get; set; }
    public int TrinId { get; set; }
    public string LastName { get; set; }
    public string FirstName { get; set; }
    public Person PersonInfo { get; set; }
    public List<Roles> AccountRoles { get; set; }
}
 

Затем создайте в своем репозитории единый метод, который заполняет все необходимые свойства, поскольку вы можете четко получить Account и на List<Roles> основе accountId , а затем присоединиться к вашей персональной информации на основе этого TrinId . Я оставлю запрос на ваше усмотрение.

Комментарии:

1. Твердый ответ, я нахожу, что многие люди чувствуют при использовании сервиса / репозитория, что каждый репозиторий должен возвращать только один объект, для которого создан репозиторий. В то время как на самом деле было бы очень неуправляемо и неэффективно создавать систему, которая строго придерживается этого.

2. Да, я изначально начал пробовать что-то подобное, но запрос очень быстро усложнился…

3. @navig8tr это нормально для имен. Я предполагаю, что в вашем Person классе будут имена и фамилии. Просто заполните их также из вашего запроса.

4. Я включил свою первую попытку всеохватывающего запроса. Я назвал его классом AccountDto , а не AccountInfo но идея та же.

5. @navig8tr Я думаю, что вы сталкиваетесь с вопросом-scope-creep. На ваш первоначальный вопрос How can I minimize the number of hits and improve speed, while following this same Service/Repository pattern? был дан ответ. Сам запрос звучит как другой.