Skip to content

Commit f7bb454

Browse files
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 <abhay.aggarwal@codeium.com>
1 parent f1c885b commit f7bb454

15 files changed

Lines changed: 1302 additions & 26 deletions

DataLayer.Core/Class1.cs

Lines changed: 0 additions & 9 deletions
This file was deleted.
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
#region licence
2+
// The MIT License (MIT)
3+
//
4+
// Filename: Blog.cs
5+
// Date Created: 2014/05/20
6+
//
7+
// Copyright (c) 2014 Jon Smith (www.selectiveanalytics.com & www.thereformedprogrammer.net)
8+
//
9+
// Permission is hereby granted, free of charge, to any person obtaining a copy
10+
// of this software and associated documentation files (the "Software"), to deal
11+
// in the Software without restriction, including without limitation the rights
12+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13+
// copies of the Software, and to permit persons to whom the Software is
14+
// furnished to do so, subject to the following conditions:
15+
//
16+
// The above copyright notice and this permission notice shall be included in all
17+
// copies or substantial portions of the Software.
18+
//
19+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25+
// SOFTWARE.
26+
#endregion
27+
using System.Collections.Generic;
28+
using System.ComponentModel.DataAnnotations;
29+
30+
namespace DataLayer.Core.DataClasses.Concrete
31+
{
32+
public class Blog
33+
{
34+
public int BlogId { get; set; }
35+
36+
[MinLength(2)]
37+
[MaxLength(64)]
38+
[Required]
39+
public string Name { get; set; }
40+
41+
[MaxLength(256)]
42+
[Required]
43+
[EmailAddress]
44+
public string EmailAddress { get; set; }
45+
46+
public ICollection<Post> Posts { get; set; }
47+
48+
public override string ToString()
49+
{
50+
return string.Format("BlogId: {0}, Name: {1}, EmailAddress: {2}, NumPosts: {3}",
51+
BlogId, Name, EmailAddress, Posts == null ? "null" : Posts.Count.ToString());
52+
}
53+
}
54+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
#region licence
2+
// The MIT License (MIT)
3+
//
4+
// Filename: TrackUpdate.cs
5+
// Date Created: 2014/05/20
6+
//
7+
// Copyright (c) 2014 Jon Smith (www.selectiveanalytics.com & www.thereformedprogrammer.net)
8+
//
9+
// Permission is hereby granted, free of charge, to any person obtaining a copy
10+
// of this software and associated documentation files (the "Software"), to deal
11+
// in the Software without restriction, including without limitation the rights
12+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13+
// copies of the Software, and to permit persons to whom the Software is
14+
// furnished to do so, subject to the following conditions:
15+
//
16+
// The above copyright notice and this permission notice shall be included in all
17+
// copies or substantial portions of the Software.
18+
//
19+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25+
// SOFTWARE.
26+
#endregion
27+
using System;
28+
29+
namespace DataLayer.Core.DataClasses.Concrete.Helpers
30+
{
31+
public abstract class TrackUpdate
32+
{
33+
public DateTime LastUpdated { get; protected set; }
34+
35+
internal void UpdateTrackingInfo()
36+
{
37+
LastUpdated = DateTime.UtcNow;
38+
}
39+
40+
protected TrackUpdate()
41+
{
42+
UpdateTrackingInfo();
43+
}
44+
}
45+
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
#region licence
2+
// The MIT License (MIT)
3+
//
4+
// Filename: Post.cs
5+
// Date Created: 2014/05/20
6+
//
7+
// Copyright (c) 2014 Jon Smith (www.selectiveanalytics.com & www.thereformedprogrammer.net)
8+
//
9+
// Permission is hereby granted, free of charge, to any person obtaining a copy
10+
// of this software and associated documentation files (the "Software"), to deal
11+
// in the Software without restriction, including without limitation the rights
12+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13+
// copies of the Software, and to permit persons to whom the Software is
14+
// furnished to do so, subject to the following conditions:
15+
//
16+
// The above copyright notice and this permission notice shall be included in all
17+
// copies or substantial portions of the Software.
18+
//
19+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25+
// SOFTWARE.
26+
#endregion
27+
using System.Collections.Generic;
28+
using System.ComponentModel.DataAnnotations;
29+
using System.Linq;
30+
using DataLayer.Core.DataClasses.Concrete.Helpers;
31+
32+
namespace DataLayer.Core.DataClasses.Concrete
33+
{
34+
public class Post : TrackUpdate, IValidatableObject
35+
{
36+
public int PostId { get; set; }
37+
38+
[MinLength(2), MaxLength(128)]
39+
[Required]
40+
public string Title { get; set; }
41+
42+
[Required]
43+
public string Content { get; set; }
44+
45+
public int BlogId { get; set; }
46+
public virtual Blog Blogger { get; set; }
47+
48+
public ICollection<Tag> Tags { get; set; }
49+
50+
public override string ToString()
51+
{
52+
return string.Format("PostId: {0}, Title: {1}, BlogId: {2}, Blogger: {3}, AllocatedTags: {4}",
53+
PostId, Title, BlogId, Blogger == null ? "null" : Blogger.Name, Tags == null ? "null" : Tags.Count().ToString());
54+
}
55+
56+
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
57+
{
58+
if (Tags != null && !Tags.Any())
59+
yield return new ValidationResult("The post must have at least one Tag.", new[] { "AllocatedTags" });
60+
61+
if (Title.Contains("!"))
62+
yield return new ValidationResult("Sorry, but you can't get too excited and include a ! in the title.", new[] { "Title" });
63+
if (Title.EndsWith("?"))
64+
yield return new ValidationResult("Sorry, but you can't ask a question, i.e. the title can't end with '?'.", new[] { "Title" });
65+
66+
if (Content.Contains(" sheep."))
67+
yield return new ValidationResult("Sorry. Not allowed to end a sentance with 'sheep'.");
68+
if (Content.Contains(" lamb."))
69+
yield return new ValidationResult("Sorry. Not allowed to end a sentance with 'lamb'.");
70+
if (Content.Contains(" cow."))
71+
yield return new ValidationResult("Sorry. Not allowed to end a sentance with 'cow'.");
72+
if (Content.Contains(" calf."))
73+
yield return new ValidationResult("Sorry. Not allowed to end a sentance with 'calf'.");
74+
}
75+
}
76+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
#region licence
2+
// The MIT License (MIT)
3+
//
4+
// Filename: Tag.cs
5+
// Date Created: 2014/05/20
6+
//
7+
// Copyright (c) 2014 Jon Smith (www.selectiveanalytics.com & www.thereformedprogrammer.net)
8+
//
9+
// Permission is hereby granted, free of charge, to any person obtaining a copy
10+
// of this software and associated documentation files (the "Software"), to deal
11+
// in the Software without restriction, including without limitation the rights
12+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13+
// copies of the Software, and to permit persons to whom the Software is
14+
// furnished to do so, subject to the following conditions:
15+
//
16+
// The above copyright notice and this permission notice shall be included in all
17+
// copies or substantial portions of the Software.
18+
//
19+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25+
// SOFTWARE.
26+
#endregion
27+
using System.Collections.Generic;
28+
using System.ComponentModel.DataAnnotations;
29+
30+
namespace DataLayer.Core.DataClasses.Concrete
31+
{
32+
public class Tag
33+
{
34+
public int TagId { get; set; }
35+
36+
[MaxLength(64)]
37+
[Required]
38+
[RegularExpression(@"\w*", ErrorMessage = "The slug must not contain spaces or non-alphanumeric characters.")]
39+
public string Slug { get; set; }
40+
41+
[MaxLength(128)]
42+
[Required]
43+
public string Name { get; set; }
44+
45+
public ICollection<Post> Posts { get; set; }
46+
47+
public override string ToString()
48+
{
49+
return string.Format("TagId: {0}, Name: {1}, Slug: {2}", TagId, Name, Slug);
50+
}
51+
}
52+
}
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
#region licence
2+
// The MIT License (MIT)
3+
//
4+
// Filename: SampleWebAppDbCore.cs
5+
// Date Created: 2014/05/20
6+
//
7+
// Copyright (c) 2014 Jon Smith (www.selectiveanalytics.com & www.thereformedprogrammer.net)
8+
//
9+
// Permission is hereby granted, free of charge, to any person obtaining a copy
10+
// of this software and associated documentation files (the "Software"), to deal
11+
// in the Software without restriction, including without limitation the rights
12+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13+
// copies of the Software, and to permit persons to whom the Software is
14+
// furnished to do so, subject to the following conditions:
15+
//
16+
// The above copyright notice and this permission notice shall be included in all
17+
// copies or substantial portions of the Software.
18+
//
19+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25+
// SOFTWARE.
26+
#endregion
27+
using System;
28+
using System.Linq;
29+
using System.Threading;
30+
using System.Threading.Tasks;
31+
using DataLayer.Core.DataClasses.Concrete;
32+
using DataLayer.Core.DataClasses.Concrete.Helpers;
33+
using Microsoft.EntityFrameworkCore;
34+
35+
namespace DataLayer.Core.DataClasses
36+
{
37+
public class SampleWebAppDbCore : DbContext
38+
{
39+
public const string NameOfConnectionString = "SampleWebAppDb";
40+
41+
private static readonly string DefaultConnectionString =
42+
@"Data Source=(localdb)\mssqllocaldb;Initial Catalog=SampleWebAppDb;MultipleActiveResultSets=True;Integrated Security=SSPI;Trusted_Connection=True";
43+
44+
private readonly string _connectionString;
45+
46+
public DbSet<Blog> Blogs { get; set; }
47+
public DbSet<Post> Posts { get; set; }
48+
public DbSet<Tag> Tags { get; set; }
49+
50+
public SampleWebAppDbCore() : base()
51+
{
52+
_connectionString = DefaultConnectionString;
53+
}
54+
55+
public SampleWebAppDbCore(DbContextOptions<SampleWebAppDbCore> options) : base(options)
56+
{
57+
}
58+
59+
public SampleWebAppDbCore(string connectionString) : base()
60+
{
61+
_connectionString = connectionString;
62+
}
63+
64+
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
65+
{
66+
if (!optionsBuilder.IsConfigured)
67+
{
68+
optionsBuilder.UseSqlServer(_connectionString ?? DefaultConnectionString);
69+
}
70+
}
71+
72+
protected override void OnModelCreating(ModelBuilder modelBuilder)
73+
{
74+
base.OnModelCreating(modelBuilder);
75+
76+
modelBuilder.Entity<Tag>()
77+
.HasIndex(t => t.Slug)
78+
.IsUnique();
79+
80+
modelBuilder.Entity<Post>()
81+
.HasOne(p => p.Blogger)
82+
.WithMany(b => b.Posts)
83+
.HasForeignKey(p => p.BlogId);
84+
85+
modelBuilder.Entity<Post>()
86+
.HasMany(p => p.Tags)
87+
.WithMany(t => t.Posts);
88+
}
89+
90+
public override int SaveChanges()
91+
{
92+
HandleChangeTracking();
93+
return base.SaveChanges();
94+
}
95+
96+
public override int SaveChanges(bool acceptAllChangesOnSuccess)
97+
{
98+
HandleChangeTracking();
99+
return base.SaveChanges(acceptAllChangesOnSuccess);
100+
}
101+
102+
public override Task<int> SaveChangesAsync(CancellationToken cancellationToken = default)
103+
{
104+
HandleChangeTracking();
105+
return base.SaveChangesAsync(cancellationToken);
106+
}
107+
108+
public override Task<int> SaveChangesAsync(bool acceptAllChangesOnSuccess, CancellationToken cancellationToken = default)
109+
{
110+
HandleChangeTracking();
111+
return base.SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken);
112+
}
113+
114+
private void HandleChangeTracking()
115+
{
116+
foreach (var entity in ChangeTracker.Entries()
117+
.Where(e => e.State == EntityState.Added || e.State == EntityState.Modified))
118+
{
119+
var trackUpdateClass = entity.Entity as TrackUpdate;
120+
if (trackUpdateClass == null) continue;
121+
trackUpdateClass.UpdateTrackingInfo();
122+
}
123+
}
124+
}
125+
}
Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,24 @@
1-
<Project Sdk="Microsoft.NET.Sdk">
1+
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
4-
<TargetFramework>netstandard2.0</TargetFramework>
4+
<TargetFramework>net8.0</TargetFramework>
5+
<ImplicitUsings>enable</ImplicitUsings>
6+
<Nullable>enable</Nullable>
57
</PropertyGroup>
68

9+
<ItemGroup>
10+
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.0" />
11+
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.0" />
12+
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.0">
13+
<PrivateAssets>all</PrivateAssets>
14+
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
15+
</PackageReference>
16+
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="8.0.0" />
17+
</ItemGroup>
18+
19+
<ItemGroup>
20+
<EmbeddedResource Include="Startup\Internal\BlogsContentSimple.xml" />
21+
<EmbeddedResource Include="Startup\Internal\BlogsContextMedium.xml" />
22+
</ItemGroup>
23+
724
</Project>

0 commit comments

Comments
 (0)