Skip to content

Commit 8599b04

Browse files
axelmarquezhJBrenesSimpatstephenfuqua
authored
[ODS-6215] Add EF Core DbContexts and supporting code to EdFi.Admin.DataAccess (5.x) (#1079)
Co-authored-by: Johnny Brenes <[email protected]> Co-authored-by: Stephen Fuqua <[email protected]>
1 parent 2bfdef2 commit 8599b04

File tree

52 files changed

+2089
-1019
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

52 files changed

+2089
-1019
lines changed

.github/workflows/Lib edFi.admin.dataaccess pullrequest.yml

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,6 @@ jobs:
2121

2222
steps:
2323
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
24-
- name: Setup .NET
25-
uses: actions/setup-dotnet@607fce577a46308457984d59e4954e075820f10a # v3.0.3
26-
with:
27-
dotnet-version: 3.1.x
2824
- name: build
2925
run: |
3026
.\build.githubactions.ps1 build -Configuration ${{ env.CONFIGURATION }} -InformationalVersion ${{ env.INFORMATIONAL_VERSION}} -BuildCounter ${{ github.run_number }} -BuildIncrementer ${{env.BUILD_INCREMENTER}} -Solution "Application/EdFi.Admin.DataAccess/EdFi.Admin.DataAccess.sln" -ProjectFile "Application/EdFi.Admin.DataAccess/EdFi.Admin.DataAccess.csproj"

.github/workflows/Lib edFi.admin.dataaccess.yml

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,6 @@ jobs:
1818

1919
steps:
2020
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
21-
- name: Setup .NET
22-
uses: actions/setup-dotnet@607fce577a46308457984d59e4954e075820f10a # v3.0.3
23-
with:
24-
dotnet-version: 3.1.x
2521
- name: build
2622
run: |
2723
.\build.githubactions.ps1 build -Configuration ${{ env.CONFIGURATION }} -InformationalVersion ${{ env.INFORMATIONAL_VERSION}} -BuildCounter ${{ github.run_number }} -BuildIncrementer ${{env.BUILD_INCREMENTER}} -Solution "Application/EdFi.Admin.DataAccess/EdFi.Admin.DataAccess.sln" -ProjectFile "Application/EdFi.Admin.DataAccess/EdFi.Admin.DataAccess.csproj"

Application/EdFi.Admin.DataAccess/Contexts/IUsersContext.cs

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,39 +5,43 @@
55

66
using System;
77
using System.Collections.Generic;
8-
using System.Data.Entity;
8+
using System.Threading;
99
using System.Threading.Tasks;
1010
using EdFi.Admin.DataAccess.Models;
11+
using Microsoft.EntityFrameworkCore;
1112

1213
namespace EdFi.Admin.DataAccess.Contexts
1314
{
1415
public interface IUsersContext : IDisposable
1516
{
16-
IDbSet<User> Users { get; set; }
17+
DbSet<User> Users { get; set; }
1718

18-
IDbSet<ApiClient> Clients { get; set; }
19+
DbSet<ApiClient> Clients { get; set; }
1920

20-
IDbSet<ClientAccessToken> ClientAccessTokens { get; set; }
21+
DbSet<ClientAccessToken> ClientAccessTokens { get; set; }
2122

22-
IDbSet<Vendor> Vendors { get; set; }
23+
DbSet<Vendor> Vendors { get; set; }
2324

24-
IDbSet<Application> Applications { get; set; }
25+
DbSet<Application> Applications { get; set; }
2526

26-
IDbSet<Profile> Profiles { get; set; }
27+
DbSet<Profile> Profiles { get; set; }
2728

28-
IDbSet<OdsInstance> OdsInstances { get; set; }
29+
DbSet<OdsInstance> OdsInstances { get; set; }
2930

30-
IDbSet<OdsInstanceComponent> OdsInstanceComponents { get; set; }
31+
DbSet<OdsInstanceComponent> OdsInstanceComponents { get; set; }
3132

32-
IDbSet<ApplicationEducationOrganization> ApplicationEducationOrganizations { get; set; }
33+
DbSet<ApplicationEducationOrganization> ApplicationEducationOrganizations { get; set; }
3334

34-
IDbSet<VendorNamespacePrefix> VendorNamespacePrefixes { get; set; }
35+
DbSet<VendorNamespacePrefix> VendorNamespacePrefixes { get; set; }
3536

36-
IDbSet<OwnershipToken> OwnershipToken { get; set; }
37+
DbSet<OwnershipToken> OwnershipTokens { get; set; }
38+
39+
DbSet<ApiClientOwnershipToken> ApiClientOwnershipTokens { get; set; }
40+
3741

3842
int SaveChanges();
3943

40-
Task<int> SaveChangesAsync();
44+
Task<int> SaveChangesAsync(CancellationToken cancellationToken);
4145

4246
/// <summary>
4347
/// Asynchronously executes a raw SQL statement with only a scalar result (e.g. row count).

Application/EdFi.Admin.DataAccess/Contexts/PostgresUsersContext.cs

Lines changed: 15 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -3,68 +3,32 @@
33
// The Ed-Fi Alliance licenses this file to you under the Apache License, Version 2.0.
44
// See the LICENSE and NOTICES files in the project root for more information.
55

6-
using System.Data.Entity;
7-
using System.Data.Entity.Core.Metadata.Edm;
8-
using System.Data.Entity.Infrastructure;
9-
using System.Data.Entity.ModelConfiguration.Conventions;
6+
using EdFi.Admin.DataAccess.Extensions;
107
using EdFi.Admin.DataAccess.Models;
11-
using EdFi.Common;
128
using EdFi.Common.Utils.Extensions;
9+
using Microsoft.EntityFrameworkCore;
10+
using System.Linq;
1311

1412
namespace EdFi.Admin.DataAccess.Contexts
1513
{
1614
public class PostgresUsersContext : UsersContext
1715
{
18-
public PostgresUsersContext(string connectionString) : base(connectionString) { }
16+
public PostgresUsersContext(DbContextOptions options)
17+
: base(options) { }
1918

20-
protected override void ApplyProviderSpecificMappings(DbModelBuilder modelBuilder)
19+
protected override void OnModelCreating(ModelBuilder modelBuilder)
2120
{
22-
// The column name in this linking table had to be shortened for Postgres
23-
modelBuilder.Entity<ApiClient>()
24-
.HasMany(t => t.ApplicationEducationOrganizations)
25-
.WithMany(t => t.Clients)
26-
.Map(
27-
m =>
28-
{
29-
m.ToTable("ApiClientApplicationEducationOrganizations", "dbo");
30-
m.MapLeftKey("ApiClient_ApiClientId");
31-
m.MapRightKey("ApplicationEdOrg_ApplicationEdOrgId");
32-
});
21+
base.OnModelCreating(modelBuilder);
3322

34-
modelBuilder.Conventions.Add<ForeignKeyLowerCaseNamingConvention>();
35-
modelBuilder.Conventions.Add<TableLowerCaseNamingConvention>();
23+
modelBuilder.Model.GetEntityTypes().ForEach(
24+
entityType =>
25+
entityType.SetSchema("dbo"));
3626

37-
modelBuilder.Properties().Configure(c => c.HasColumnName(c.ClrPropertyInfo.Name.ToLowerInvariant()));
38-
}
39-
40-
private class TableLowerCaseNamingConvention : IStoreModelConvention<EntitySet>
41-
{
42-
public void Apply(EntitySet entitySet, DbModel model)
43-
{
44-
Preconditions.ThrowIfNull(entitySet, nameof(entitySet));
45-
Preconditions.ThrowIfNull(model, nameof(model));
46-
47-
entitySet.Table = entitySet.Table.ToLowerInvariant();
48-
}
49-
}
50-
51-
private class ForeignKeyLowerCaseNamingConvention : IStoreModelConvention<AssociationType>
52-
{
53-
public void Apply(AssociationType association, DbModel model)
54-
{
55-
Preconditions.ThrowIfNull(association, nameof(association));
56-
Preconditions.ThrowIfNull(model, nameof(model));
57-
58-
if (!association.IsForeignKey)
59-
{
60-
return;
61-
}
62-
63-
association.Constraint.FromProperties.ForEach(PropertyNamesToLowerInvariant);
64-
association.Constraint.ToProperties.ForEach(PropertyNamesToLowerInvariant);
27+
modelBuilder.Model.GetEntityTypes().Single(e => e.ClrType.Name == nameof(ApiClientApplicationEducationOrganization))
28+
.GetProperty("ApplicationEducationOrganizationId")
29+
.SetColumnName("applicationedorg_applicationedorgid");
6530

66-
void PropertyNamesToLowerInvariant(EdmProperty property) => property.Name = property.Name.ToLowerInvariant();
67-
}
31+
modelBuilder.MakeDbObjectNamesLowercase();
6832
}
6933
}
70-
}
34+
}

Application/EdFi.Admin.DataAccess/Contexts/SqlServerUsersContext.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,12 @@
33
// The Ed-Fi Alliance licenses this file to you under the Apache License, Version 2.0.
44
// See the LICENSE and NOTICES files in the project root for more information.
55

6+
using Microsoft.EntityFrameworkCore;
7+
68
namespace EdFi.Admin.DataAccess.Contexts
79
{
810
public class SqlServerUsersContext : UsersContext
911
{
10-
public SqlServerUsersContext(string connectionString) : base(connectionString) { }
12+
public SqlServerUsersContext(DbContextOptions options) : base(options) { }
1113
}
1214
}

Application/EdFi.Admin.DataAccess/Contexts/UsersContext.cs

Lines changed: 45 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -5,23 +5,22 @@
55

66
using System;
77
using System.Collections.Generic;
8-
using System.Data.Entity;
8+
using System.Linq;
99
using System.Linq.Expressions;
10+
using System.Runtime.CompilerServices;
1011
using System.Threading.Tasks;
12+
using EdFi.Admin.DataAccess.Extensions;
1113
using EdFi.Admin.DataAccess.Models;
12-
using EdFi.Admin.DataAccess.Utils;
14+
using Microsoft.EntityFrameworkCore;
1315

1416
namespace EdFi.Admin.DataAccess.Contexts
1517
{
1618
public abstract class UsersContext : DbContext, IUsersContext
1719
{
1820

19-
protected UsersContext(string connectionString)
20-
: base(connectionString)
21-
{
22-
Database.SetInitializer(new ValidateDatabase<SqlServerUsersContext>());
23-
Database.SetInitializer(new ValidateDatabase<PostgresUsersContext>());
24-
}
21+
protected UsersContext(DbContextOptions options)
22+
: base(options) { }
23+
2524
public const string UserTableName = "Users";
2625

2726
public static string UserNameColumn
@@ -34,55 +33,70 @@ public static string UserIdColumn
3433
get { return UserMemberName(x => x.UserId); }
3534
}
3635

37-
public IDbSet<User> Users { get; set; }
36+
public DbSet<User> Users { get; set; }
3837

39-
public IDbSet<ApiClient> Clients { get; set; }
38+
public DbSet<ApiClient> Clients { get; set; }
4039

41-
public IDbSet<ClientAccessToken> ClientAccessTokens { get; set; }
40+
public DbSet<ClientAccessToken> ClientAccessTokens { get; set; }
4241

43-
public IDbSet<Vendor> Vendors { get; set; }
42+
public DbSet<Vendor> Vendors { get; set; }
4443

45-
public IDbSet<Application> Applications { get; set; }
44+
public DbSet<Application> Applications { get; set; }
4645

47-
public IDbSet<Profile> Profiles { get; set; }
46+
public DbSet<Profile> Profiles { get; set; }
4847

49-
public IDbSet<OdsInstance> OdsInstances { get; set; }
48+
public DbSet<OdsInstance> OdsInstances { get; set; }
5049

51-
public IDbSet<OdsInstanceComponent> OdsInstanceComponents { get; set; }
50+
public DbSet<OdsInstanceComponent> OdsInstanceComponents { get; set; }
5251

5352
//TODO: This should really be removed from being directly on the context. Application should own
5453
//TODO: these instances, and deleting an application should delete the associated LEA's
55-
public IDbSet<ApplicationEducationOrganization> ApplicationEducationOrganizations { get; set; }
54+
public DbSet<ApplicationEducationOrganization> ApplicationEducationOrganizations { get; set; }
5655

57-
public IDbSet<VendorNamespacePrefix> VendorNamespacePrefixes { get; set; }
56+
public DbSet<VendorNamespacePrefix> VendorNamespacePrefixes { get; set; }
5857

59-
public IDbSet<OwnershipToken> OwnershipToken { get; set; }
58+
public DbSet<OwnershipToken> OwnershipTokens { get; set; }
6059

61-
public IDbSet<WebPagesUsersInRoles> UsersInRoles { get; set; }
60+
public DbSet<ApiClientOwnershipToken> ApiClientOwnershipTokens { get; set; }
6261

63-
protected override void OnModelCreating(DbModelBuilder modelBuilder)
62+
public DbSet<WebPagesUsersInRoles> UsersInRoles { get; set; }
63+
64+
protected override void OnModelCreating(ModelBuilder modelBuilder)
6465
{
65-
ApplyProviderSpecificMappings(modelBuilder);
66+
modelBuilder.Entity<ApiClient>()
67+
.HasMany(t => t.ApplicationEducationOrganizations)
68+
.WithMany(t => t.Clients)
69+
.UsingEntity<ApiClientApplicationEducationOrganization>(
70+
"ApiClientApplicationEducationOrganizations",
71+
l =>
72+
l.HasOne<ApplicationEducationOrganization>().WithMany().HasForeignKey(
73+
"ApplicationEducationOrganizationId"),
74+
r =>
75+
r.HasOne<ApiClient>().WithMany().HasForeignKey("ApiClientId"));
76+
77+
modelBuilder.Entity<Application>()
78+
.HasMany(a => a.Profiles)
79+
.WithMany(a => a.Applications)
80+
.UsingEntity("ProfileApplications");
81+
82+
modelBuilder.UseUnderscoredFkColumnNames();
83+
84+
modelBuilder.Model.FindEntityTypes(typeof(ApiClient)).First().GetProperty("CreatorOwnershipTokenId")
85+
.SetColumnName("CreatorOwnershipTokenId_OwnershipTokenId");
6686
}
6787

68-
/// <remarks>
69-
/// Sub-classes should override this to provide database system-specific column and/or
70-
/// table mappings: for example, if a linking table column in Postgres needs to map to a
71-
/// name other than the default provided by Entity Framework.
72-
/// </remarks>
73-
protected virtual void ApplyProviderSpecificMappings(DbModelBuilder modelBuilder) { }
74-
7588
/// <inheritdoc />
7689
public Task<int> ExecuteSqlCommandAsync(string sqlStatement, params object[] parameters)
7790
{
78-
return Database.ExecuteSqlCommandAsync(sqlStatement.ToLowerInvariant(), parameters);
91+
return Database.ExecuteSqlInterpolatedAsync(
92+
FormattableStringFactory.Create(sqlStatement.ToLowerInvariant(), parameters));
7993
}
8094

8195
/// <inheritdoc />
8296
public async Task<IReadOnlyList<TReturn>> ExecuteQueryAsync<TReturn>(string sqlStatement, params object[] parameters)
8397
{
8498
return await Database
85-
.SqlQuery<TReturn>(sqlStatement.ToLowerInvariant(), parameters)
99+
.SqlQueryRaw<TReturn>(sqlStatement.ToLowerInvariant(), parameters)
86100
.ToListAsync();
87101
}
88102

Application/EdFi.Admin.DataAccess/Contexts/UsersContextFactory.cs

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,13 @@
88
using EdFi.Admin.DataAccess.Providers;
99
using EdFi.Common;
1010
using EdFi.Common.Configuration;
11+
using Microsoft.EntityFrameworkCore;
1112

1213
namespace EdFi.Admin.DataAccess.Contexts
1314
{
1415
public class UsersContextFactory : IUsersContextFactory
1516
{
16-
private readonly Dictionary<DatabaseEngine, Type> _usersContextTypeByDatabaseEngine = new Dictionary<DatabaseEngine, Type>
17+
private readonly Dictionary<DatabaseEngine, Type> _usersContextTypeByDatabaseEngine = new()
1718
{
1819
{DatabaseEngine.SqlServer, typeof(SqlServerUsersContext)},
1920
{DatabaseEngine.Postgres, typeof(PostgresUsersContext)}
@@ -28,12 +29,39 @@ public UsersContextFactory(IAdminDatabaseConnectionStringProvider connectionStri
2829
_connectionStringsProvider = Preconditions.ThrowIfNull(connectionStringsProvider, nameof(connectionStringsProvider));
2930
_databaseEngine = Preconditions.ThrowIfNull(databaseEngine, nameof(databaseEngine));
3031
}
32+
public Type GetUsersContextType()
33+
{
34+
if (_usersContextTypeByDatabaseEngine.TryGetValue(_databaseEngine, out Type contextType))
35+
{
36+
return contextType;
37+
}
38+
39+
throw new InvalidOperationException(
40+
$"No UsersContext defined for database type {_databaseEngine.DisplayName}");
41+
}
3142

3243
public IUsersContext CreateContext()
3344
{
34-
if (_usersContextTypeByDatabaseEngine.TryGetValue(_databaseEngine, out Type contextType))
45+
if (_databaseEngine == DatabaseEngine.SqlServer)
46+
{
47+
return Activator.CreateInstance(
48+
GetUsersContextType(),
49+
new DbContextOptionsBuilder<SqlServerUsersContext>()
50+
.UseLazyLoadingProxies()
51+
.UseSqlServer(_connectionStringsProvider.GetConnectionString())
52+
.Options) as
53+
IUsersContext;
54+
}
55+
56+
if (_databaseEngine == DatabaseEngine.Postgres)
3557
{
36-
return Activator.CreateInstance(contextType, _connectionStringsProvider.GetConnectionString()) as IUsersContext;
58+
return Activator.CreateInstance(
59+
GetUsersContextType(),
60+
new DbContextOptionsBuilder<PostgresUsersContext>()
61+
.UseLazyLoadingProxies()
62+
.UseNpgsql(_connectionStringsProvider.GetConnectionString())
63+
.Options) as
64+
IUsersContext;
3765
}
3866

3967
throw new InvalidOperationException(

Application/EdFi.Admin.DataAccess/EdFi.Admin.DataAccess.csproj

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22
<PropertyGroup>
33
<PackageId>EdFi.Admin.DataAccess</PackageId>
4-
<TargetFramework>net6.0</TargetFramework>
4+
<TargetFramework>net8.0</TargetFramework>
55
<PackageLicenseFile>LICENSE.txt</PackageLicenseFile>
66
<AssemblyName>EdFi.Admin.DataAccess</AssemblyName>
77
<RootNamespace>EdFi.Admin.DataAccess</RootNamespace>
@@ -17,12 +17,15 @@
1717
</PropertyGroup>
1818
<ItemGroup>
1919
<PackageReference Include="EdFi.Suite3.Common" Version="5.4.447" />
20-
<PackageReference Include="EntityFramework" Version="6.4.4" />
21-
<PackageReference Include="EntityFramework6.Npgsql" Version="6.4.3" />
22-
<PackageReference Include="log4net" Version="2.0.13" />
23-
<PackageReference Include="Npgsql" Version="6.0.11" />
20+
<PackageReference Include="log4net" Version="2.0.17" />
21+
<PackageReference Include="Microsoft.Data.SqlClient" Version="5.2.1" />
22+
<PackageReference Include="Microsoft.EntityFrameworkCore.Abstractions" Version="8.0.6" />
23+
<PackageReference Include="Microsoft.EntityFrameworkCore.Proxies" Version="8.0.6" />
24+
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="8.0.6" />
25+
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.6" />
26+
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.4" />
2427
</ItemGroup>
2528
<ItemGroup>
2629
<None Include="../../LICENSE.txt" Pack="true" PackagePath="LICENSE.txt" />
2730
</ItemGroup>
28-
</Project>
31+
</Project>

0 commit comments

Comments
 (0)