NOTE: Apart from
(and even then it's questionable, I'm Scottish). These are machine translated in languages I don't read. If they're terrible please contact me.
You can see how this translation was done in this article.
Saturday, 17 August 2024
//Less than a minute
Δείτε μέρη 1 και 2 και 3 για τα προηγούμενα βήματα.
Σε προηγούμενα μέρη καλύψαμε πώς να δημιουργήσουμε τη βάση δεδομένων, πώς δομούνται οι ελεγκτές και οι απόψεις μας, και πώς λειτουργούσαν οι υπηρεσίες μας. Σε αυτό το μέρος θα καλύψουμε λεπτομέρειες για το πώς να σπείρουμε τη βάση δεδομένων με κάποια αρχικά δεδομένα και πώς λειτουργούν οι υπηρεσίες EF Based.
Ως συνήθως μπορείς να δεις όλη την πηγή για αυτό στο GitHub μου Ορίστε., στον πιο διαυγή/blog φάκελο.
Στο προηγούμενο μέρος καλύψαμε τον τρόπο με τον οποίο αρχικοποίηση και δημιουργία των υπηρεσιών. Σε αυτό το μέρος θα καλύψουμε το πώς να σπείρουμε τη βάση δεδομένων με κάποια αρχικά δεδομένα. Αυτό γίνεται στο EFBlogPopulator
Μαθήματα. Η τάξη αυτή είναι καταχωρημένη ως υπηρεσία στο SetupBlog
μέθοδος επέκτασης.
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();
}
Μπορείς να το δεις αυτό στο... Populate
Η μέθοδος που καλούμε για την _markdownBlogService.GetPages()
Αυτό τρέχει μέσω των αρχείων μας makrdown και κατοικεί ένα μάτσο από BlogViewModels
που περιέχει όλες τις θέσεις.
Στη συνέχεια, κάνουμε το ίδιο για τις γλώσσες? translated
φάκελο για όλα τα μεταφρασμένα αρχεία markdown που δημιουργήσαμε χρησιμοποιώντας EasyNMT (δείτε Ορίστε. για το πώς θα το κάνουμε αυτό το μέρος).
Μετά θα καλέσουμε τον δικό μας. EnsureLanguages
μέθοδος που εξασφαλίζει ότι όλες οι γλώσσες βρίσκονται στη βάση δεδομένων. Αυτή είναι μια απλή μέθοδος που ελέγχει αν υπάρχει η γλώσσα και αν δεν την προσθέτει στη βάση δεδομένων.
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;
}
Θα δείτε ότι αυτό είναι απλό και απλά εξασφαλίζει ότι όλες οι γλώσσες που πήραμε από τις θέσεις markdown είναι στη βάση δεδομένων? SaveChanges
για την εξασφάλιση της δημιουργίας των ταυτοτήτων.
Μετά θα καλέσουμε τον δικό μας. EnsureCategoriesAndPosts
μέθοδος που εξασφαλίζει ότι όλες οι κατηγορίες και θέσεις βρίσκονται στη βάση δεδομένων. Αυτό είναι λίγο πιο περίπλοκο καθώς πρέπει να διασφαλίσουμε ότι οι κατηγορίες βρίσκονται στη βάση δεδομένων και στη συνέχεια πρέπει να διασφαλίσουμε ότι οι θέσεις βρίσκονται στη βάση δεδομένων.
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);
}
}
Εδώ χρησιμοποιούμε το Πλαίσιο.Categories.Τοπικά για να παρακολουθείτε τις κατηγορίες που έχουν προστεθεί στο Πλαίσιο (είναι αποθηκευμένες στη βάση δεδομένων κατά τη διάρκεια της SaveAsync
Τηλεφώνημα).
Μπορείτε να δείτε ότι καλούμε στο PostsQuery
μέθοδος μας βασική τάξη η οποία είναι μια απλή μέθοδος που επιστρέφει ένα ερωτηματικό της BlogPostEntity
Για να ρωτήσουμε τη βάση δεδομένων για τις θέσεις.
protected IQueryable<BlogPostEntity> PostsQuery()=>Context.BlogPosts.Include(x => x.Categories)
.Include(x => x.LanguageEntity);
Στη συνέχεια, καλούμε στο AddCategoriesToContext
μέθοδος που εξασφαλίζει ότι όλες οι κατηγορίες βρίσκονται στη βάση δεδομένων. Αυτή είναι μια απλή μέθοδος που ελέγχει εάν υπάρχει η κατηγορία και αν δεν την προσθέτει στη βάση δεδομένων.
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);
}
}
Και πάλι αυτό ελέγχει αν υπάρχει η κατηγορία και αν δεν την προσθέτει στη βάση δεδομένων.
Στη συνέχεια, καλούμε στο AddBlogPostToContext
Με τη μέθοδο αυτή, η EFBaseService
για να αποθηκεύσετε τη θέση στη βάση δεδομένων.
private async Task AddBlogPostToContext(
BlogPostViewModel post,
LanguageEntity postLanguageEntity,
List<CategoryEntity> categories,
BlogPostEntity? currentPost)
{
await SavePost(post, currentPost, categories, new List<LanguageEntity> { postLanguageEntity });
}
Το κάνουμε αυτό καλώντας το SavePost
μέθοδος η οποία είναι μια μέθοδος που αποθηκεύει τη θέση στη βάση δεδομένων. Αυτή η μέθοδος είναι λίγο περίπλοκη καθώς πρέπει να ελέγξει αν η θέση έχει αλλάξει και αν να ενημερώσει τη θέση στη βάση δεδομένων.
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;
}
Όπως μπορείτε να δείτε αυτό έχει μια πολλή ανίχνευση αλλαγών για να εξασφαλίσει ότι δεν ξαναπροσθέτουμε θέσεις που δεν έχουν αλλάξει. Ελέγχουμε το χασίς του περιεχομένου, τις κατηγορίες, την ημερομηνία και τον τίτλο. Αν κάποιο από αυτά έχουν αλλάξει, ενημερώνουμε τη θέση στη βάση δεδομένων.
Ένα πράγμα που πρέπει να παρατηρήσετε είναι πόσο ενοχλητικός έλεγχος ενός DateTimeOffset είναι; πρέπει να το μετατρέψουμε σε UTC και στη συνέχεια να πάρει την ημερομηνία για να το συγκρίνουν. Αυτό συμβαίνει επειδή η DateTimeOffset
έχει ένα στοιχείο του χρόνου και θέλουμε να συγκρίνουμε ακριβώς την ημερομηνία.
var dateChanged = currentPost?.PublishedDate.UtcDateTime.Date != post.PublishedDate.ToUniversalTime().Date;
Τώρα έχουμε ένα πλήρως λειτουργικό σύστημα blog που μπορεί να κατοικηθεί από τα αρχεία markdown και να μεταφραστεί αρχεία markdown. Στο επόμενο μέρος θα καλύψουμε την απλή υπηρεσία που χρησιμοποιούμε για την εμφάνιση θέσεων αποθηκευμένων στη βάση δεδομένων.