Att lägga till enhetens ram för bloggposter (Pt. 4) (Svenska (Swedish))

Att lägga till enhetens ram för bloggposter (Pt. 4)

Saturday, 17 August 2024

//

6 minute read

Se delar 1 och 2 och 3 för de tidigare stegen

Inleiding

I tidigare delar har vi omfattat hur man sätter upp databasen, ,, hur våra kontroller och synsätt är strukturerade,,, och hur våra tjänster fungerar.., i den här delen omfattar vi details om hur man planterar databasen med några inledande data och hur EF-baserade tjänster fungerar,

Som vanligt kan ni se alla källor till detta på min GitHub här, i den mest uppenbara foldern

Att plantera databasen

I den tidigare delen visade vi hur att initialisera och sätta upp tjänster. I den här delen ska vi bevaka hur man planterar databasen med några initiala data. EFBlogPopulator klass. Den här klassen är registrerad som en tjänst i SetupBlog förlängning metod.

    public async Task Populate()
    {
        var posts = await _markdownBlogService.GetPages();
        var languages = _markdownBlogService.LanguageList();

        var languageEntities = await EnsureLanguages(languages);
        await EnsureCategoriesAndPosts(posts, languageEntities);

        await Context.SaveChangesAsync();
    }

Ni kan se det i Populate metod vi använder _markdownBlogService.GetPages() den kör igenom våra makrdown-filer och befolkar en massa BlogViewModels som innefattar alla posten. Vi gör samma sak för språken; det här ser på vår translated folder för alla översatta markerdown-filer som vi skapade med EasyNMT (se här för hur vi gör den delen

Att lägga till språk

Sedan ropar vi in i vår EnsureLanguages metod som säkerställer att alla språk finns i databasen. Det här är en enkel metod som kontrollerar om språket existerar och om inte lägger det till databasen

  private async Task<List<LanguageEntity>> EnsureLanguages(Dictionary<string, List<string>> languages)
    {
        var languageList = languages.SelectMany(x => x.Value).ToList();
        var currentLanguages = await Context.Languages.Select(x => x.Name).ToListAsync();

        var languageEntities = new List<LanguageEntity>();
        var enLang = new LanguageEntity { Name =MarkdownBaseService.EnglishLanguage };

        if (!currentLanguages.Contains(MarkdownBaseService.EnglishLanguage)) Context.Languages.Add(enLang);
        languageEntities.Add(enLang);

        foreach (var language in languageList)
        {
            if (languageEntities.Any(x => x.Name == language)) continue;

            var langItem = new LanguageEntity { Name = language };

            if (!currentLanguages.Contains(language)) Context.Languages.Add(langItem);

            languageEntities.Add(langItem);
        }

        await Context.SaveChangesAsync(); // Save the languages first so we can reference them in the blog posts
        return languageEntities;
    }

Ni kommer att se att detta är ppretty enkelt och bara försäkrar oss om att alla språk vi fick från markerdown-posterna finns i databasen, och eftersom vi specifikerade att IDs skapades automatiskt behöver vi SaveChanges för att säkerställa att IDs genereras.

Att lägga till kategorierna och meddelanden

Sedan ropar vi in i vår EnsureCategoriesAndPosts metod som säkerställer att alla kategorier och poster finns i databasen.

    private async Task EnsureCategoriesAndPosts(
        IEnumerable<BlogPostViewModel> posts,
        List<LanguageEntity> languageEntities)
    {
        var languages = languageEntities.ToDictionary(x => x.Name, x => x);
        var currentPosts = await PostsQuery().ToListAsync();
        foreach (var post in posts)
        {
            var existingCategories = Context.Categories.Local.ToList();
            var currentPost =
                currentPosts.FirstOrDefault(x => x.Slug == post.Slug && x.LanguageEntity.Name == post.Language);
            await AddCategoriesToContext(post.Categories, existingCategories);
            existingCategories = Context.Categories.Local.ToList();
            await AddBlogPostToContext(post, languages[post.Language], existingCategories, currentPost);
        }
    }

Här använder vi kontexten.Kategorier.Local för att spåra kategorierna som just nu har lagts till kontexten. M SK2 de ' är sparade i databasen under SaveAsync ringa). Ni kan se att vi PostsQuery metoden i vår basklass som är en enkel metod som återvänder en frågabar BlogPostEntity så att vi kan bläddra i databasen för posterna.

  protected IQueryable<BlogPostEntity> PostsQuery()=>Context.BlogPosts.Include(x => x.Categories)
        .Include(x => x.LanguageEntity);
   

Att lägga till kategorierna

Sedan ropar vi in AddCategoriesToContext metod som säkerställer att alla kategorier finns i databasen. Det här är en enkel metod som kontrollerar om kategorin existerar och om den inte lägger till databasen

    private async Task AddCategoriesToContext(
        IEnumerable<string> categoryList,
        List<CategoryEntity> existingCategories)
    {
        foreach (var category in categoryList)
        {
            if (existingCategories.Any(x => x.Name == category)) continue;

            var cat = new CategoryEntity { Name = category };

             await Context.Categories.AddAsync(cat);
        }
    }

Återigen kontrollerar detta om kategorin existerar och om inte lägger den till databasen.

Att lägga till bloggarna

Sedan ropar vi in AddBlogPostToContext metod, detta ringer in i EFBaseService att spara meddelandet i databasen.

    private async Task AddBlogPostToContext(
        BlogPostViewModel post,
        LanguageEntity postLanguageEntity,
        List<CategoryEntity> categories,
        BlogPostEntity? currentPost)
    {
        await SavePost(post, currentPost, categories, new List<LanguageEntity> { postLanguageEntity });
    }

Vi gör detta genom att kalla SavePost metod som är en metod som sparar posten till databasen. Den här metoden är lite komplex eftersom den måste kontrollera om posten har ändrats och om det gör att posten blir uppdaterad i databasen


   public async Task<BlogPostEntity?> SavePost(BlogPostViewModel post, BlogPostEntity? currentPost =null ,
        List<CategoryEntity>? categories = null,
        List<LanguageEntity>? languages = null)
    {
        if (languages == null)
            languages = await Context.Languages.ToListAsync();

    var postLanguageEntity = languages.FirstOrDefault(x => x.Name == post.Language);
        if (postLanguageEntity == null)
        {
            Logger.LogError("Language {Language} not found", post.Language);
            return null;
        }
        categories ??= await Context.Categories.Where(x => post.Categories.Contains(x.Name)).ToListAsync();
         currentPost ??= await PostsQuery().Where(x=>x.Slug == post.Slug).FirstOrDefaultAsync();
        try
        {
            var hash = post.HtmlContent.ContentHash();
            var currentCategoryNames = currentPost?.Categories.Select(x => x.Name).ToArray() ?? Array.Empty<string>();
            var categoriesChanged = false;
            if (!currentCategoryNames.All(post.Categories.Contains) ||
                !post.Categories.All(currentCategoryNames.Contains))
            {
                categoriesChanged = true;
                Logger.LogInformation("Categories have changed for post {Post}", post.Slug);
            }

            var dateChanged = currentPost?.PublishedDate.UtcDateTime.Date != post.PublishedDate.ToUniversalTime().Date;
            var titleChanged = currentPost?.Title != post.Title;
            if (!titleChanged && !dateChanged && hash == currentPost?.ContentHash && !categoriesChanged)
            {
                Logger.LogInformation("Post {Post} has not changed", post.Slug);
                return currentPost;
            }

            
            var blogPost = currentPost ?? new BlogPostEntity();
            
            blogPost.Title = post.Title;
            blogPost.Slug = post.Slug;
            blogPost.OriginalMarkdown = post.OriginalMarkdown;
            blogPost.HtmlContent = post.HtmlContent;
            blogPost.PlainTextContent = post.PlainTextContent;
            blogPost.ContentHash = hash;
            blogPost.PublishedDate = post.PublishedDate;
            blogPost.LanguageEntity = postLanguageEntity;
            blogPost.Categories = categories.Where(x => post.Categories.Contains(x.Name)).ToList();

            if (currentPost != null)
            {
                Logger.LogInformation("Updating post {Post}", post.Slug);
                Context.BlogPosts.Update(blogPost); // Update the existing post
            }
            else
            {
                Logger.LogInformation("Adding new post {Post}", post.Slug);
                Context.BlogPosts.Add(blogPost); // Add a new post
            }
            return blogPost;
        }
        catch (Exception e)
        {
            Logger.LogError(e, "Error adding post {Post}", post.Slug);
        }

        return null;
    }

Som ni kan se har detta ett LITT av förändringsdetektion för att se till att vi inte- -ser- -tillsätta- -poster som inte har ändrats- -.. Vi kontrollerar innehållets hash- -kategorierna, datumet och titeln. .. Om någon av dessa har ändrat- -visar vi upp postitusen i databasen.

En sak att lägga märke till är hur irriterande det är att kolla en DateTimeOffset är; vi måste konvertera den till UTC och sedan få datumet för att jämföra den. Det beror på DateTimeOffset har en tidskomponent och vi vill jämföra bara datumet

var dateChanged = currentPost?.PublishedDate.UtcDateTime.Date != post.PublishedDate.ToUniversalTime().Date;

I slutsats

Nu har vi ett fullt fungerande bloggsystem som kan fyllas med markerdown- och översatta markerdowndater.

Finding related posts...
logo

© 2026 Scott Galloway — Unlicense — All content and source code on this site is free to use, copy, modify, and sell.