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

<!--category-- ASP.NET, Entity Framework -->
<datetime class="hidden">2024-08-17T20:00</datetime>

Se delar [1](/blog/addingentityframeworkforblogpostspt1) och [2](/blog/addingentityframeworkforblogpostspt2) och [3](/blog/addingentityframeworkforblogpostspt3) 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](https://github.com/scottgal/mostlylucidweb/tree/main/Mostlylucid/Blog), i den mest uppenbara foldern

[TOC]

# Att plantera databasen

I den tidigare delen visade vi hur [att initialisera och sätta upp tjänster](/blog/addingentityframeworkforblogpostspt2#setup). 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.

```csharp
    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](/blog/autotranslatingmarkdownfiles) 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

```csharp
  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.

```csharp
    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.

```csharp
  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

```csharp
    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.

```csharp
    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

```csharp

   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

```csharp
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.