Back to "Het 'Entity Framework' toevoegen voor Blog Posts (Pt. 4)"

This is a viewer only at the moment see the article on how this works.

To update the preview hit Ctrl-Alt-R (or ⌘-Alt-R on Mac) or Enter to refresh. The Save icon lets you save the markdown file to disk

This is a preview from the server running through my markdig pipeline

ASP.NET Entity Framework

Het 'Entity Framework' toevoegen voor Blog Posts (Pt. 4)

Saturday, 17 August 2024

Zie delen 1 en 2 en 3 voor de vorige stappen.

Inleiding

In eerdere delen hebben we behandeld hoe de database op te zetten, hoe onze controllers en visies zijn gestructureerd, en hoe onze diensten werktenM SK2 In deze deel zullen we de details over hoe je de database met een aantal begingegevens inzet en hoe de EF-gebaseerde diensten werken .

Zoals gewoonlijk zie je hier alle bronnen voor op mijn GitHub. Hier., in de meest duidelijke folder/BloggidsM SK2

Zedding van de database

In het vorige deel hebben we behandeld hoe we Initialiseer en opstelling van de dienste.. In dit deel zullen we ' behandelen hoe je de database inzet met een aantal begingegevens. EFBlogPopulator class. Deze klas is geregistreerd als een dienst in de SetupBlog Verlengingsmethode.

    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();
    }

Je ziet dat in de Populate De methode die we in de _markdownBlogService.GetPages() Dit loopt door onze makrdown-bestanden en bevolkt een hoop van BlogViewModels containing all the posts. Dan doen we hetzelfde voor de talen; dit kijkt naar onze translated gids voor alle vertaalde markerdown-bestanden die we genereerden met EasyNMT (sien Hier. voor hoe we dat deel doen.

Voeg de talen toe

Dan roepen we in onze EnsureLanguages methode die ervoor zorgt dat alle talen in de database zijn.. Dit is een eenvoudige methode om te controleren of de taal bestaat en of ze niet aan de database wordt toegevoegd.

  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;
    }

Je zal zien dat dit ppretty simpel is en ervoor zorgt dat alle talen die we van de markdown-posts kregen in de database zitten. SaveChanges om te zorgen dat de Ids worden gegenereerd.

Categories en posten toevoegen

Dan roepen we in onze EnsureCategoriesAndPosts methode die ervoor zorgt dat alle categorieën en posities in de database zijn.

    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);
        }
    }

Hier gebruiken we de Context.Categorieën.Local om de categorieën te volgen die momenteel bijgevoegd zijn in het Context (zeiM SK3in de Databasis gestopt zijn tijdens de SaveAsync Call). Je kunt zien dat we in de PostsQuery Het is een eenvoudige methode die een vraagbaar antwoord terugbrengt op de basisklas. BlogPostEntity Dus kunnen we de database vragen voor de posts.

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

De categorieën toevoegen.

Dan roepen we in de AddCategoriesToContext methode die ervoor zorgt dat alle categorieën in de database zitten. Dit is een eenvoudige methode om te controleren of de categorie bestaat en of ze niet aan de database wordt toegevoegd.

    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);
        }
    }

Nogmaals, dit controleert of de categorie bestaat en als niet voegt ze toe in de database.

Het toevoegen van de blogposts

Dan roepen we in de AddBlogPostToContext methode, dit roept dan in de EFBaseService om de post in de database te slaan.

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

We doen dit door de SavePost methode die de post in de database bewaart. Deze methode is een beetje complex omdat ze moet controleren of de post veranderd is en als dat zo is, de post opdateert in de databasis.


   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;
    }

Zoals je ziet heeft dit een LITTLE veranderingen waarnemen om ervoor te zorgen dat we geen posts die niet veranderd zijn hervoegen.

Eén ding dat je moet opmerken is hoe vervelend het is om een DateTimeOffset te controleren:; we moeten het omzetten naar UTC en dan de datum krijgen om het te vergelijken. DateTimeOffset Het heeft een tijdscomponent en we willen alleen de datum vergelijken.

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

In slotting:

Nu hebben we een volledig werkend blogsysteem dat kan worden bevolkt van markdown-bestanden en vertaalde markdownbestanden.

logo

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