From f7bb45407ce4095d669d3172b8eeee10a104c8eb Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Mon, 15 Dec 2025 08:37:02 +0000 Subject: [PATCH] Phase 2: Data Layer Migration from Entity Framework 6.1.3 to Entity Framework Core - Update DataLayer.Core to target net8.0 with EF Core 8.0 packages - Migrate entity classes (Blog, Post, Tag) with validation logic preserved - Create SampleWebAppDbCore DbContext with change tracking and model configuration - Add TrackUpdate helper class for LastUpdated tracking - Create DataLayerCoreInitialise for database initialization - Add LoadDbDataFromXml helper for seeding data from XML files - Copy XML data files (BlogsContentSimple.xml, BlogsContextMedium.xml) - Update ServiceLayer.Core to target net8.0 for compatibility - Add comprehensive unit tests for DataLayer.Core (15 tests) - Configure Tag Slug uniqueness constraint in OnModelCreating - Preserve all entity relationships (Blog-Post, Post-Tag many-to-many) Co-Authored-By: Abhay Aggarwal --- DataLayer.Core/Class1.cs | 9 - DataLayer.Core/DataClasses/Concrete/Blog.cs | 54 +++ .../Concrete/Helpers/TrackUpdate.cs | 45 +++ DataLayer.Core/DataClasses/Concrete/Post.cs | 76 ++++ DataLayer.Core/DataClasses/Concrete/Tag.cs | 52 +++ .../DataClasses/SampleWebAppDbCore.cs | 125 ++++++ DataLayer.Core/DataLayer.Core.csproj | 21 +- .../Startup/DataLayerCoreInitialise.cs | 105 +++++ .../Startup/Internal/BlogsContentSimple.xml | 49 +++ .../Startup/Internal/BlogsContextMedium.xml | 377 ++++++++++++++++++ .../Startup/Internal/LoadDbDataFromXml.cs | 98 +++++ ServiceLayer.Core/ServiceLayer.Core.csproj | 12 +- Tests.Core/DataLayerCoreTests.cs | 294 ++++++++++++++ Tests.Core/Tests.Core.csproj | 1 + Tests.Core/UnitTest1.cs | 10 - 15 files changed, 1302 insertions(+), 26 deletions(-) delete mode 100644 DataLayer.Core/Class1.cs create mode 100644 DataLayer.Core/DataClasses/Concrete/Blog.cs create mode 100644 DataLayer.Core/DataClasses/Concrete/Helpers/TrackUpdate.cs create mode 100644 DataLayer.Core/DataClasses/Concrete/Post.cs create mode 100644 DataLayer.Core/DataClasses/Concrete/Tag.cs create mode 100644 DataLayer.Core/DataClasses/SampleWebAppDbCore.cs create mode 100644 DataLayer.Core/Startup/DataLayerCoreInitialise.cs create mode 100644 DataLayer.Core/Startup/Internal/BlogsContentSimple.xml create mode 100644 DataLayer.Core/Startup/Internal/BlogsContextMedium.xml create mode 100644 DataLayer.Core/Startup/Internal/LoadDbDataFromXml.cs create mode 100644 Tests.Core/DataLayerCoreTests.cs delete mode 100644 Tests.Core/UnitTest1.cs diff --git a/DataLayer.Core/Class1.cs b/DataLayer.Core/Class1.cs deleted file mode 100644 index e14d7d8..0000000 --- a/DataLayer.Core/Class1.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System; - -namespace DataLayer.Core -{ - public class Class1 - { - - } -} diff --git a/DataLayer.Core/DataClasses/Concrete/Blog.cs b/DataLayer.Core/DataClasses/Concrete/Blog.cs new file mode 100644 index 0000000..986046a --- /dev/null +++ b/DataLayer.Core/DataClasses/Concrete/Blog.cs @@ -0,0 +1,54 @@ +#region licence +// The MIT License (MIT) +// +// Filename: Blog.cs +// Date Created: 2014/05/20 +// +// Copyright (c) 2014 Jon Smith (www.selectiveanalytics.com & www.thereformedprogrammer.net) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +#endregion +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; + +namespace DataLayer.Core.DataClasses.Concrete +{ + public class Blog + { + public int BlogId { get; set; } + + [MinLength(2)] + [MaxLength(64)] + [Required] + public string Name { get; set; } + + [MaxLength(256)] + [Required] + [EmailAddress] + public string EmailAddress { get; set; } + + public ICollection Posts { get; set; } + + public override string ToString() + { + return string.Format("BlogId: {0}, Name: {1}, EmailAddress: {2}, NumPosts: {3}", + BlogId, Name, EmailAddress, Posts == null ? "null" : Posts.Count.ToString()); + } + } +} diff --git a/DataLayer.Core/DataClasses/Concrete/Helpers/TrackUpdate.cs b/DataLayer.Core/DataClasses/Concrete/Helpers/TrackUpdate.cs new file mode 100644 index 0000000..3c1f8e7 --- /dev/null +++ b/DataLayer.Core/DataClasses/Concrete/Helpers/TrackUpdate.cs @@ -0,0 +1,45 @@ +#region licence +// The MIT License (MIT) +// +// Filename: TrackUpdate.cs +// Date Created: 2014/05/20 +// +// Copyright (c) 2014 Jon Smith (www.selectiveanalytics.com & www.thereformedprogrammer.net) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +#endregion +using System; + +namespace DataLayer.Core.DataClasses.Concrete.Helpers +{ + public abstract class TrackUpdate + { + public DateTime LastUpdated { get; protected set; } + + internal void UpdateTrackingInfo() + { + LastUpdated = DateTime.UtcNow; + } + + protected TrackUpdate() + { + UpdateTrackingInfo(); + } + } +} diff --git a/DataLayer.Core/DataClasses/Concrete/Post.cs b/DataLayer.Core/DataClasses/Concrete/Post.cs new file mode 100644 index 0000000..9ef9f96 --- /dev/null +++ b/DataLayer.Core/DataClasses/Concrete/Post.cs @@ -0,0 +1,76 @@ +#region licence +// The MIT License (MIT) +// +// Filename: Post.cs +// Date Created: 2014/05/20 +// +// Copyright (c) 2014 Jon Smith (www.selectiveanalytics.com & www.thereformedprogrammer.net) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +#endregion +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using DataLayer.Core.DataClasses.Concrete.Helpers; + +namespace DataLayer.Core.DataClasses.Concrete +{ + public class Post : TrackUpdate, IValidatableObject + { + public int PostId { get; set; } + + [MinLength(2), MaxLength(128)] + [Required] + public string Title { get; set; } + + [Required] + public string Content { get; set; } + + public int BlogId { get; set; } + public virtual Blog Blogger { get; set; } + + public ICollection Tags { get; set; } + + public override string ToString() + { + return string.Format("PostId: {0}, Title: {1}, BlogId: {2}, Blogger: {3}, AllocatedTags: {4}", + PostId, Title, BlogId, Blogger == null ? "null" : Blogger.Name, Tags == null ? "null" : Tags.Count().ToString()); + } + + public IEnumerable Validate(ValidationContext validationContext) + { + if (Tags != null && !Tags.Any()) + yield return new ValidationResult("The post must have at least one Tag.", new[] { "AllocatedTags" }); + + if (Title.Contains("!")) + yield return new ValidationResult("Sorry, but you can't get too excited and include a ! in the title.", new[] { "Title" }); + if (Title.EndsWith("?")) + yield return new ValidationResult("Sorry, but you can't ask a question, i.e. the title can't end with '?'.", new[] { "Title" }); + + if (Content.Contains(" sheep.")) + yield return new ValidationResult("Sorry. Not allowed to end a sentance with 'sheep'."); + if (Content.Contains(" lamb.")) + yield return new ValidationResult("Sorry. Not allowed to end a sentance with 'lamb'."); + if (Content.Contains(" cow.")) + yield return new ValidationResult("Sorry. Not allowed to end a sentance with 'cow'."); + if (Content.Contains(" calf.")) + yield return new ValidationResult("Sorry. Not allowed to end a sentance with 'calf'."); + } + } +} diff --git a/DataLayer.Core/DataClasses/Concrete/Tag.cs b/DataLayer.Core/DataClasses/Concrete/Tag.cs new file mode 100644 index 0000000..9f3b8cf --- /dev/null +++ b/DataLayer.Core/DataClasses/Concrete/Tag.cs @@ -0,0 +1,52 @@ +#region licence +// The MIT License (MIT) +// +// Filename: Tag.cs +// Date Created: 2014/05/20 +// +// Copyright (c) 2014 Jon Smith (www.selectiveanalytics.com & www.thereformedprogrammer.net) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +#endregion +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; + +namespace DataLayer.Core.DataClasses.Concrete +{ + public class Tag + { + public int TagId { get; set; } + + [MaxLength(64)] + [Required] + [RegularExpression(@"\w*", ErrorMessage = "The slug must not contain spaces or non-alphanumeric characters.")] + public string Slug { get; set; } + + [MaxLength(128)] + [Required] + public string Name { get; set; } + + public ICollection Posts { get; set; } + + public override string ToString() + { + return string.Format("TagId: {0}, Name: {1}, Slug: {2}", TagId, Name, Slug); + } + } +} diff --git a/DataLayer.Core/DataClasses/SampleWebAppDbCore.cs b/DataLayer.Core/DataClasses/SampleWebAppDbCore.cs new file mode 100644 index 0000000..4b1d5a5 --- /dev/null +++ b/DataLayer.Core/DataClasses/SampleWebAppDbCore.cs @@ -0,0 +1,125 @@ +#region licence +// The MIT License (MIT) +// +// Filename: SampleWebAppDbCore.cs +// Date Created: 2014/05/20 +// +// Copyright (c) 2014 Jon Smith (www.selectiveanalytics.com & www.thereformedprogrammer.net) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +#endregion +using System; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using DataLayer.Core.DataClasses.Concrete; +using DataLayer.Core.DataClasses.Concrete.Helpers; +using Microsoft.EntityFrameworkCore; + +namespace DataLayer.Core.DataClasses +{ + public class SampleWebAppDbCore : DbContext + { + public const string NameOfConnectionString = "SampleWebAppDb"; + + private static readonly string DefaultConnectionString = + @"Data Source=(localdb)\mssqllocaldb;Initial Catalog=SampleWebAppDb;MultipleActiveResultSets=True;Integrated Security=SSPI;Trusted_Connection=True"; + + private readonly string _connectionString; + + public DbSet Blogs { get; set; } + public DbSet Posts { get; set; } + public DbSet Tags { get; set; } + + public SampleWebAppDbCore() : base() + { + _connectionString = DefaultConnectionString; + } + + public SampleWebAppDbCore(DbContextOptions options) : base(options) + { + } + + public SampleWebAppDbCore(string connectionString) : base() + { + _connectionString = connectionString; + } + + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + { + if (!optionsBuilder.IsConfigured) + { + optionsBuilder.UseSqlServer(_connectionString ?? DefaultConnectionString); + } + } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + base.OnModelCreating(modelBuilder); + + modelBuilder.Entity() + .HasIndex(t => t.Slug) + .IsUnique(); + + modelBuilder.Entity() + .HasOne(p => p.Blogger) + .WithMany(b => b.Posts) + .HasForeignKey(p => p.BlogId); + + modelBuilder.Entity() + .HasMany(p => p.Tags) + .WithMany(t => t.Posts); + } + + public override int SaveChanges() + { + HandleChangeTracking(); + return base.SaveChanges(); + } + + public override int SaveChanges(bool acceptAllChangesOnSuccess) + { + HandleChangeTracking(); + return base.SaveChanges(acceptAllChangesOnSuccess); + } + + public override Task SaveChangesAsync(CancellationToken cancellationToken = default) + { + HandleChangeTracking(); + return base.SaveChangesAsync(cancellationToken); + } + + public override Task SaveChangesAsync(bool acceptAllChangesOnSuccess, CancellationToken cancellationToken = default) + { + HandleChangeTracking(); + return base.SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken); + } + + private void HandleChangeTracking() + { + foreach (var entity in ChangeTracker.Entries() + .Where(e => e.State == EntityState.Added || e.State == EntityState.Modified)) + { + var trackUpdateClass = entity.Entity as TrackUpdate; + if (trackUpdateClass == null) continue; + trackUpdateClass.UpdateTrackingInfo(); + } + } + } +} diff --git a/DataLayer.Core/DataLayer.Core.csproj b/DataLayer.Core/DataLayer.Core.csproj index dbdcea4..2ca2a3e 100644 --- a/DataLayer.Core/DataLayer.Core.csproj +++ b/DataLayer.Core/DataLayer.Core.csproj @@ -1,7 +1,24 @@ - + - netstandard2.0 + net8.0 + enable + enable + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + diff --git a/DataLayer.Core/Startup/DataLayerCoreInitialise.cs b/DataLayer.Core/Startup/DataLayerCoreInitialise.cs new file mode 100644 index 0000000..7fd3a93 --- /dev/null +++ b/DataLayer.Core/Startup/DataLayerCoreInitialise.cs @@ -0,0 +1,105 @@ +#region licence +// The MIT License (MIT) +// +// Filename: DataLayerCoreInitialise.cs +// Date Created: 2014/05/20 +// +// Copyright (c) 2014 Jon Smith (www.selectiveanalytics.com & www.thereformedprogrammer.net) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +#endregion +using System; +using System.Collections.Generic; +using System.Linq; +using DataLayer.Core.DataClasses; +using DataLayer.Core.Startup.Internal; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Logging; + +namespace DataLayer.Core.Startup +{ + public enum TestDataSelection { Small = 0, Medium = 1 } + + public static class DataLayerCoreInitialise + { + private static ILogger _logger; + + private static readonly Dictionary XmlBlogsDataFileManifestPath = new Dictionary + { + { TestDataSelection.Small, "DataLayer.Core.Startup.Internal.BlogsContentSimple.xml" }, + { TestDataSelection.Medium, "DataLayer.Core.Startup.Internal.BlogsContextMedium.xml" } + }; + + public static void InitialiseThis(SampleWebAppDbCore context, ILogger logger = null, bool ensureCreated = true) + { + _logger = logger; + + if (ensureCreated) + { + context.Database.EnsureCreated(); + } + } + + public static void MigrateDatabase(SampleWebAppDbCore context, ILogger logger = null) + { + _logger = logger; + + try + { + context.Database.Migrate(); + _logger?.LogInformation("Database migration completed successfully."); + } + catch (Exception ex) + { + _logger?.LogError(ex, "Error during database migration."); + throw; + } + } + + public static void ResetBlogs(SampleWebAppDbCore context, TestDataSelection selection) + { + try + { + context.Posts.ToList().ForEach(x => context.Posts.Remove(x)); + context.Tags.ToList().ForEach(x => context.Tags.Remove(x)); + context.Blogs.ToList().ForEach(x => context.Blogs.Remove(x)); + context.SaveChanges(); + } + catch (Exception ex) + { + _logger?.LogCritical(ex, "Exception when resetting the blogs"); + throw; + } + + var bloggers = LoadDbDataFromXml.FormBlogsWithPosts(XmlBlogsDataFileManifestPath[selection]); + + context.Blogs.AddRange(bloggers); + + try + { + context.SaveChanges(); + } + catch (Exception ex) + { + _logger?.LogCritical(ex, "Error when resetting courses data."); + throw new FormatException("Problem writing to database. See log.", ex); + } + } + } +} diff --git a/DataLayer.Core/Startup/Internal/BlogsContentSimple.xml b/DataLayer.Core/Startup/Internal/BlogsContentSimple.xml new file mode 100644 index 0000000..1808793 --- /dev/null +++ b/DataLayer.Core/Startup/Internal/BlogsContentSimple.xml @@ -0,0 +1,49 @@ + + + + + Good post + good + + + Bad post + bad + + + Ugly post + ugly + + + + + Jon Smith + jon.smith@nospam.com + + + First great post + A fine set of words. +In two lines. + good,ugly + + + Second post, which isn't bad + Another fine set of words. +With this line +and another line, making three lines. + bad + + + + + Fred Bloggs + fred.bloggs@nospam.com + + + Freds good post + He hasn't got much to say. + ugly,bad + + + + + diff --git a/DataLayer.Core/Startup/Internal/BlogsContextMedium.xml b/DataLayer.Core/Startup/Internal/BlogsContextMedium.xml new file mode 100644 index 0000000..5a2f4aa --- /dev/null +++ b/DataLayer.Core/Startup/Internal/BlogsContextMedium.xml @@ -0,0 +1,377 @@ + + + + + About Me + about + + + Business + biz + + + My Opinion + opinion + + + Education + edu + + + Mystery + mystery + + + Entertainment + entertain + + + Vices and Foibles + vices + + + Personal + personal + + + Murder and Mayhem + murder + + + Programming + programming + + + + + Ada Lovelace + ada@nospam.com + + + About Ada Lovelace + The following was taken from Wikipedia, the free encyclopedia. + + Augusta Ada King, Countess of Lovelace (10 December 1815 – 27 November 1852), born Augusta Ada Byron and now + commonly known as Ada Lovelace, was an English mathematician and writer chiefly known for her work on + Charles Babbage's early mechanical general-purpose computer, the Analytical Engine. Her notes on the engine + include what is recognised as the first algorithm intended to be carried out by a machine. Because of this, + she is often described as the world's first computer programmer. + + Lovelace was born 10 December 1815 as the only child of the poet Lord Byron and his wife Anne Isabella Byron. + All Byron's other children were born out of wedlock to other women. + Byron separated from his wife a month after Ada was born and left England forever four months later, + eventually dying of disease in the Greek War of Independence when Ada was eight years old. + Ada's mother remained bitter at Lord Byron and promoted Ada's interest in mathematics and logic in an + effort to prevent her from developing what she saw as the insanity seen in her father, but Ada remained + interested in him despite this (and was, upon her eventual death, buried next to him at her request). + + ------- + Personal note: + + My wife, Dr. Honora Smith, is related to Ada Lovelace, which is why she appears in this blog post. + + about + + + Thoughts on programming with a spanner + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur lobortis nibh sit amet mauris blandit molestie. + Vivamus id enim orci. Duis vitae aliquet risus. Sed lorem felis, mattis non elementum rhoncus, + ultricies in mauris. Aliquam erat volutpat. Interdum et malesuada fames ac ante ipsum primis in faucibus. + Nunc suscipit posuere augue, id rhoncus augue dictum condimentum. Sed et ante sapien. Phasellus convallis + in quam ut posuere. Mauris rhoncus et orci in lobortis. Duis velit diam, semper sit amet mollis non, + sollicitudin nec turpis. Cras semper sem ligula, non porta odio ornare at. Vestibulum in odio vel est + mollis aliquam vel non elit. Interdum et malesuada fames ac ante ipsum primis in faucibus. + Mauris aliquet mauris commodo dui vehicula viverra. Nam dignissim cursus arcu, non tincidunt est posuere quis. + + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur lobortis nibh sit amet mauris blandit molestie. + Vivamus id enim orci. Duis vitae aliquet risus. Sed lorem felis, mattis non elementum rhoncus, + ultricies in mauris. Aliquam erat volutpat. Interdum et malesuada fames ac ante ipsum primis in faucibus. + Nunc suscipit posuere augue, id rhoncus augue dictum condimentum. Sed et ante sapien. Phasellus convallis + in quam ut posuere. Mauris rhoncus et orci in lobortis. Duis velit diam, semper sit amet mollis non, + sollicitudin nec turpis. Cras semper sem ligula, non porta odio ornare at. Vestibulum in odio vel est + mollis aliquam vel non elit. Interdum et malesuada fames ac ante ipsum primis in faucibus. + Mauris aliquet mauris commodo dui vehicula viverra. Nam dignissim cursus arcu, non tincidunt est posuere quis. + + programming,opinion + + + Babbage engine - the internals + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur lobortis nibh sit amet mauris blandit molestie. + Vivamus id enim orci. Duis vitae aliquet risus. Sed lorem felis, mattis non elementum rhoncus, + ultricies in mauris. Aliquam erat volutpat. Interdum et malesuada fames ac ante ipsum primis in faucibus. + Nunc suscipit posuere augue, id rhoncus augue dictum condimentum. Sed et ante sapien. Phasellus convallis + in quam ut posuere. Mauris rhoncus et orci in lobortis. Duis velit diam, semper sit amet mollis non, + sollicitudin nec turpis. Cras semper sem ligula, non porta odio ornare at. Vestibulum in odio vel est + mollis aliquam vel non elit. Interdum et malesuada fames ac ante ipsum primis in faucibus. + Mauris aliquet mauris commodo dui vehicula viverra. Nam dignissim cursus arcu, non tincidunt est posuere quis. + + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur lobortis nibh sit amet mauris blandit molestie. + Vivamus id enim orci. Duis vitae aliquet risus. Sed lorem felis, mattis non elementum rhoncus, + ultricies in mauris. Aliquam erat volutpat. Interdum et malesuada fames ac ante ipsum primis in faucibus. + Nunc suscipit posuere augue, id rhoncus augue dictum condimentum. Sed et ante sapien. Phasellus convallis + in quam ut posuere. Mauris rhoncus et orci in lobortis. Duis velit diam, semper sit amet mollis non, + sollicitudin nec turpis. Cras semper sem ligula, non porta odio ornare at. Vestibulum in odio vel est + mollis aliquam vel non elit. Interdum et malesuada fames ac ante ipsum primis in faucibus. + Mauris aliquet mauris commodo dui vehicula viverra. Nam dignissim cursus arcu, non tincidunt est posuere quis. + + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur lobortis nibh sit amet mauris blandit molestie. + Vivamus id enim orci. Duis vitae aliquet risus. Sed lorem felis, mattis non elementum rhoncus, + ultricies in mauris. Aliquam erat volutpat. Interdum et malesuada fames ac ante ipsum primis in faucibus. + Nunc suscipit posuere augue, id rhoncus augue dictum condimentum. Sed et ante sapien. Phasellus convallis + in quam ut posuere. Mauris rhoncus et orci in lobortis. Duis velit diam, semper sit amet mollis non, + sollicitudin nec turpis. Cras semper sem ligula, non porta odio ornare at. Vestibulum in odio vel est + mollis aliquam vel non elit. Interdum et malesuada fames ac ante ipsum primis in faucibus. + Mauris aliquet mauris commodo dui vehicula viverra. Nam dignissim cursus arcu, non tincidunt est posuere quis. + + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur lobortis nibh sit amet mauris blandit molestie. + Vivamus id enim orci. Duis vitae aliquet risus. Sed lorem felis, mattis non elementum rhoncus, + ultricies in mauris. Aliquam erat volutpat. Interdum et malesuada fames ac ante ipsum primis in faucibus. + Nunc suscipit posuere augue, id rhoncus augue dictum condimentum. Sed et ante sapien. Phasellus convallis + in quam ut posuere. Mauris rhoncus et orci in lobortis. Duis velit diam, semper sit amet mollis non, + sollicitudin nec turpis. Cras semper sem ligula, non porta odio ornare at. Vestibulum in odio vel est + mollis aliquam vel non elit. Interdum et malesuada fames ac ante ipsum primis in faucibus. + Mauris aliquet mauris commodo dui vehicula viverra. Nam dignissim cursus arcu, non tincidunt est posuere quis. + + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur lobortis nibh sit amet mauris blandit molestie. + Vivamus id enim orci. Duis vitae aliquet risus. Sed lorem felis, mattis non elementum rhoncus, + ultricies in mauris. Aliquam erat volutpat. Interdum et malesuada fames ac ante ipsum primis in faucibus. + Nunc suscipit posuere augue, id rhoncus augue dictum condimentum. Sed et ante sapien. Phasellus convallis + in quam ut posuere. Mauris rhoncus et orci in lobortis. Duis velit diam, semper sit amet mollis non, + sollicitudin nec turpis. Cras semper sem ligula, non porta odio ornare at. Vestibulum in odio vel est + mollis aliquam vel non elit. Interdum et malesuada fames ac ante ipsum primis in faucibus. + Mauris aliquet mauris commodo dui vehicula viverra. Nam dignissim cursus arcu, non tincidunt est posuere quis. + + programming,edu + + + An algrorithm for winning on the horses + + The following was paraphrased from Wikipedia, the free encyclopedia. + + Ada Lovelack has a love of gambling. This led to her forming a syndicate with male friends, and an ambitious attempt in 1851 + to create a mathematical model for successful large bets. This went disastrously wrong, leaving her thousands of pounds in debt + to the syndicate, forcing her to admit it all to her husband. + + + programming,vices,personal + + + Women in programming - we got here first + The following was taken from Wikipedia, the free encyclopedia. + + Ada described her approach as "poetical science" and herself as an "Analyst (and Metaphysician)". + As a young adult, her mathematical talents led her to an ongoing working relationship and friendship + with fellow British mathematician Charles Babbage, and in particular Babbage's work on the Analytical Engine. + Between 1842 and 1843, she translated an article by Italian military engineer Luigi Menabrea on the engine, + which she supplemented with an elaborate set of notes of her own, simply called Notes. + These notes contain what many consider to be the first computer program—that is, an algorithm designed + to be carried out by a machine. Lovelace's notes are important in the early history of computers. + She also developed a vision on the capability of computers to go beyond mere calculating or number-crunching while others, + including Babbage himself, focused only on those capabilities. Her mind-set of "poetical science" led her to + ask basic questions about the Analytical Engine (as shown in her notes) examining how individuals and society + relate to technology as a collaborative tool. + + programming,personal,opinion + + + + + Sherlock Holmes + sherlock@nospam.com + + + About Sherlock Holmes + The following was taken from Wikipedia, the free encyclopedia. + + Sherlock Holmes is a fictional detective created by Scottish author and physician Sir Arthur Conan Doyle. + A London-based "consulting detective" whose abilities border on the fantastic, Holmes is famous for his + astute logical reasoning, his ability to adopt almost any disguise, and his use of forensic science skills to solve difficult cases. + + Holmes, who first appeared in publication in 1887, was featured in four novels and 56 short stories. + The first novel, A Study in Scarlet, appeared in Beeton's Christmas Annual in 1887 and the second, + The Sign of the Four, in Lippincott's Monthly Magazine in 1890. The character grew tremendously in + popularity with the first series of short stories in The Strand Magazine, beginning with + "A Scandal in Bohemia" in 1891; further series of short stories and two novels published in serial + form appeared between then and 1927. The stories cover a period from around 1880 up to 1914. + + about + + + Sound qualities of 221b Baker Street + Sed adipiscing eros et ipsum consectetur, vel pulvinar leo viverra. Phasellus posuere molestie magna, + non sagittis mauris rhoncus et. Fusce blandit, ante quis scelerisque rutrum, arcu eros adipiscing lorem, + ac pretium elit augue quis quam. Donec eu semper turpis. Quisque quis est cursus, iaculis turpis pretium, + bibendum ante. Aliquam pharetra sem viverra nibh molestie, eu euismod eros rutrum. Cras ultricies enim at leo + laoreet dignissim. Nulla rhoncus, leo ac sollicitudin egestas, nunc nisi cursus augue, rutrum pellentesque + dolor lacus et odio. Curabitur ac iaculis justo. In nec ullamcorper mi. Donec arcu quam, posuere id magna + semper, mattis imperdiet augue. Aenean interdum massa sed tincidunt tempor. Nam mollis laoreet ante. + Phasellus faucibus condimentum massa, at euismod ligula. Maecenas facilisis mi sed consectetur consequat. + + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur lobortis nibh sit amet mauris blandit molestie. + Vivamus id enim orci. Duis vitae aliquet risus. Sed lorem felis, mattis non elementum rhoncus, + ultricies in mauris. Aliquam erat volutpat. Interdum et malesuada fames ac ante ipsum primis in faucibus. + Nunc suscipit posuere augue, id rhoncus augue dictum condimentum. Sed et ante sapien. Phasellus convallis + in quam ut posuere. Mauris rhoncus et orci in lobortis. Duis velit diam, semper sit amet mollis non, + sollicitudin nec turpis. Cras semper sem ligula, non porta odio ornare at. Vestibulum in odio vel est + mollis aliquam vel non elit. Interdum et malesuada fames ac ante ipsum primis in faucibus. + Mauris aliquet mauris commodo dui vehicula viverra. Nam dignissim cursus arcu, non tincidunt est posuere quis. + + entertain,personal + + + Using urchins as an information source + Sed adipiscing eros et ipsum consectetur, vel pulvinar leo viverra. Phasellus posuere molestie magna, + non sagittis mauris rhoncus et. Fusce blandit, ante quis scelerisque rutrum, arcu eros adipiscing lorem, + ac pretium elit augue quis quam. Donec eu semper turpis. Quisque quis est cursus, iaculis turpis pretium, + bibendum ante. Aliquam pharetra sem viverra nibh molestie, eu euismod eros rutrum. Cras ultricies enim at leo + laoreet dignissim. Nulla rhoncus, leo ac sollicitudin egestas, nunc nisi cursus augue, rutrum pellentesque + dolor lacus et odio. Curabitur ac iaculis justo. In nec ullamcorper mi. Donec arcu quam, posuere id magna + semper, mattis imperdiet augue. Aenean interdum massa sed tincidunt tempor. Nam mollis laoreet ante. + Phasellus faucibus condimentum massa, at euismod ligula. Maecenas facilisis mi sed consectetur consequat. + + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur lobortis nibh sit amet mauris blandit molestie. + Vivamus id enim orci. Duis vitae aliquet risus. Sed lorem felis, mattis non elementum rhoncus, + ultricies in mauris. Aliquam erat volutpat. Interdum et malesuada fames ac ante ipsum primis in faucibus. + Nunc suscipit posuere augue, id rhoncus augue dictum condimentum. Sed et ante sapien. Phasellus convallis + in quam ut posuere. Mauris rhoncus et orci in lobortis. Duis velit diam, semper sit amet mollis non, + sollicitudin nec turpis. Cras semper sem ligula, non porta odio ornare at. Vestibulum in odio vel est + mollis aliquam vel non elit. Interdum et malesuada fames ac ante ipsum primis in faucibus. + Mauris aliquet mauris commodo dui vehicula viverra. Nam dignissim cursus arcu, non tincidunt est posuere quis. + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur lobortis nibh sit amet mauris blandit molestie. + + Vivamus id enim orci. Duis vitae aliquet risus. Sed lorem felis, mattis non elementum rhoncus, + ultricies in mauris. Aliquam erat volutpat. Interdum et malesuada fames ac ante ipsum primis in faucibus. + Nunc suscipit posuere augue, id rhoncus augue dictum condimentum. Sed et ante sapien. Phasellus convallis + in quam ut posuere. Mauris rhoncus et orci in lobortis. Duis velit diam, semper sit amet mollis non, + sollicitudin nec turpis. Cras semper sem ligula, non porta odio ornare at. Vestibulum in odio vel est + mollis aliquam vel non elit. Interdum et malesuada fames ac ante ipsum primis in faucibus. + Mauris aliquet mauris commodo dui vehicula viverra. Nam dignissim cursus arcu, non tincidunt est posuere quis. + + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur lobortis nibh sit amet mauris blandit molestie. + Vivamus id enim orci. Duis vitae aliquet risus. Sed lorem felis, mattis non elementum rhoncus, + ultricies in mauris. Aliquam erat volutpat. Interdum et malesuada fames ac ante ipsum primis in faucibus. + Nunc suscipit posuere augue, id rhoncus augue dictum condimentum. Sed et ante sapien. Phasellus convallis + in quam ut posuere. Mauris rhoncus et orci in lobortis. Duis velit diam, semper sit amet mollis non, + sollicitudin nec turpis. Cras semper sem ligula, non porta odio ornare at. Vestibulum in odio vel est + mollis aliquam vel non elit. Interdum et malesuada fames ac ante ipsum primis in faucibus. + Mauris aliquet mauris commodo dui vehicula viverra. Nam dignissim cursus arcu, non tincidunt est posuere quis. + + biz + + + My considered option on the TV adaptions of my life + I enjoy "Elementary" on Sky, although the person portraying my is a little dull comparied to me. + Watson however is rather grand, and in my opinion more intelligent than my poor Waston I had to work with. + + The BBC "Sherlock" is entertaining but later versions have become more darker than I would like. + I realise we are dealing with the dregs of society, but we must not be pulled down to their level you know. + + opinion,entertain + + + Professor Moriarty's habits and acquaintances + Sed adipiscing eros et ipsum consectetur, vel pulvinar leo viverra. Phasellus posuere molestie magna, + non sagittis mauris rhoncus et. Fusce blandit, ante quis scelerisque rutrum, arcu eros adipiscing lorem, + ac pretium elit augue quis quam. Donec eu semper turpis. Quisque quis est cursus, iaculis turpis pretium, + bibendum ante. Aliquam pharetra sem viverra nibh molestie, eu euismod eros rutrum. Cras ultricies enim at leo + laoreet dignissim. Nulla rhoncus, leo ac sollicitudin egestas, nunc nisi cursus augue, rutrum pellentesque + dolor lacus et odio. Curabitur ac iaculis justo. In nec ullamcorper mi. Donec arcu quam, posuere id magna + semper, mattis imperdiet augue. Aenean interdum massa sed tincidunt tempor. Nam mollis laoreet ante. + Phasellus faucibus condimentum massa, at euismod ligula. Maecenas facilisis mi sed consectetur consequat. + + - Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur lobortis nibh sit amet mauris blandit molestie. + - Vivamus id enim orci. Duis vitae aliquet risus. + - Sed lorem felis, mattis non elementum rhoncus, ultricies in mauris. Aliquam erat volutpat. + - Interdum et malesuada fames ac ante ipsum primis in faucibus. + - Nunc suscipit posuere augue, id rhoncus augue dictum condimentum. + + Sed et ante sapien. Phasellus convallis + in quam ut posuere. Mauris rhoncus et orci in lobortis. Duis velit diam, semper sit amet mollis non, + sollicitudin nec turpis. Cras semper sem ligula, non porta odio ornare at. Vestibulum in odio vel est + mollis aliquam vel non elit. Interdum et malesuada fames ac ante ipsum primis in faucibus. + Mauris aliquet mauris commodo dui vehicula viverra. Nam dignissim cursus arcu, non tincidunt est posuere quis. + + edu,biz + + + + + Homer Simpson + doh@nospam.com + + + About Homer Simpson + The following was taken from Wikipedia, the free encyclopedia. + + Homer Jay Simpson is a cartoon character in the animated television series The Simpsons as the + patriarch of the eponymous family. He is voiced by Dan Castellaneta and first appeared on television, + along with the rest of his family, in The Tracey Ullman Show short "Good Night" on April 19, 1987. + Homer was created and designed by cartoonist Matt Groening while he was waiting in the lobby of + James L. Brooks' office. Groening had been called to pitch a series of shorts based on his comic strip + Life in Hell but instead decided to create a new set of characters. He named the character after his father, + Homer Groening. After appearing for three seasons on The Tracey Ullman Show, the Simpson family got their + own series on Fox that debuted December 17, 1989. + + Homer and his wife Marge have three children: Bart, Lisa, and Maggie. As the family's provider, + he works at the Springfield Nuclear Power Plant. Homer embodies several American working class stereotypes: + he is crude, bald, overweight, incompetent, clumsy, lazy, a heavy drinker, and ignorant; however, he is + essentially a decent man and fiercely devoted to his family. Despite the suburban blue-collar routine of + his life, he has had a number of remarkable experiences. + + about + + + I am in love with Beer, oh sorry I meant Marge + I don't say much other than Beer is good. + + vices,personal + + + Homer's Php... No, pph... D'oh, PhD. + What's a PhD.? + + edu + + + + + Yoni the cat + yoni@nospam.com + + + About Yoni the cat + Yoni the cat gracously stays with our son Josh and his partner Katie in London, UK. + He had a nasty accident and now only has three legs. + + about + + + Why I only have three legs + I don't remember much about the accident, but I was out on the town and something nasty happened. + My human servants fixed me up and I am still able to get around, although I do need more fuss because of this + traumatic event. + + personal + + + I might allow you to pet me... + Ut malesuada nibh quis velit tincidunt sollicitudin cursus placerat lorem. Praesent laoreet, + erat vel viverra euismod, massa nunc vehicula lectus, imperdiet convallis nisi nisl in felis. + + personal,vices + + + The 17 things humans do that I find annoying + Ut malesuada nibh quis velit tincidunt sollicitudin cursus placerat lorem. Praesent laoreet, + erat vel viverra euismod, massa nunc vehicula lectus, imperdiet convallis nisi nisl in felis. + Interdum et malesuada fames ac ante ipsum primis in faucibus. Nullam laoreet scelerisque nulla. + Phasellus eleifend erat vitae blandit porta. Maecenas sed congue mi. Vivamus felis neque, iaculis + sit amet sollicitudin ac, rhoncus et enim. Etiam non ullamcorper elit. Phasellus convallis nibh ut + mollis aliquet. Nullam commodo mauris vel mauris hendrerit aliquet. + + personal,opinion + + + + + diff --git a/DataLayer.Core/Startup/Internal/LoadDbDataFromXml.cs b/DataLayer.Core/Startup/Internal/LoadDbDataFromXml.cs new file mode 100644 index 0000000..4c60818 --- /dev/null +++ b/DataLayer.Core/Startup/Internal/LoadDbDataFromXml.cs @@ -0,0 +1,98 @@ +#region licence +// The MIT License (MIT) +// +// Filename: LoadDbDataFromXml.cs +// Date Created: 2014/05/22 +// +// Copyright (c) 2014 Jon Smith (www.selectiveanalytics.com & www.thereformedprogrammer.net) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +#endregion +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Reflection; +using System.Xml.Linq; +using DataLayer.Core.DataClasses.Concrete; + +namespace DataLayer.Core.Startup.Internal +{ + internal static class LoadDbDataFromXml + { + public static IEnumerable FormBlogsWithPosts(string filepathWithinAssembly) + { + var assemblyHoldingFile = Assembly.GetAssembly(typeof(LoadDbDataFromXml)); + + using (var fileStream = assemblyHoldingFile.GetManifestResourceStream(filepathWithinAssembly)) + { + if (fileStream == null) + throw new NullReferenceException("Could not find the xml file you asked for. Did you remember to set properties->BuildAction to Embedded Resource?"); + var xmlData = XElement.Load(fileStream); + + var tagsDict = DecodeTags(xmlData.Element("Tags")); + return DecodeBlogs(xmlData.Element("Blogs"), tagsDict); + } + } + + private static IEnumerable DecodeBlogs(XElement element, Dictionary tagsDict) + { + var result = new Collection(); + foreach (var blogXml in element.Elements("Blog")) + { + var newBlogger = new Blog() + { + Name = blogXml.Element("Name").Value, + EmailAddress = blogXml.Element("Email").Value, + Posts = new Collection() + }; + + foreach (var postXml in blogXml.Element("Posts").Elements("Post")) + { + var content = postXml.Element("Content").Value; + var trimmedContent = string.Join("\n", content.Split('\n').Select(x => x.Trim())); + var newPost = new Post() + { + Blogger = newBlogger, + Title = postXml.Element("Title").Value, + Content = trimmedContent, + Tags = postXml.Element("TagSlugs").Value.Split(',').Select(x => tagsDict[x.Trim()]).ToList() + }; + newBlogger.Posts.Add(newPost); + } + result.Add(newBlogger); + } + return result; + } + + private static Dictionary DecodeTags(XElement element) + { + var result = new Dictionary(); + foreach (var newTag in element.Elements("Tag").Select(tagXml => new Tag() + { + Name = tagXml.Element("Name").Value, + Slug = tagXml.Element("Slug").Value + })) + { + result[newTag.Slug] = newTag; + } + return result; + } + } +} diff --git a/ServiceLayer.Core/ServiceLayer.Core.csproj b/ServiceLayer.Core/ServiceLayer.Core.csproj index bb6bfbd..13698ff 100644 --- a/ServiceLayer.Core/ServiceLayer.Core.csproj +++ b/ServiceLayer.Core/ServiceLayer.Core.csproj @@ -1,11 +1,13 @@ - + + + + net8.0 + enable + enable + - - netstandard2.0 - - diff --git a/Tests.Core/DataLayerCoreTests.cs b/Tests.Core/DataLayerCoreTests.cs new file mode 100644 index 0000000..34ae41e --- /dev/null +++ b/Tests.Core/DataLayerCoreTests.cs @@ -0,0 +1,294 @@ +using DataLayer.Core.DataClasses; +using DataLayer.Core.DataClasses.Concrete; +using Microsoft.EntityFrameworkCore; +using System.ComponentModel.DataAnnotations; + +namespace Tests.Core; + +public class DataLayerCoreTests +{ + private static DbContextOptions GetInMemoryOptions(string dbName) + { + return new DbContextOptionsBuilder() + .UseInMemoryDatabase(databaseName: dbName) + .Options; + } + + [Fact] + public void SampleWebAppDbCore_CanBeCreated() + { + var options = GetInMemoryOptions("TestDb_CanBeCreated"); + using var context = new SampleWebAppDbCore(options); + Assert.NotNull(context); + } + + [Fact] + public void SampleWebAppDbCore_HasBlogsDbSet() + { + var options = GetInMemoryOptions("TestDb_HasBlogsDbSet"); + using var context = new SampleWebAppDbCore(options); + Assert.NotNull(context.Blogs); + } + + [Fact] + public void SampleWebAppDbCore_HasPostsDbSet() + { + var options = GetInMemoryOptions("TestDb_HasPostsDbSet"); + using var context = new SampleWebAppDbCore(options); + Assert.NotNull(context.Posts); + } + + [Fact] + public void SampleWebAppDbCore_HasTagsDbSet() + { + var options = GetInMemoryOptions("TestDb_HasTagsDbSet"); + using var context = new SampleWebAppDbCore(options); + Assert.NotNull(context.Tags); + } + + [Fact] + public void Blog_CanBeAddedAndRetrieved() + { + var options = GetInMemoryOptions("TestDb_BlogCanBeAdded"); + using (var context = new SampleWebAppDbCore(options)) + { + var blog = new Blog + { + Name = "Test Blog", + EmailAddress = "test@example.com" + }; + context.Blogs.Add(blog); + context.SaveChanges(); + } + + using (var context = new SampleWebAppDbCore(options)) + { + var blog = context.Blogs.FirstOrDefault(); + Assert.NotNull(blog); + Assert.Equal("Test Blog", blog.Name); + Assert.Equal("test@example.com", blog.EmailAddress); + } + } + + [Fact] + public void Tag_CanBeAddedAndRetrieved() + { + var options = GetInMemoryOptions("TestDb_TagCanBeAdded"); + using (var context = new SampleWebAppDbCore(options)) + { + var tag = new Tag + { + Name = "Test Tag", + Slug = "test-tag" + }; + context.Tags.Add(tag); + context.SaveChanges(); + } + + using (var context = new SampleWebAppDbCore(options)) + { + var tag = context.Tags.FirstOrDefault(); + Assert.NotNull(tag); + Assert.Equal("Test Tag", tag.Name); + Assert.Equal("test-tag", tag.Slug); + } + } + + [Fact] + public void Post_CanBeAddedWithBlogRelationship() + { + var options = GetInMemoryOptions("TestDb_PostWithBlog"); + using (var context = new SampleWebAppDbCore(options)) + { + var blog = new Blog + { + Name = "Test Blog", + EmailAddress = "test@example.com" + }; + context.Blogs.Add(blog); + context.SaveChanges(); + + var tag = new Tag + { + Name = "Test Tag", + Slug = "testtag" + }; + context.Tags.Add(tag); + context.SaveChanges(); + + var post = new Post + { + Title = "Test Post", + Content = "Test Content", + BlogId = blog.BlogId, + Tags = new List { tag } + }; + context.Posts.Add(post); + context.SaveChanges(); + } + + using (var context = new SampleWebAppDbCore(options)) + { + var post = context.Posts.Include(p => p.Blogger).FirstOrDefault(); + Assert.NotNull(post); + Assert.Equal("Test Post", post.Title); + Assert.NotNull(post.Blogger); + Assert.Equal("Test Blog", post.Blogger.Name); + } + } + + [Fact] + public void Post_LastUpdatedIsSetOnCreation() + { + var options = GetInMemoryOptions("TestDb_PostLastUpdated"); + using var context = new SampleWebAppDbCore(options); + + var blog = new Blog + { + Name = "Test Blog", + EmailAddress = "test@example.com" + }; + context.Blogs.Add(blog); + context.SaveChanges(); + + var tag = new Tag + { + Name = "Test Tag", + Slug = "testtag" + }; + context.Tags.Add(tag); + context.SaveChanges(); + + var beforeCreate = DateTime.UtcNow; + var post = new Post + { + Title = "Test Post", + Content = "Test Content", + BlogId = blog.BlogId, + Tags = new List { tag } + }; + context.Posts.Add(post); + context.SaveChanges(); + var afterCreate = DateTime.UtcNow; + + Assert.True(post.LastUpdated >= beforeCreate.AddSeconds(-1)); + Assert.True(post.LastUpdated <= afterCreate.AddSeconds(1)); + } + + [Fact] + public void Post_ValidationFailsWithExclamationInTitle() + { + var post = new Post + { + Title = "Test Post!", + Content = "Test Content", + Tags = new List { new Tag { Name = "Tag", Slug = "tag" } } + }; + + var validationContext = new ValidationContext(post); + var validationResults = new List(); + var isValid = Validator.TryValidateObject(post, validationContext, validationResults, true); + + var titleError = validationResults.FirstOrDefault(r => r.MemberNames.Contains("Title")); + Assert.NotNull(titleError); + Assert.Contains("!", titleError.ErrorMessage); + } + + [Fact] + public void Post_ValidationFailsWithQuestionMarkAtEnd() + { + var post = new Post + { + Title = "Test Post?", + Content = "Test Content", + Tags = new List { new Tag { Name = "Tag", Slug = "tag" } } + }; + + var validationContext = new ValidationContext(post); + var validationResults = new List(); + Validator.TryValidateObject(post, validationContext, validationResults, true); + + var titleError = validationResults.FirstOrDefault(r => r.MemberNames.Contains("Title")); + Assert.NotNull(titleError); + Assert.Contains("?", titleError.ErrorMessage); + } + + [Fact] + public void Post_ValidationFailsWithNoTags() + { + var post = new Post + { + Title = "Test Post", + Content = "Test Content", + Tags = new List() + }; + + var validationContext = new ValidationContext(post); + var validationResults = new List(); + Validator.TryValidateObject(post, validationContext, validationResults, true); + + var tagError = validationResults.FirstOrDefault(r => r.MemberNames.Contains("AllocatedTags")); + Assert.NotNull(tagError); + Assert.Contains("at least one Tag", tagError.ErrorMessage); + } + + [Fact] + public void Tag_SlugValidationFailsWithSpaces() + { + var tag = new Tag + { + Name = "Test Tag", + Slug = "test tag" + }; + + var validationContext = new ValidationContext(tag); + var validationResults = new List(); + var isValid = Validator.TryValidateObject(tag, validationContext, validationResults, true); + + Assert.False(isValid); + var slugError = validationResults.FirstOrDefault(r => r.MemberNames.Contains("Slug")); + Assert.NotNull(slugError); + } + + [Fact] + public void Blog_NameRequiredValidation() + { + var blog = new Blog + { + Name = null!, + EmailAddress = "test@example.com" + }; + + var validationContext = new ValidationContext(blog); + var validationResults = new List(); + var isValid = Validator.TryValidateObject(blog, validationContext, validationResults, true); + + Assert.False(isValid); + var nameError = validationResults.FirstOrDefault(r => r.MemberNames.Contains("Name")); + Assert.NotNull(nameError); + } + + [Fact] + public void Blog_EmailValidation() + { + var blog = new Blog + { + Name = "Test Blog", + EmailAddress = "invalid-email" + }; + + var validationContext = new ValidationContext(blog); + var validationResults = new List(); + var isValid = Validator.TryValidateObject(blog, validationContext, validationResults, true); + + Assert.False(isValid); + var emailError = validationResults.FirstOrDefault(r => r.MemberNames.Contains("EmailAddress")); + Assert.NotNull(emailError); + } + + [Fact] + public void SampleWebAppDbCore_ConnectionStringConstantIsCorrect() + { + Assert.Equal("SampleWebAppDb", SampleWebAppDbCore.NameOfConnectionString); + } +} diff --git a/Tests.Core/Tests.Core.csproj b/Tests.Core/Tests.Core.csproj index c76157b..06843c5 100644 --- a/Tests.Core/Tests.Core.csproj +++ b/Tests.Core/Tests.Core.csproj @@ -11,6 +11,7 @@ + diff --git a/Tests.Core/UnitTest1.cs b/Tests.Core/UnitTest1.cs deleted file mode 100644 index 8d8af74..0000000 --- a/Tests.Core/UnitTest1.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace Tests.Core; - -public class UnitTest1 -{ - [Fact] - public void Test1() - { - - } -} \ No newline at end of file