Skip to content

Commit

Permalink
.NET Entity Framework Core 3.1 support (attempt #2) (#3356)
Browse files Browse the repository at this point in the history
* packages updated, fix compiler errors/warnings

* scaffold

* fix ShardDatabaseOfflineTools

* WorldDatabase fix performance

* ShardDatabase fix GetCharacters

* move comment

stuff

* extend UseMySQL statements

* fix character creation linq parsing

* fix exception

* fix references
  • Loading branch information
Mag-nus authored Dec 8, 2020
1 parent 216646d commit 43c9300
Show file tree
Hide file tree
Showing 16 changed files with 1,403 additions and 1,015 deletions.
8 changes: 2 additions & 6 deletions Source/ACE.Database/ACE.Database.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,8 @@

<ItemGroup>
<PackageReference Include="log4net" Version="2.0.12" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="2.2.6" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="2.2.6">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="2.2.6" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="3.1.10" />
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="3.2.4" />
<PackageReference Include="System.Diagnostics.Debug" Version="4.3.0" />
<PackageReference Include="System.IO.FileSystem" Version="4.3.0" />
<PackageReference Include="System.Net.NameResolution" Version="4.3.0" />
Expand Down
30 changes: 12 additions & 18 deletions Source/ACE.Database/Extensions/QueryableExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,34 +1,28 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

using Microsoft.EntityFrameworkCore.Query;
using Microsoft.EntityFrameworkCore.Query.Internal;
using Microsoft.EntityFrameworkCore.Storage;
using Microsoft.EntityFrameworkCore.Query.SqlExpressions;


namespace ACE.Database.Extensions
{
public static class QueryableExtensions
{
private static readonly TypeInfo QueryCompilerTypeInfo = typeof(QueryCompiler).GetTypeInfo();

private static readonly FieldInfo QueryCompilerField = typeof(EntityQueryProvider).GetTypeInfo().DeclaredFields.First(x => x.Name == "_queryCompiler");
private static readonly FieldInfo QueryModelGeneratorField = typeof(QueryCompiler).GetTypeInfo().DeclaredFields.First(x => x.Name == "_queryModelGenerator");
private static readonly FieldInfo DataBaseField = QueryCompilerTypeInfo.DeclaredFields.Single(x => x.Name == "_database");
private static readonly PropertyInfo DatabaseDependenciesField = typeof(Microsoft.EntityFrameworkCore.Storage.Database).GetTypeInfo().DeclaredProperties.Single(x => x.Name == "Dependencies");

/// <exception cref="InvalidOperationException"></exception>
public static string ToSql<TEntity>(this IQueryable<TEntity> query)
{
var queryCompiler = (QueryCompiler)QueryCompilerField.GetValue(query.Provider);
var queryModelGenerator = (QueryModelGenerator)QueryModelGeneratorField.GetValue(queryCompiler);
var queryModel = queryModelGenerator.ParseQuery(query.Expression);
var database = DataBaseField.GetValue(queryCompiler);
var databaseDependencies = (DatabaseDependencies)DatabaseDependenciesField.GetValue(database);
var queryCompilationContext = databaseDependencies.QueryCompilationContextFactory.Create(false);
var modelVisitor = (RelationalQueryModelVisitor)queryCompilationContext.CreateQueryModelVisitor();
modelVisitor.CreateQueryExecutor<TEntity>(queryModel);
var sql = modelVisitor.Queries.First().ToString();
var enumerator = query.Provider.Execute<IEnumerable<TEntity>>(query.Expression).GetEnumerator();
var enumeratorType = enumerator.GetType();
var selectFieldInfo = enumeratorType.GetField("_selectExpression", BindingFlags.NonPublic | BindingFlags.Instance) ?? throw new InvalidOperationException($"cannot find field _selectExpression on type {enumeratorType.Name}");
var sqlGeneratorFieldInfo = enumeratorType.GetField("_querySqlGeneratorFactory", BindingFlags.NonPublic | BindingFlags.Instance) ?? throw new InvalidOperationException($"cannot find field _querySqlGeneratorFactory on type {enumeratorType.Name}");
var selectExpression = selectFieldInfo.GetValue(enumerator) as SelectExpression ?? throw new InvalidOperationException($"could not get SelectExpression");
var factory = sqlGeneratorFieldInfo.GetValue(enumerator) as IQuerySqlGeneratorFactory ?? throw new InvalidOperationException($"could not get IQuerySqlGeneratorFactory");
var sqlGenerator = factory.Create();
var command = sqlGenerator.GetCommand(selectExpression);
var sql = command.CommandText;

return sql;
}
Expand Down
2 changes: 1 addition & 1 deletion Source/ACE.Database/Models/Auth/Accesslevel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,6 @@ public Accesslevel()
public string Name { get; set; }
public string Prefix { get; set; }

public ICollection<Account> Account { get; set; }
public virtual ICollection<Account> Account { get; set; }
}
}
4 changes: 2 additions & 2 deletions Source/ACE.Database/Models/Auth/Account.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;

namespace ACE.Database.Models.Auth
Expand All @@ -21,6 +21,6 @@ public partial class Account
public DateTime? BanExpireTime { get; set; }
public string BanReason { get; set; }

public Accesslevel AccessLevelNavigation { get; set; }
public virtual Accesslevel AccessLevelNavigation { get; set; }
}
}
51 changes: 34 additions & 17 deletions Source/ACE.Database/Models/Auth/AuthDbContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
var config = Common.ConfigManager.Config.MySql.Authentication;

optionsBuilder.UseMySql($"server={config.Host};port={config.Port};user={config.Username};password={config.Password};database={config.Database}", builder =>
optionsBuilder.UseMySql($"server={config.Host};port={config.Port};user={config.Username};password={config.Password};database={config.Database};TreatTinyAsBoolean=False", builder =>
{
builder.EnableRetryOnFailure(10);
});
Expand All @@ -39,7 +39,8 @@ protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Accesslevel>(entity =>
{
entity.HasKey(e => e.Level);
entity.HasKey(e => e.Level)
.HasName("PRIMARY");
entity.ToTable("accesslevel");
Expand All @@ -49,17 +50,21 @@ protected override void OnModelCreating(ModelBuilder modelBuilder)
entity.Property(e => e.Level)
.HasColumnName("level")
.HasDefaultValueSql("'0'");
.ValueGeneratedNever();
entity.Property(e => e.Name)
.IsRequired()
.HasColumnName("name")
.HasColumnType("varchar(45)");
.HasColumnType("varchar(45)")
.HasCharSet("utf8")
.HasCollation("utf8_general_ci");
entity.Property(e => e.Prefix)
.HasColumnName("prefix")
.HasColumnType("varchar(45)")
.HasDefaultValueSql("''");
.HasDefaultValueSql("''")
.HasCharSet("utf8")
.HasCollation("utf8_general_ci");
});

modelBuilder.Entity<Account>(entity =>
Expand All @@ -75,22 +80,24 @@ protected override void OnModelCreating(ModelBuilder modelBuilder)
entity.Property(e => e.AccountId).HasColumnName("accountId");
entity.Property(e => e.AccessLevel)
.HasColumnName("accessLevel")
.HasDefaultValueSql("'0'");
entity.Property(e => e.AccessLevel).HasColumnName("accessLevel");
entity.Property(e => e.AccountName)
.IsRequired()
.HasColumnName("accountName")
.HasColumnType("varchar(50)");
.HasColumnType("varchar(50)")
.HasCharSet("utf8")
.HasCollation("utf8_general_ci");
entity.Property(e => e.BanExpireTime)
.HasColumnName("ban_Expire_Time")
.HasColumnType("datetime");
entity.Property(e => e.BanReason)
.HasColumnName("ban_Reason")
.HasColumnType("varchar(1000)");
.HasColumnType("varchar(1000)")
.HasCharSet("utf8")
.HasCollation("utf8_general_ci");
entity.Property(e => e.BannedByAccountId).HasColumnName("banned_By_Account_Id");
Expand All @@ -105,11 +112,13 @@ protected override void OnModelCreating(ModelBuilder modelBuilder)
entity.Property(e => e.CreateTime)
.HasColumnName("create_Time")
.HasColumnType("datetime")
.HasDefaultValueSql("'CURRENT_TIMESTAMP'");
.HasDefaultValueSql("CURRENT_TIMESTAMP");
entity.Property(e => e.EmailAddress)
.HasColumnName("email_Address")
.HasColumnType("varchar(320)");
.HasColumnType("varchar(320)")
.HasCharSet("utf8")
.HasCollation("utf8_general_ci");
entity.Property(e => e.LastLoginIP)
.HasColumnName("last_Login_I_P")
Expand All @@ -122,24 +131,32 @@ protected override void OnModelCreating(ModelBuilder modelBuilder)
entity.Property(e => e.PasswordHash)
.IsRequired()
.HasColumnName("passwordHash")
.HasColumnType("varchar(88)");
.HasColumnType("varchar(88)")
.HasComment("base64 encoded version of the hashed passwords. 88 characters are needed to base64 encode SHA512 output.")
.HasCharSet("utf8")
.HasCollation("utf8_general_ci");
entity.Property(e => e.PasswordSalt)
.IsRequired()
.HasColumnName("passwordSalt")
.HasColumnType("varchar(88)")
.HasDefaultValueSql("'use bcrypt'");
.HasDefaultValueSql("'use bcrypt'")
.HasComment("This is no longer used, except to indicate if bcrypt is being employed for migration purposes. Previously: base64 encoded version of the password salt. 512 byte salts (88 characters when base64 encoded) are recommend for SHA512.")
.HasCharSet("utf8")
.HasCollation("utf8_general_ci");
entity.Property(e => e.TotalTimesLoggedIn)
.HasColumnName("total_Times_Logged_In")
.HasDefaultValueSql("'0'");
entity.Property(e => e.TotalTimesLoggedIn).HasColumnName("total_Times_Logged_In");
entity.HasOne(d => d.AccessLevelNavigation)
.WithMany(p => p.Account)
.HasForeignKey(d => d.AccessLevel)
.OnDelete(DeleteBehavior.ClientSetNull)
.HasConstraintName("fk_accesslevel");
});

OnModelCreatingPartial(modelBuilder);
}

partial void OnModelCreatingPartial(ModelBuilder modelBuilder);
}
}
Loading

0 comments on commit 43c9300

Please sign in to comment.