首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >编制按字母分组的字母列表

编制按字母分组的字母列表
EN

Code Review用户
提问于 2020-09-15 19:12:30
回答 1查看 104关注 0票数 6

我有一个宠物项目(GitHub上的开源),我建立了一个学习练习。这是一个歌词web应用程序构建在.NET Core中。

在网站的艺术家页面上(查看移动大小的视图,目前只对手机进行了优化),我按字母顺序列出了所有的艺术家。这些艺术家是按字母分组的。

生成该页的代码如下:

控制器

代码语言:javascript
复制
[Route("artists")]
public async Task<IActionResult> Index()
{
  IDictionary<char, List<LibraryArtistViewModel>> viewModel = await _artistsService
    .GetAllArtistsAsync();

  return View(viewModel);
}

服务

代码语言:javascript
复制
public async Task<IDictionary<char, List<LibraryArtistViewModel>>> GetAllArtistsAsync()
{
  List<LibraryArtistViewModel> artists = new List<LibraryArtistViewModel>();

  await using NpgsqlConnection connection = new NpgsqlConnection(databaseOptions.ConnectionString);
  await connection.OpenAsync();

  await using NpgsqlCommand command = new NpgsqlCommand("select a.first_name, a.last_name, \"as\".name as primary_slug, ai.data as image_data, count(l.title) as number_of_lyrics from artists a left join artist_images ai on ai.artist_id = a.id inner join artist_slugs \"as\" on \"as\".artist_id = a.id left join lyrics l on l.artist_id = a.id where a.is_approved = true and a.is_deleted = false and \"as\".is_primary = true and l.is_approved = true and l.is_deleted = false group by a.id, \"as\".name, ai.data order by a.first_name asc;", connection);

  await using NpgsqlDataReader reader = await command.ExecuteReaderAsync();

  while (await reader.ReadAsync())
  {
    LibraryArtistViewModel artist = new LibraryArtistViewModel();
    string firstName = Convert.ToString(reader[0]);
    string lastName = Convert.ToString(reader[1]);
    string fullName = textInfo.ToTitleCase($"{firstName} {lastName}");
    string primarySlug = Convert.ToString(reader[2]);
    bool hasImage = reader[3] != System.DBNull.Value;
    int numberOfLyrics = Convert.ToInt32(reader[4]);

    artist.FirstName = firstName;
    artist.LastName = lastName;
    artist.FullName = fullName;
    artist.PrimarySlug = primarySlug;
    artist.HasImage = hasImage;
    artist.NumberOfLyrics = numberOfLyrics;

    artists.Add(artist);
  }

  IDictionary<char, List<LibraryArtistViewModel>> dictionary = BuildDictionary(artists);

  return dictionary;
}

private IDictionary<char, List<LibraryArtistViewModel>> BuildDictionary(List<LibraryArtistViewModel> artists)
{
  List<char> letters = new List<char>();

  IDictionary<char, List<LibraryArtistViewModel>> dictionary =
    new Dictionary<char, List<LibraryArtistViewModel>>();

  foreach (LibraryArtistViewModel artist in artists)
  {
    char firstLetter = char.ToUpper(artist.FirstName[0]);

    if (!letters.Contains(firstLetter))
    {
      letters.Add(firstLetter);

      dictionary.Add(firstLetter, new List<LibraryArtistViewModel>());
    }
  }

  foreach (char letter in letters)
  {
    foreach (LibraryArtistViewModel artist in artists)
    {
      char firstLetter = char.ToUpper(artist.FirstName[0]);

      if (letter == firstLetter)
      {
        List<LibraryArtistViewModel> artistsBeginningWithTheLetter = dictionary[letter];
        artistsBeginningWithTheLetter.Add(artist);
      }
    }
  }

  return dictionary;
}

上面的代码工作,但我觉得它是没有效率的。我觉得有更好的方法。

而且,我对我提供图像的方式也很不确定。我的图像目前以字节形式存储在数据库中,并且我有一个控制器为图像提供服务。这似乎真的在扼杀我在https://web.dev上的性能指标。

我做这个项目是为了学习东西。所以我真的很乐意学习如何“正确”地做事情,即使他们被认为是过分的。

我很感激上面的一些建议,以及我如何能够改善艺术家形象的情况。

EN

回答 1

Code Review用户

发布于 2020-09-15 22:38:40

嗯,你有一个很好的learning exercise项目。下面是您提供的最优秀代码的一些注释:

  • 您使用的是string查询,它打开SQL Injections。要克服这一问题,首先需要将查询标记为constreadonly,并以分号结束查询(您确实这样做了),并确保查询本身没有任何可能以不适当方式使用的SQL错误。但是,如果您想要正确地管理数据库,可以使用Object-relational Mapping AKA ORM (如Entity Framework )。
  • 将图像存储在心理磁盘中,并将其路径(或文件名)存储到数据库中。将文件字节存储到数据库中会影响系统性能,因为每次选择图像时都需要读取和呈现图像。如果您以物理方式存储文件,则呈现速度会更快。因此,在项目文件夹中创建一个文件夹,将图像存储在该文件夹中(在您想要的任何结构中),然后在数据库中存储每个图像的路径。然后,在您的应用程序中,您只需获得路径,调整路径,使用户可以查看。例如,如果存储路径images/artists/someartist.png,那么您只需像https://www.somewebsite.com/images/artists/someartist.png一样向其添加当前的web url。
  • 请始终将PNG图像用于web,因为png图像在质量和大小之间更加平衡,并且是比其他类型的web更好的选择。

在您的代码中,不应该在表示层上执行排序操作,实际上,如果将排序操作移到早期应用程序阶段(如在您的示例中对艺术家进行排序),则您的应用程序具有按字母顺序过滤艺术家姓名的核心功能。这意味着,在将数据插入数据库之前,需要将此功能应用于数据,因为它被用作核心功能,而不是附加功能。

但是,有不同的方法可以这样做,一种方法是添加一个属性存储的名字字母如下:

代码语言:javascript
复制
public class LibraryArtistViewModel
{
    public char FirstLetter => FirstName?.Length > 0 ? FirstName.ToUpper()[0] : char.MinValue;

    public string FirstName { get; set; }

    public string LastName { get; set; }

    public string FullName { get; set; }

    public string PrimarySlug { get; set; }

    public bool HasImage { get; set; }

    public int NumberOfLyrics { get; set; }
        
}

然后,您可以在GetAllArtistsAsync()中这样做:

代码语言:javascript
复制
  while (await reader.ReadAsync())

      {
        var artist = new LibraryArtistViewModel
        {
            FirstName = reader[0]?.ToString(),
            LastName = reader[1]?.ToString(),
            FullName = textInfo.ToTitleCase($"{reader[0]?.ToString()} {reader[1]?.ToString()}"),
            PrimarySlug = reader[2]?.ToString(),
            HasImage = reader[3] != System.DBNull.Value;
            NumberOfLyrics = int.TryParse(reader[4]?.ToString(), out int number) ? number : 0;  
        };
        
        artists.Add(artist);
      }
    
    var dictionary = artists.GroupBy(x=> x.FirstLetter).ToDictionary(x => x.Key, x => x.ToList()).OrderBy(x=> x.Key);

虽然这会给你你想要的,但它会带来一些微小的表现。正如我前面所说的,由于您的艺术家应该被排序,所以您需要从插入数据的时间开始对其进行排序,要么从数据库中排序,要么从更快的数据库中排序,或者从CreateNewArtistAsync操作中排序。因此,在您的Get中,您不需要对每个请求重新排序。

票数 2
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/249408

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档