init
This commit is contained in:
@@ -0,0 +1,28 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<RootNamespace>Avalonia_EFCore</RootNamespace>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="10.0.7" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="10.0.7" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="10.0.7" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="10.0.7" />
|
||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="10.0.0" />
|
||||
<PackageReference Include="MySql.EntityFrameworkCore" Version="10.0.7" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="10.0.7">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="10.0.7" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Avalonia-Common\Avalonia-Common.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,50 @@
|
||||
using Avalonia_EFCore.Models;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Avalonia_EFCore.Database
|
||||
{
|
||||
/// <summary>
|
||||
/// 应用数据库上下文 —— 继承自 Avalonia-EFCore 的 AppDbContext。
|
||||
/// 所有业务实体在此注册 DbSet。
|
||||
/// 这是 Avalonia-API 和 Avalonia-PC 共用的具体数据上下文。
|
||||
/// </summary>
|
||||
public class AppDataContext(DatabaseConfiguration dbConfig) : AppDbContext(dbConfig)
|
||||
{
|
||||
/// <summary>天气预报数据</summary>
|
||||
public DbSet<WeatherForecastEntity> WeatherForecasts => Set<WeatherForecastEntity>();
|
||||
|
||||
/// <summary>用户数据</summary>
|
||||
public DbSet<UserEntity> Users => Set<UserEntity>();
|
||||
|
||||
/// <summary>API refresh token 数据</summary>
|
||||
public DbSet<ApiRefreshTokenEntity> ApiRefreshTokens => Set<ApiRefreshTokenEntity>();
|
||||
|
||||
/// <summary>
|
||||
/// 配置实体映射,包括主键、索引和属性约束。
|
||||
/// </summary>
|
||||
/// <param name="modelBuilder">模型构建器。</param>
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||
{
|
||||
base.OnModelCreating(modelBuilder);
|
||||
|
||||
modelBuilder.Entity<WeatherForecastEntity>(entity =>
|
||||
{
|
||||
entity.HasKey(e => e.Id).HasName("pk-weather-forecast");
|
||||
entity.Property(e => e.Summary).HasMaxLength(200);
|
||||
});
|
||||
|
||||
modelBuilder.Entity<UserEntity>(entity =>
|
||||
{
|
||||
entity.HasKey(e => e.Id).HasName("pk-user");
|
||||
entity.Property(e => e.Email).HasMaxLength(200);
|
||||
});
|
||||
|
||||
modelBuilder.Entity<ApiRefreshTokenEntity>(entity =>
|
||||
{
|
||||
entity.HasKey(e => e.Id).HasName("pk-api-refresh-token");
|
||||
entity.HasIndex(e => e.TokenHash).IsUnique().HasDatabaseName("idx-api-refresh-token-hash");
|
||||
entity.HasIndex(e => e.UserId).HasDatabaseName("idx-api-refresh-token-user-id");
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
using Microsoft.EntityFrameworkCore.Design;
|
||||
|
||||
namespace Avalonia_EFCore.Database
|
||||
{
|
||||
/// <summary>
|
||||
/// 设计时 DbContext 工厂,用于 EF Core 迁移工具生成迁移代码。
|
||||
/// </summary>
|
||||
public class AppDataContextFactory : IDesignTimeDbContextFactory<AppDataContext>
|
||||
{
|
||||
/// <summary>
|
||||
/// 创建用于设计时的 AppDataContext 实例,默认使用 SQLite 提供程序。
|
||||
/// </summary>
|
||||
/// <param name="args">命令行参数。</param>
|
||||
/// <returns>配置好的数据上下文实例。</returns>
|
||||
public AppDataContext CreateDbContext(string[] args)
|
||||
{
|
||||
return new AppDataContext(DesignTimeDatabaseConfiguration.Create(args));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// SQLite 迁移设计时工厂。
|
||||
/// </summary>
|
||||
public sealed class SqliteAppDataContextFactory : IDesignTimeDbContextFactory<SqliteAppDataContext>
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public SqliteAppDataContext CreateDbContext(string[] args)
|
||||
=> new(DesignTimeDatabaseConfiguration.Create(args, DatabaseProvider.SQLite));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// SQL Server 迁移设计时工厂。
|
||||
/// </summary>
|
||||
public sealed class SqlServerAppDataContextFactory : IDesignTimeDbContextFactory<SqlServerAppDataContext>
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public SqlServerAppDataContext CreateDbContext(string[] args)
|
||||
=> new(DesignTimeDatabaseConfiguration.Create(args, DatabaseProvider.SqlServer));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// PostgreSQL 迁移设计时工厂。
|
||||
/// </summary>
|
||||
public sealed class PostgreSqlAppDataContextFactory : IDesignTimeDbContextFactory<PostgreSqlAppDataContext>
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public PostgreSqlAppDataContext CreateDbContext(string[] args)
|
||||
=> new(DesignTimeDatabaseConfiguration.Create(args, DatabaseProvider.PostgreSQL));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// MySQL 迁移设计时工厂。
|
||||
/// </summary>
|
||||
public sealed class MySqlAppDataContextFactory : IDesignTimeDbContextFactory<MySqlAppDataContext>
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public MySqlAppDataContext CreateDbContext(string[] args)
|
||||
=> new(DesignTimeDatabaseConfiguration.Create(args, DatabaseProvider.MySQL));
|
||||
}
|
||||
|
||||
internal static class DesignTimeDatabaseConfiguration
|
||||
{
|
||||
public static DatabaseConfiguration Create(string[] args, DatabaseProvider defaultProvider = DatabaseProvider.SQLite)
|
||||
{
|
||||
DatabaseProviderRegistry.RegisterDefaults();
|
||||
|
||||
var provider = GetProvider(args) ?? defaultProvider;
|
||||
return provider switch
|
||||
{
|
||||
DatabaseProvider.SQLite => DatabaseConfiguration.ForSQLite("avalonia-api.db"),
|
||||
DatabaseProvider.SqlServer => DatabaseConfiguration.ForSqlServer("(localdb)\\MSSQLLocalDB", "AvaloniaApi"),
|
||||
DatabaseProvider.PostgreSQL => DatabaseConfiguration.ForPostgreSQL("localhost", "avalonia_api", "postgres", "postgres"),
|
||||
DatabaseProvider.MySQL => DatabaseConfiguration.ForMySQL("localhost", "avalonia_api", "root", "root"),
|
||||
_ => DatabaseConfiguration.ForSQLite("avalonia-api.db"),
|
||||
};
|
||||
}
|
||||
|
||||
private static DatabaseProvider? GetProvider(string[] args)
|
||||
{
|
||||
for (var i = 0; i < args.Length; i++)
|
||||
{
|
||||
var arg = args[i];
|
||||
string? value = null;
|
||||
|
||||
if (arg.Equals("--provider", StringComparison.OrdinalIgnoreCase) && i + 1 < args.Length)
|
||||
{
|
||||
value = args[i + 1];
|
||||
}
|
||||
else if (arg.StartsWith("--provider=", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
value = arg["--provider=".Length..];
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(value)
|
||||
&& Enum.TryParse<DatabaseProvider>(value, ignoreCase: true, out var provider))
|
||||
{
|
||||
return provider;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Avalonia_EFCore.Database
|
||||
{
|
||||
/// <summary>
|
||||
/// 应用数据库上下文基类 —— 自动根据 DatabaseConfiguration 选择数据库提供程序。
|
||||
/// 所有业务 DbContext 继承此类即可获得多数据库支持。
|
||||
/// </summary>
|
||||
public abstract class AppDbContext(DatabaseConfiguration dbConfig) : DbContext
|
||||
{
|
||||
/// <summary>
|
||||
/// 数据库配置。
|
||||
/// </summary>
|
||||
private readonly DatabaseConfiguration _dbConfig = dbConfig;
|
||||
|
||||
/// <summary>
|
||||
/// 配置数据库提供程序和连接选项。
|
||||
/// </summary>
|
||||
/// <param name="optionsBuilder">选项构建器。</param>
|
||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||
{
|
||||
if (optionsBuilder.IsConfigured) return;
|
||||
|
||||
ConfigureProvider(optionsBuilder, _dbConfig);
|
||||
|
||||
if (_dbConfig.EnableDetailedLog)
|
||||
{
|
||||
optionsBuilder.LogTo(Console.WriteLine, Microsoft.Extensions.Logging.LogLevel.Information);
|
||||
}
|
||||
|
||||
// 启用详细的 EF Core 错误信息
|
||||
optionsBuilder.EnableDetailedErrors();
|
||||
optionsBuilder.EnableSensitiveDataLogging(_dbConfig.EnableDetailedLog);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据配置选择数据库提供程序。
|
||||
/// 使用注册模式,由宿主项目注册具体的提供程序实现。
|
||||
/// </summary>
|
||||
public static void ConfigureProvider(DbContextOptionsBuilder optionsBuilder, DatabaseConfiguration config)
|
||||
{
|
||||
if (DatabaseProviderRegistry.TryGet(config.Provider, out var configurator))
|
||||
{
|
||||
configurator(optionsBuilder, config.ConnectionString, config.Timeout);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new NotSupportedException(
|
||||
$"数据库提供程序 {config.Provider} 未注册。" +
|
||||
$"请在宿主项目中安装对应的 EF Core NuGet 包并调用 DatabaseProviderRegistry.Register()。");
|
||||
}
|
||||
|
||||
optionsBuilder.EnableDetailedErrors();
|
||||
optionsBuilder.EnableSensitiveDataLogging(config.EnableDetailedLog);
|
||||
|
||||
if (config.EnableDetailedLog)
|
||||
{
|
||||
optionsBuilder.LogTo(Console.WriteLine, Microsoft.Extensions.Logging.LogLevel.Information);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 保存时自动设置时间戳。
|
||||
/// </summary>
|
||||
public override int SaveChanges(bool acceptAllChangesOnSuccess)
|
||||
{
|
||||
SetTimestamps();
|
||||
return base.SaveChanges(acceptAllChangesOnSuccess);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 异步保存更改,自动设置时间戳。
|
||||
/// </summary>
|
||||
/// <param name="acceptAllChangesOnSuccess">是否在成功时接受所有更改。</param>
|
||||
/// <param name="cancellationToken">取消令牌。</param>
|
||||
/// <returns>受影响的行数。</returns>
|
||||
public override Task<int> SaveChangesAsync(bool acceptAllChangesOnSuccess, CancellationToken cancellationToken = default)
|
||||
{
|
||||
SetTimestamps();
|
||||
return base.SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 自动设置新增或修改实体的 CreatedAt 和 UpdatedAt 时间戳。
|
||||
/// </summary>
|
||||
private void SetTimestamps()
|
||||
{
|
||||
var entries = ChangeTracker.Entries()
|
||||
.Where(e => e.State == EntityState.Added || e.State == EntityState.Modified);
|
||||
|
||||
foreach (var entry in entries)
|
||||
{
|
||||
var entity = entry.Entity;
|
||||
|
||||
// 使用反射设置 CreatedAt / UpdatedAt(如果存在)
|
||||
var createdAtProp = entity.GetType().GetProperty("CreatedAt");
|
||||
var updatedAtProp = entity.GetType().GetProperty("UpdatedAt");
|
||||
|
||||
if (entry.State == EntityState.Added && createdAtProp != null)
|
||||
{
|
||||
createdAtProp.SetValue(entity, DateTime.UtcNow);
|
||||
}
|
||||
|
||||
if (updatedAtProp != null)
|
||||
{
|
||||
updatedAtProp.SetValue(entity, DateTime.UtcNow);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
namespace Avalonia_EFCore.Database
|
||||
{
|
||||
/// <summary>
|
||||
/// 支持的数据库提供程序类型。
|
||||
/// </summary>
|
||||
public enum DatabaseProvider
|
||||
{
|
||||
/// <summary>SQLite(本地文件数据库,无需安装,跨平台)</summary>
|
||||
SQLite,
|
||||
|
||||
/// <summary>MySQL / MariaDB</summary>
|
||||
MySQL,
|
||||
|
||||
/// <summary>PostgreSQL</summary>
|
||||
PostgreSQL,
|
||||
|
||||
/// <summary>SQL Server</summary>
|
||||
SqlServer
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 数据库连接配置 —— 在 appsettings.json 中配置。
|
||||
/// </summary>
|
||||
public class DatabaseConfiguration
|
||||
{
|
||||
/// <summary>数据库提供程序</summary>
|
||||
public DatabaseProvider Provider { get; set; } = DatabaseProvider.SQLite;
|
||||
|
||||
/// <summary>连接字符串</summary>
|
||||
public string ConnectionString { get; set; } = "Data Source=app.db";
|
||||
|
||||
/// <summary>是否在启动时自动执行迁移</summary>
|
||||
public bool AutoMigrate { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// 是否在迁移前删除并重建当前连接指向的数据库。
|
||||
/// 仅用于切换数据库类型或本地开发重建库;生产环境默认必须保持 false。
|
||||
/// </summary>
|
||||
public bool RecreateDatabase { get; set; } = false;
|
||||
|
||||
/// <summary>是否启用详细日志(会打印 SQL 语句)</summary>
|
||||
public bool EnableDetailedLog { get; set; } = false;
|
||||
|
||||
/// <summary>连接超时(秒)</summary>
|
||||
public int Timeout { get; set; } = 30;
|
||||
|
||||
// ---- 快捷构建方法 ----
|
||||
|
||||
/// <summary>SQLite 本地数据库</summary>
|
||||
public static DatabaseConfiguration ForSQLite(string dataSource = "app.db")
|
||||
{
|
||||
return new DatabaseConfiguration
|
||||
{
|
||||
Provider = DatabaseProvider.SQLite,
|
||||
ConnectionString = $"Data Source={dataSource}",
|
||||
AutoMigrate = true,
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>MySQL 数据库</summary>
|
||||
public static DatabaseConfiguration ForMySQL(string server, string database, string user, string password, uint port = 3306)
|
||||
{
|
||||
return new DatabaseConfiguration
|
||||
{
|
||||
Provider = DatabaseProvider.MySQL,
|
||||
ConnectionString = $"Server={server};Port={port};Database={database};User={user};Password={password};",
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>PostgreSQL 数据库</summary>
|
||||
public static DatabaseConfiguration ForPostgreSQL(string host, string database, string username, string password, int port = 5432)
|
||||
{
|
||||
return new DatabaseConfiguration
|
||||
{
|
||||
Provider = DatabaseProvider.PostgreSQL,
|
||||
ConnectionString = $"Host={host};Port={port};Database={database};Username={username};Password={password};",
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>SQL Server 数据库</summary>
|
||||
public static DatabaseConfiguration ForSqlServer(string server, string database, string? user = null, string? password = null)
|
||||
{
|
||||
var connStr = string.IsNullOrEmpty(user)
|
||||
? $"Server={server};Database={database};Trusted_Connection=True;TrustServerCertificate=True;"
|
||||
: $"Server={server};Database={database};User Id={user};Password={password};TrustServerCertificate=True;";
|
||||
return new DatabaseConfiguration
|
||||
{
|
||||
Provider = DatabaseProvider.SqlServer,
|
||||
ConnectionString = connStr,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Avalonia_EFCore.Database
|
||||
{
|
||||
/// <summary>
|
||||
/// 数据库服务注册扩展 —— 在 Program.cs 中一行配置数据库。
|
||||
/// </summary>
|
||||
public static class DatabaseExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// 注册数据库上下文及相关服务。
|
||||
/// </summary>
|
||||
/// <typeparam name="TContext">继承自 AppDbContext 的业务 DbContext</typeparam>
|
||||
public static IServiceCollection AddAppDatabase<TContext>(
|
||||
this IServiceCollection services,
|
||||
DatabaseConfiguration config)
|
||||
where TContext : AppDbContext
|
||||
{
|
||||
// 注册配置
|
||||
services.AddSingleton(config);
|
||||
|
||||
if (typeof(TContext) == typeof(AppDataContext))
|
||||
{
|
||||
services.AddProviderAppDataContext(config);
|
||||
services.AddScoped<DatabaseManager<TContext>>();
|
||||
|
||||
return services;
|
||||
}
|
||||
|
||||
// 注册 DbContext
|
||||
services.AddDbContext<TContext>(options =>
|
||||
{
|
||||
AppDbContext.ConfigureProvider(options, config);
|
||||
});
|
||||
|
||||
// 注册数据库管理器
|
||||
services.AddScoped<DatabaseManager<TContext>>();
|
||||
|
||||
return services;
|
||||
}
|
||||
|
||||
private static void AddProviderAppDataContext(this IServiceCollection services, DatabaseConfiguration config)
|
||||
{
|
||||
switch (config.Provider)
|
||||
{
|
||||
case DatabaseProvider.SQLite:
|
||||
services.AddDbContext<AppDataContext, SqliteAppDataContext>(options =>
|
||||
AppDbContext.ConfigureProvider(options, config));
|
||||
break;
|
||||
case DatabaseProvider.SqlServer:
|
||||
services.AddDbContext<AppDataContext, SqlServerAppDataContext>(options =>
|
||||
AppDbContext.ConfigureProvider(options, config));
|
||||
break;
|
||||
case DatabaseProvider.PostgreSQL:
|
||||
services.AddDbContext<AppDataContext, PostgreSqlAppDataContext>(options =>
|
||||
AppDbContext.ConfigureProvider(options, config));
|
||||
break;
|
||||
case DatabaseProvider.MySQL:
|
||||
services.AddDbContext<AppDataContext, MySqlAppDataContext>(options =>
|
||||
AppDbContext.ConfigureProvider(options, config));
|
||||
break;
|
||||
default:
|
||||
throw new NotSupportedException($"数据库提供程序 {config.Provider} 未注册。");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 初始化数据库(在应用启动时调用一次)。
|
||||
/// </summary>
|
||||
public static IServiceProvider InitializeDatabase<TContext>(
|
||||
this IServiceProvider serviceProvider,
|
||||
Action<TContext, IServiceProvider?>? seeder = null)
|
||||
where TContext : AppDbContext
|
||||
{
|
||||
using var scope = serviceProvider.CreateScope();
|
||||
var dbManager = scope.ServiceProvider.GetRequiredService<DatabaseManager<TContext>>();
|
||||
|
||||
// 同步等待初始化(启动时阻塞)
|
||||
dbManager.InitializeAsync(seeder).GetAwaiter().GetResult();
|
||||
|
||||
return serviceProvider;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,224 @@
|
||||
using Avalonia_Common.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Avalonia_EFCore.Database
|
||||
{
|
||||
/// <summary>
|
||||
/// 数据库管理器 —— 负责连接测试、自动迁移、种子数据、版本检查。
|
||||
/// 在应用启动时调用,确保数据库结构与应用代码同步。
|
||||
/// </summary>
|
||||
public class DatabaseManager<TContext> where TContext : AppDbContext
|
||||
{
|
||||
/// <summary>
|
||||
/// 数据库上下文实例。
|
||||
/// </summary>
|
||||
private readonly TContext _context;
|
||||
/// <summary>
|
||||
/// 数据库配置。
|
||||
/// </summary>
|
||||
private readonly DatabaseConfiguration _config;
|
||||
/// <summary>
|
||||
/// DI 服务提供程序(可选,用于种子数据中解析服务)。
|
||||
/// </summary>
|
||||
private readonly IServiceProvider? _serviceProvider;
|
||||
|
||||
/// <summary>
|
||||
/// 初始化数据库管理器。
|
||||
/// </summary>
|
||||
/// <param name="context">数据库上下文。</param>
|
||||
/// <param name="config">数据库配置。</param>
|
||||
/// <param name="serviceProvider">可选的 DI 容器。</param>
|
||||
public DatabaseManager(TContext context, DatabaseConfiguration config, IServiceProvider? serviceProvider = null)
|
||||
{
|
||||
_context = context;
|
||||
_config = config;
|
||||
_serviceProvider = serviceProvider;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 初始化数据库:测试连接 → 自动迁移 → 种子数据。
|
||||
/// </summary>
|
||||
public async Task InitializeAsync(Action<TContext, IServiceProvider?>? seeder = null)
|
||||
{
|
||||
AppLog.Information(
|
||||
"正在初始化数据库 Provider={Provider}, AppVersion={AppVersion}",
|
||||
_config.Provider,
|
||||
GetApplicationVersion());
|
||||
|
||||
// 1. 自动迁移(如果启用)。MigrateAsync 会按迁移历史顺序执行全部待处理迁移,
|
||||
// 支持用户从较旧软件版本直接升级到当前版本。
|
||||
if (_config.AutoMigrate)
|
||||
{
|
||||
if (_config.RecreateDatabase)
|
||||
{
|
||||
AppLog.Warning(
|
||||
"RecreateDatabase=true,将删除并重建当前连接指向的数据库。Provider={Provider}",
|
||||
_config.Provider);
|
||||
|
||||
await _context.Database.EnsureDeletedAsync();
|
||||
}
|
||||
|
||||
await MigrateAsync();
|
||||
}
|
||||
else
|
||||
{
|
||||
var canConnect = await CanConnectAsync();
|
||||
if (!canConnect)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"无法连接到数据库 [{_config.Provider}],请检查连接字符串和数据库服务状态。");
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 种子数据
|
||||
if (seeder != null)
|
||||
{
|
||||
seeder(_context, _serviceProvider);
|
||||
await _context.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 测试数据库连接是否正常。
|
||||
/// </summary>
|
||||
public async Task<bool> CanConnectAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
return await _context.Database.CanConnectAsync();
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 执行待处理的迁移。
|
||||
/// 使用 EF Core 原生迁移机制,自动检测并应用 Schema 变更。
|
||||
/// </summary>
|
||||
public async Task MigrateAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
var appliedMigrations = (await _context.Database.GetAppliedMigrationsAsync()).ToList();
|
||||
var pendingMigrations = await _context.Database.GetPendingMigrationsAsync();
|
||||
|
||||
if (pendingMigrations.Any())
|
||||
{
|
||||
if (appliedMigrations.Count == 0)
|
||||
{
|
||||
AppLog.Information(
|
||||
"未检测到已应用迁移,将按当前 Provider={Provider} 从 0 构建完整表结构",
|
||||
_config.Provider);
|
||||
}
|
||||
|
||||
AppLog.Information(
|
||||
"当前已应用 {AppliedCount} 个迁移,检测到 {PendingCount} 个待执行迁移: {Migrations}",
|
||||
appliedMigrations.Count,
|
||||
pendingMigrations.Count(),
|
||||
string.Join(", ", pendingMigrations));
|
||||
|
||||
await _context.Database.MigrateAsync();
|
||||
|
||||
AppLog.Information("数据库迁移完成({Count} 个迁移已应用)", pendingMigrations.Count());
|
||||
}
|
||||
else
|
||||
{
|
||||
AppLog.Information("数据库已是最新版本,无需迁移");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
AppLog.Error(ex, "数据库迁移失败");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前应用程序的版本号,优先读取 AssemblyInformationalVersion,回退到 AssemblyVersion。
|
||||
/// </summary>
|
||||
/// <returns>应用程序版本字符串。</returns>
|
||||
private static string GetApplicationVersion()
|
||||
{
|
||||
var assembly = Assembly.GetEntryAssembly() ?? typeof(TContext).Assembly;
|
||||
return assembly
|
||||
.GetCustomAttribute<AssemblyInformationalVersionAttribute>()
|
||||
?.InformationalVersion
|
||||
?? assembly.GetName().Version?.ToString()
|
||||
?? "unknown";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取数据库当前版本信息。
|
||||
/// </summary>
|
||||
public async Task<DatabaseVersionInfo> GetVersionInfoAsync()
|
||||
{
|
||||
var appliedMigrations = await _context.Database.GetAppliedMigrationsAsync();
|
||||
var pendingMigrations = await _context.Database.GetPendingMigrationsAsync();
|
||||
|
||||
return new DatabaseVersionInfo
|
||||
{
|
||||
Provider = _config.Provider.ToString(),
|
||||
AppliedMigrations = appliedMigrations.ToList(),
|
||||
PendingMigrations = pendingMigrations.ToList(),
|
||||
IsLatest = !pendingMigrations.Any(),
|
||||
CanConnect = await CanConnectAsync(),
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 生成从指定迁移到最新版本的 SQL 脚本(用于生产环境审计)。
|
||||
/// </summary>
|
||||
public string GenerateMigrationScript(string? fromMigration = null)
|
||||
{
|
||||
var migrator = _context.GetService<IMigrator>();
|
||||
return fromMigration is null
|
||||
? migrator.GenerateScript()
|
||||
: migrator.GenerateScript(fromMigration);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 确保数据库已创建(不执行迁移,适用于简单场景)。
|
||||
/// </summary>
|
||||
public bool EnsureCreated()
|
||||
{
|
||||
return _context.Database.EnsureCreated();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 数据库版本信息 DTO。
|
||||
/// </summary>
|
||||
public class DatabaseVersionInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取或设置数据库提供程序名称。
|
||||
/// </summary>
|
||||
public string Provider { get; set; } = string.Empty;
|
||||
/// <summary>
|
||||
/// 获取或设置已应用的迁移列表。
|
||||
/// </summary>
|
||||
public List<string> AppliedMigrations { get; set; } = new();
|
||||
/// <summary>
|
||||
/// 获取或设置待应用的迁移列表。
|
||||
/// </summary>
|
||||
public List<string> PendingMigrations { get; set; } = new();
|
||||
/// <summary>
|
||||
/// 获取或设置是否为最新版本。
|
||||
/// </summary>
|
||||
public bool IsLatest { get; set; }
|
||||
/// <summary>
|
||||
/// 获取或设置数据库是否可连接。
|
||||
/// </summary>
|
||||
public bool CanConnect { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Avalonia_EFCore.Database
|
||||
{
|
||||
/// <summary>
|
||||
/// 数据库提供程序注册表 —— 统一注册所有支持的提供程序配置委托。
|
||||
/// 具体使用哪个提供程序由各宿主项目决定:
|
||||
/// Avalonia-API:从 appsettings.json 的 DatabaseConfiguration 节读取;
|
||||
/// Avalonia-PC :固定使用 SQLite。
|
||||
/// </summary>
|
||||
public static class DatabaseProviderRegistry
|
||||
{
|
||||
/// <summary>
|
||||
/// 提供程序配置委托:optionsBuilder, connectionString, timeout → void
|
||||
/// </summary>
|
||||
public delegate void ProviderConfigurator(DbContextOptionsBuilder optionsBuilder, string connectionString, int timeout);
|
||||
|
||||
/// <summary>
|
||||
/// 保存已注册的数据库提供程序及其配置委托。
|
||||
/// </summary>
|
||||
private static readonly Dictionary<DatabaseProvider, ProviderConfigurator> _providers = new();
|
||||
|
||||
/// <summary>
|
||||
/// 注册一个数据库提供程序。
|
||||
/// </summary>
|
||||
public static void Register(DatabaseProvider provider, ProviderConfigurator configurator)
|
||||
{
|
||||
_providers[provider] = configurator;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 尝试获取注册的提供程序配置。
|
||||
/// </summary>
|
||||
public static bool TryGet(DatabaseProvider provider, out ProviderConfigurator configurator)
|
||||
{
|
||||
return _providers.TryGetValue(provider, out configurator!);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 注册所有内置提供程序的默认配置(四个包均已内置在 Avalonia-EFCore 中)。
|
||||
/// 注册完成后由调用方根据自身需求选择具体的 <see cref="DatabaseProvider"/>。
|
||||
/// </summary>
|
||||
public static void RegisterDefaults()
|
||||
{
|
||||
Register(DatabaseProvider.SQLite, (opts, cs, timeout) =>
|
||||
opts.UseSqlite(cs, o => o.CommandTimeout(timeout)));
|
||||
|
||||
Register(DatabaseProvider.SqlServer, (opts, cs, timeout) =>
|
||||
opts.UseSqlServer(cs, o => { o.CommandTimeout(timeout); o.EnableRetryOnFailure(3); }));
|
||||
|
||||
Register(DatabaseProvider.PostgreSQL, (opts, cs, timeout) =>
|
||||
opts.UseNpgsql(cs, o => { o.CommandTimeout(timeout); o.EnableRetryOnFailure(3); }));
|
||||
|
||||
Register(DatabaseProvider.MySQL, (opts, cs, timeout) =>
|
||||
opts.UseMySQL(cs, o => o.CommandTimeout(timeout)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
namespace Avalonia_EFCore.Database
|
||||
{
|
||||
/// <summary>
|
||||
/// SQLite 专用 DbContext,用于隔离 SQLite 迁移集。
|
||||
/// </summary>
|
||||
public sealed class SqliteAppDataContext(DatabaseConfiguration dbConfig) : AppDataContext(dbConfig)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// SQL Server 专用 DbContext,用于隔离 SQL Server 迁移集。
|
||||
/// </summary>
|
||||
public sealed class SqlServerAppDataContext(DatabaseConfiguration dbConfig) : AppDataContext(dbConfig)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// PostgreSQL 专用 DbContext,用于隔离 PostgreSQL 迁移集。
|
||||
/// </summary>
|
||||
public sealed class PostgreSqlAppDataContext(DatabaseConfiguration dbConfig) : AppDataContext(dbConfig)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// MySQL 专用 DbContext,用于隔离 MySQL 迁移集。
|
||||
/// </summary>
|
||||
public sealed class MySqlAppDataContext(DatabaseConfiguration dbConfig) : AppDataContext(dbConfig)
|
||||
{
|
||||
}
|
||||
}
|
||||
+175
@@ -0,0 +1,175 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using Avalonia_EFCore.Database;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Avalonia_EFCore.Migrations.MySQL
|
||||
{
|
||||
[DbContext(typeof(MySqlAppDataContext))]
|
||||
[Migration("20260520082626_AutoMigration_20260520162543")]
|
||||
partial class AutoMigration_20260520162543
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "10.0.7")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 64);
|
||||
|
||||
modelBuilder.Entity("Avalonia_EFCore.Models.ApiRefreshTokenEntity", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint")
|
||||
.HasColumnName("id");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("datetime(6)")
|
||||
.HasColumnName("created-at");
|
||||
|
||||
b.Property<string>("Device")
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("varchar(200)")
|
||||
.HasColumnName("device");
|
||||
|
||||
b.Property<DateTime>("ExpiresAt")
|
||||
.HasColumnType("datetime(6)")
|
||||
.HasColumnName("expires-at");
|
||||
|
||||
b.Property<string>("IpAddress")
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("varchar(64)")
|
||||
.HasColumnName("ip-address");
|
||||
|
||||
b.Property<string>("ReplacedByTokenHash")
|
||||
.HasMaxLength(128)
|
||||
.HasColumnType("varchar(128)")
|
||||
.HasColumnName("replaced-by-token-hash");
|
||||
|
||||
b.Property<DateTime?>("RevokedAt")
|
||||
.HasColumnType("datetime(6)")
|
||||
.HasColumnName("revoked-at");
|
||||
|
||||
b.Property<string>("TokenHash")
|
||||
.IsRequired()
|
||||
.HasMaxLength(128)
|
||||
.HasColumnType("varchar(128)")
|
||||
.HasColumnName("token-hash");
|
||||
|
||||
b.Property<int>("UserId")
|
||||
.HasColumnType("int")
|
||||
.HasColumnName("user-id");
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("pk-api-refresh-token");
|
||||
|
||||
b.HasIndex("TokenHash")
|
||||
.IsUnique()
|
||||
.HasDatabaseName("idx-api-refresh-token-hash");
|
||||
|
||||
b.HasIndex("UserId")
|
||||
.HasDatabaseName("idx-api-refresh-token-user-id");
|
||||
|
||||
b.ToTable("api-refresh-token", t =>
|
||||
{
|
||||
t.HasComment("API refresh token");
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Avalonia_EFCore.Models.UserEntity", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int")
|
||||
.HasColumnName("id")
|
||||
.HasComment("用户主键");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("datetime(6)")
|
||||
.HasColumnName("created-at")
|
||||
.HasComment("创建时间");
|
||||
|
||||
b.Property<string>("Email")
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("varchar(200)")
|
||||
.HasColumnName("email")
|
||||
.HasComment("用户邮箱");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("varchar(100)")
|
||||
.HasColumnName("name")
|
||||
.HasComment("用户名称");
|
||||
|
||||
b.Property<string>("PhoneNumber")
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("varchar(50)")
|
||||
.HasColumnName("phone-number")
|
||||
.HasComment("电话号码");
|
||||
|
||||
b.Property<DateTime>("UpdatedAt")
|
||||
.HasColumnType("datetime(6)")
|
||||
.HasColumnName("updated-at")
|
||||
.HasComment("更新时间");
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("pk-user");
|
||||
|
||||
b.ToTable("user", t =>
|
||||
{
|
||||
t.HasComment("用户实体,演示数据库 CRUD 操作");
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Avalonia_EFCore.Models.WeatherForecastEntity", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int")
|
||||
.HasColumnName("id")
|
||||
.HasComment("天气预报主键");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("datetime(6)")
|
||||
.HasColumnName("created-at")
|
||||
.HasComment("创建时间");
|
||||
|
||||
b.Property<DateOnly>("Date")
|
||||
.HasColumnType("date")
|
||||
.HasColumnName("date")
|
||||
.HasComment("预报日期");
|
||||
|
||||
b.Property<string>("Summary")
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("varchar(200)")
|
||||
.HasColumnName("summary")
|
||||
.HasComment("天气摘要");
|
||||
|
||||
b.Property<int>("TemperatureC")
|
||||
.HasColumnType("int")
|
||||
.HasColumnName("temperature-c")
|
||||
.HasComment("摄氏温度");
|
||||
|
||||
b.Property<DateTime>("UpdatedAt")
|
||||
.HasColumnType("datetime(6)")
|
||||
.HasColumnName("updated-at")
|
||||
.HasComment("更新时间");
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("pk-weather-forecast");
|
||||
|
||||
b.ToTable("weather-forecast", t =>
|
||||
{
|
||||
t.HasComment("天气预报数据实体");
|
||||
});
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using MySql.EntityFrameworkCore.Metadata;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Avalonia_EFCore.Migrations.MySQL
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AutoMigration_20260520162543 : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AlterDatabase()
|
||||
.Annotation("MySQL:Charset", "utf8mb4");
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "api-refresh-token",
|
||||
columns: table => new
|
||||
{
|
||||
id = table.Column<long>(type: "bigint", nullable: false)
|
||||
.Annotation("MySQL:ValueGenerationStrategy", MySQLValueGenerationStrategy.IdentityColumn),
|
||||
userid = table.Column<int>(name: "user-id", type: "int", nullable: false),
|
||||
tokenhash = table.Column<string>(name: "token-hash", type: "varchar(128)", maxLength: 128, nullable: false),
|
||||
createdat = table.Column<DateTime>(name: "created-at", type: "datetime(6)", nullable: false),
|
||||
expiresat = table.Column<DateTime>(name: "expires-at", type: "datetime(6)", nullable: false),
|
||||
revokedat = table.Column<DateTime>(name: "revoked-at", type: "datetime(6)", nullable: true),
|
||||
replacedbytokenhash = table.Column<string>(name: "replaced-by-token-hash", type: "varchar(128)", maxLength: 128, nullable: true),
|
||||
device = table.Column<string>(type: "varchar(200)", maxLength: 200, nullable: true),
|
||||
ipaddress = table.Column<string>(name: "ip-address", type: "varchar(64)", maxLength: 64, nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("pk-api-refresh-token", x => x.id);
|
||||
},
|
||||
comment: "API refresh token")
|
||||
.Annotation("MySQL:Charset", "utf8mb4");
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "user",
|
||||
columns: table => new
|
||||
{
|
||||
id = table.Column<int>(type: "int", nullable: false, comment: "用户主键")
|
||||
.Annotation("MySQL:ValueGenerationStrategy", MySQLValueGenerationStrategy.IdentityColumn),
|
||||
name = table.Column<string>(type: "varchar(100)", maxLength: 100, nullable: true, comment: "用户名称"),
|
||||
email = table.Column<string>(type: "varchar(200)", maxLength: 200, nullable: true, comment: "用户邮箱"),
|
||||
phonenumber = table.Column<string>(name: "phone-number", type: "varchar(50)", maxLength: 50, nullable: true, comment: "电话号码"),
|
||||
createdat = table.Column<DateTime>(name: "created-at", type: "datetime(6)", nullable: false, comment: "创建时间"),
|
||||
updatedat = table.Column<DateTime>(name: "updated-at", type: "datetime(6)", nullable: false, comment: "更新时间")
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("pk-user", x => x.id);
|
||||
},
|
||||
comment: "用户实体,演示数据库 CRUD 操作")
|
||||
.Annotation("MySQL:Charset", "utf8mb4");
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "weather-forecast",
|
||||
columns: table => new
|
||||
{
|
||||
id = table.Column<int>(type: "int", nullable: false, comment: "天气预报主键")
|
||||
.Annotation("MySQL:ValueGenerationStrategy", MySQLValueGenerationStrategy.IdentityColumn),
|
||||
date = table.Column<DateOnly>(type: "date", nullable: false, comment: "预报日期"),
|
||||
temperaturec = table.Column<int>(name: "temperature-c", type: "int", nullable: false, comment: "摄氏温度"),
|
||||
summary = table.Column<string>(type: "varchar(200)", maxLength: 200, nullable: true, comment: "天气摘要"),
|
||||
createdat = table.Column<DateTime>(name: "created-at", type: "datetime(6)", nullable: false, comment: "创建时间"),
|
||||
updatedat = table.Column<DateTime>(name: "updated-at", type: "datetime(6)", nullable: false, comment: "更新时间")
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("pk-weather-forecast", x => x.id);
|
||||
},
|
||||
comment: "天气预报数据实体")
|
||||
.Annotation("MySQL:Charset", "utf8mb4");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "idx-api-refresh-token-hash",
|
||||
table: "api-refresh-token",
|
||||
column: "token-hash",
|
||||
unique: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "idx-api-refresh-token-user-id",
|
||||
table: "api-refresh-token",
|
||||
column: "user-id");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "api-refresh-token");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "user");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "weather-forecast");
|
||||
}
|
||||
}
|
||||
}
|
||||
+181
@@ -0,0 +1,181 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using Avalonia_EFCore.Database;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Avalonia_EFCore.Migrations.MySQL
|
||||
{
|
||||
[DbContext(typeof(MySqlAppDataContext))]
|
||||
[Migration("20260520083306_AutoMigration_20260520163216")]
|
||||
partial class AutoMigration_20260520163216
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "10.0.7")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 64);
|
||||
|
||||
modelBuilder.Entity("Avalonia_EFCore.Models.ApiRefreshTokenEntity", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint")
|
||||
.HasColumnName("id");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("datetime(6)")
|
||||
.HasColumnName("created-at");
|
||||
|
||||
b.Property<string>("Device")
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("varchar(200)")
|
||||
.HasColumnName("device");
|
||||
|
||||
b.Property<DateTime>("ExpiresAt")
|
||||
.HasColumnType("datetime(6)")
|
||||
.HasColumnName("expires-at");
|
||||
|
||||
b.Property<string>("IpAddress")
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("varchar(64)")
|
||||
.HasColumnName("ip-address");
|
||||
|
||||
b.Property<string>("ReplacedByTokenHash")
|
||||
.HasMaxLength(128)
|
||||
.HasColumnType("varchar(128)")
|
||||
.HasColumnName("replaced-by-token-hash");
|
||||
|
||||
b.Property<DateTime?>("RevokedAt")
|
||||
.HasColumnType("datetime(6)")
|
||||
.HasColumnName("revoked-at");
|
||||
|
||||
b.Property<string>("TokenHash")
|
||||
.IsRequired()
|
||||
.HasMaxLength(128)
|
||||
.HasColumnType("varchar(128)")
|
||||
.HasColumnName("token-hash");
|
||||
|
||||
b.Property<int>("UserId")
|
||||
.HasColumnType("int")
|
||||
.HasColumnName("user-id");
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("pk-api-refresh-token");
|
||||
|
||||
b.HasIndex("TokenHash")
|
||||
.IsUnique()
|
||||
.HasDatabaseName("idx-api-refresh-token-hash");
|
||||
|
||||
b.HasIndex("UserId")
|
||||
.HasDatabaseName("idx-api-refresh-token-user-id");
|
||||
|
||||
b.ToTable("api-refresh-token", t =>
|
||||
{
|
||||
t.HasComment("API refresh token");
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Avalonia_EFCore.Models.UserEntity", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int")
|
||||
.HasColumnName("id")
|
||||
.HasComment("用户主键");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("datetime(6)")
|
||||
.HasColumnName("created-at")
|
||||
.HasComment("创建时间");
|
||||
|
||||
b.Property<string>("Email")
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("varchar(200)")
|
||||
.HasColumnName("email")
|
||||
.HasComment("用户邮箱");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("varchar(100)")
|
||||
.HasColumnName("name")
|
||||
.HasComment("用户名称");
|
||||
|
||||
b.Property<string>("PasswordHash")
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("varchar(200)")
|
||||
.HasColumnName("password-hash")
|
||||
.HasComment("密码哈希值");
|
||||
|
||||
b.Property<string>("PhoneNumber")
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("varchar(50)")
|
||||
.HasColumnName("phone-number")
|
||||
.HasComment("电话号码");
|
||||
|
||||
b.Property<DateTime>("UpdatedAt")
|
||||
.HasColumnType("datetime(6)")
|
||||
.HasColumnName("updated-at")
|
||||
.HasComment("更新时间");
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("pk-user");
|
||||
|
||||
b.ToTable("user", t =>
|
||||
{
|
||||
t.HasComment("用户实体,演示数据库 CRUD 操作");
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Avalonia_EFCore.Models.WeatherForecastEntity", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int")
|
||||
.HasColumnName("id")
|
||||
.HasComment("天气预报主键");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("datetime(6)")
|
||||
.HasColumnName("created-at")
|
||||
.HasComment("创建时间");
|
||||
|
||||
b.Property<DateOnly>("Date")
|
||||
.HasColumnType("date")
|
||||
.HasColumnName("date")
|
||||
.HasComment("预报日期");
|
||||
|
||||
b.Property<string>("Summary")
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("varchar(200)")
|
||||
.HasColumnName("summary")
|
||||
.HasComment("天气摘要");
|
||||
|
||||
b.Property<int>("TemperatureC")
|
||||
.HasColumnType("int")
|
||||
.HasColumnName("temperature-c")
|
||||
.HasComment("摄氏温度");
|
||||
|
||||
b.Property<DateTime>("UpdatedAt")
|
||||
.HasColumnType("datetime(6)")
|
||||
.HasColumnName("updated-at")
|
||||
.HasComment("更新时间");
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("pk-weather-forecast");
|
||||
|
||||
b.ToTable("weather-forecast", t =>
|
||||
{
|
||||
t.HasComment("天气预报数据实体");
|
||||
});
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Avalonia_EFCore.Migrations.MySQL
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AutoMigration_20260520163216 : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "password-hash",
|
||||
table: "user",
|
||||
type: "varchar(200)",
|
||||
maxLength: 200,
|
||||
nullable: true,
|
||||
comment: "密码哈希值");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "password-hash",
|
||||
table: "user");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,178 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using Avalonia_EFCore.Database;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Avalonia_EFCore.Migrations.MySQL
|
||||
{
|
||||
[DbContext(typeof(MySqlAppDataContext))]
|
||||
partial class MySqlAppDataContextModelSnapshot : ModelSnapshot
|
||||
{
|
||||
protected override void BuildModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "10.0.7")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 64);
|
||||
|
||||
modelBuilder.Entity("Avalonia_EFCore.Models.ApiRefreshTokenEntity", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint")
|
||||
.HasColumnName("id");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("datetime(6)")
|
||||
.HasColumnName("created-at");
|
||||
|
||||
b.Property<string>("Device")
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("varchar(200)")
|
||||
.HasColumnName("device");
|
||||
|
||||
b.Property<DateTime>("ExpiresAt")
|
||||
.HasColumnType("datetime(6)")
|
||||
.HasColumnName("expires-at");
|
||||
|
||||
b.Property<string>("IpAddress")
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("varchar(64)")
|
||||
.HasColumnName("ip-address");
|
||||
|
||||
b.Property<string>("ReplacedByTokenHash")
|
||||
.HasMaxLength(128)
|
||||
.HasColumnType("varchar(128)")
|
||||
.HasColumnName("replaced-by-token-hash");
|
||||
|
||||
b.Property<DateTime?>("RevokedAt")
|
||||
.HasColumnType("datetime(6)")
|
||||
.HasColumnName("revoked-at");
|
||||
|
||||
b.Property<string>("TokenHash")
|
||||
.IsRequired()
|
||||
.HasMaxLength(128)
|
||||
.HasColumnType("varchar(128)")
|
||||
.HasColumnName("token-hash");
|
||||
|
||||
b.Property<int>("UserId")
|
||||
.HasColumnType("int")
|
||||
.HasColumnName("user-id");
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("pk-api-refresh-token");
|
||||
|
||||
b.HasIndex("TokenHash")
|
||||
.IsUnique()
|
||||
.HasDatabaseName("idx-api-refresh-token-hash");
|
||||
|
||||
b.HasIndex("UserId")
|
||||
.HasDatabaseName("idx-api-refresh-token-user-id");
|
||||
|
||||
b.ToTable("api-refresh-token", t =>
|
||||
{
|
||||
t.HasComment("API refresh token");
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Avalonia_EFCore.Models.UserEntity", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int")
|
||||
.HasColumnName("id")
|
||||
.HasComment("用户主键");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("datetime(6)")
|
||||
.HasColumnName("created-at")
|
||||
.HasComment("创建时间");
|
||||
|
||||
b.Property<string>("Email")
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("varchar(200)")
|
||||
.HasColumnName("email")
|
||||
.HasComment("用户邮箱");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("varchar(100)")
|
||||
.HasColumnName("name")
|
||||
.HasComment("用户名称");
|
||||
|
||||
b.Property<string>("PasswordHash")
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("varchar(200)")
|
||||
.HasColumnName("password-hash")
|
||||
.HasComment("密码哈希值");
|
||||
|
||||
b.Property<string>("PhoneNumber")
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("varchar(50)")
|
||||
.HasColumnName("phone-number")
|
||||
.HasComment("电话号码");
|
||||
|
||||
b.Property<DateTime>("UpdatedAt")
|
||||
.HasColumnType("datetime(6)")
|
||||
.HasColumnName("updated-at")
|
||||
.HasComment("更新时间");
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("pk-user");
|
||||
|
||||
b.ToTable("user", t =>
|
||||
{
|
||||
t.HasComment("用户实体,演示数据库 CRUD 操作");
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Avalonia_EFCore.Models.WeatherForecastEntity", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int")
|
||||
.HasColumnName("id")
|
||||
.HasComment("天气预报主键");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("datetime(6)")
|
||||
.HasColumnName("created-at")
|
||||
.HasComment("创建时间");
|
||||
|
||||
b.Property<DateOnly>("Date")
|
||||
.HasColumnType("date")
|
||||
.HasColumnName("date")
|
||||
.HasComment("预报日期");
|
||||
|
||||
b.Property<string>("Summary")
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("varchar(200)")
|
||||
.HasColumnName("summary")
|
||||
.HasComment("天气摘要");
|
||||
|
||||
b.Property<int>("TemperatureC")
|
||||
.HasColumnType("int")
|
||||
.HasColumnName("temperature-c")
|
||||
.HasComment("摄氏温度");
|
||||
|
||||
b.Property<DateTime>("UpdatedAt")
|
||||
.HasColumnType("datetime(6)")
|
||||
.HasColumnName("updated-at")
|
||||
.HasComment("更新时间");
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("pk-weather-forecast");
|
||||
|
||||
b.ToTable("weather-forecast", t =>
|
||||
{
|
||||
t.HasComment("天气预报数据实体");
|
||||
});
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
Generated
+184
@@ -0,0 +1,184 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using Avalonia_EFCore.Database;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Avalonia_EFCore.Migrations.PostgreSQL
|
||||
{
|
||||
[DbContext(typeof(PostgreSqlAppDataContext))]
|
||||
[Migration("20260520082617_AutoMigration_20260520162543")]
|
||||
partial class AutoMigration_20260520162543
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "10.0.7")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
||||
|
||||
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
||||
|
||||
modelBuilder.Entity("Avalonia_EFCore.Models.ApiRefreshTokenEntity", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint")
|
||||
.HasColumnName("id");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("created-at");
|
||||
|
||||
b.Property<string>("Device")
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("character varying(200)")
|
||||
.HasColumnName("device");
|
||||
|
||||
b.Property<DateTime>("ExpiresAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("expires-at");
|
||||
|
||||
b.Property<string>("IpAddress")
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("character varying(64)")
|
||||
.HasColumnName("ip-address");
|
||||
|
||||
b.Property<string>("ReplacedByTokenHash")
|
||||
.HasMaxLength(128)
|
||||
.HasColumnType("character varying(128)")
|
||||
.HasColumnName("replaced-by-token-hash");
|
||||
|
||||
b.Property<DateTime?>("RevokedAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("revoked-at");
|
||||
|
||||
b.Property<string>("TokenHash")
|
||||
.IsRequired()
|
||||
.HasMaxLength(128)
|
||||
.HasColumnType("character varying(128)")
|
||||
.HasColumnName("token-hash");
|
||||
|
||||
b.Property<int>("UserId")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("user-id");
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("pk-api-refresh-token");
|
||||
|
||||
b.HasIndex("TokenHash")
|
||||
.IsUnique()
|
||||
.HasDatabaseName("idx-api-refresh-token-hash");
|
||||
|
||||
b.HasIndex("UserId")
|
||||
.HasDatabaseName("idx-api-refresh-token-user-id");
|
||||
|
||||
b.ToTable("api-refresh-token", t =>
|
||||
{
|
||||
t.HasComment("API refresh token");
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Avalonia_EFCore.Models.UserEntity", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("id")
|
||||
.HasComment("用户主键");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("created-at")
|
||||
.HasComment("创建时间");
|
||||
|
||||
b.Property<string>("Email")
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("character varying(200)")
|
||||
.HasColumnName("email")
|
||||
.HasComment("用户邮箱");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("character varying(100)")
|
||||
.HasColumnName("name")
|
||||
.HasComment("用户名称");
|
||||
|
||||
b.Property<string>("PhoneNumber")
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("character varying(50)")
|
||||
.HasColumnName("phone-number")
|
||||
.HasComment("电话号码");
|
||||
|
||||
b.Property<DateTime>("UpdatedAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("updated-at")
|
||||
.HasComment("更新时间");
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("pk-user");
|
||||
|
||||
b.ToTable("user", t =>
|
||||
{
|
||||
t.HasComment("用户实体,演示数据库 CRUD 操作");
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Avalonia_EFCore.Models.WeatherForecastEntity", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("id")
|
||||
.HasComment("天气预报主键");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("created-at")
|
||||
.HasComment("创建时间");
|
||||
|
||||
b.Property<DateOnly>("Date")
|
||||
.HasColumnType("date")
|
||||
.HasColumnName("date")
|
||||
.HasComment("预报日期");
|
||||
|
||||
b.Property<string>("Summary")
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("character varying(200)")
|
||||
.HasColumnName("summary")
|
||||
.HasComment("天气摘要");
|
||||
|
||||
b.Property<int>("TemperatureC")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("temperature-c")
|
||||
.HasComment("摄氏温度");
|
||||
|
||||
b.Property<DateTime>("UpdatedAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("updated-at")
|
||||
.HasComment("更新时间");
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("pk-weather-forecast");
|
||||
|
||||
b.ToTable("weather-forecast", t =>
|
||||
{
|
||||
t.HasComment("天气预报数据实体");
|
||||
});
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Avalonia_EFCore.Migrations.PostgreSQL
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AutoMigration_20260520162543 : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "api-refresh-token",
|
||||
columns: table => new
|
||||
{
|
||||
id = table.Column<long>(type: "bigint", nullable: false)
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||
userid = table.Column<int>(name: "user-id", type: "integer", nullable: false),
|
||||
tokenhash = table.Column<string>(name: "token-hash", type: "character varying(128)", maxLength: 128, nullable: false),
|
||||
createdat = table.Column<DateTime>(name: "created-at", type: "timestamp with time zone", nullable: false),
|
||||
expiresat = table.Column<DateTime>(name: "expires-at", type: "timestamp with time zone", nullable: false),
|
||||
revokedat = table.Column<DateTime>(name: "revoked-at", type: "timestamp with time zone", nullable: true),
|
||||
replacedbytokenhash = table.Column<string>(name: "replaced-by-token-hash", type: "character varying(128)", maxLength: 128, nullable: true),
|
||||
device = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: true),
|
||||
ipaddress = table.Column<string>(name: "ip-address", type: "character varying(64)", maxLength: 64, nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("pk-api-refresh-token", x => x.id);
|
||||
},
|
||||
comment: "API refresh token");
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "user",
|
||||
columns: table => new
|
||||
{
|
||||
id = table.Column<int>(type: "integer", nullable: false, comment: "用户主键")
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||
name = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: true, comment: "用户名称"),
|
||||
email = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: true, comment: "用户邮箱"),
|
||||
phonenumber = table.Column<string>(name: "phone-number", type: "character varying(50)", maxLength: 50, nullable: true, comment: "电话号码"),
|
||||
createdat = table.Column<DateTime>(name: "created-at", type: "timestamp with time zone", nullable: false, comment: "创建时间"),
|
||||
updatedat = table.Column<DateTime>(name: "updated-at", type: "timestamp with time zone", nullable: false, comment: "更新时间")
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("pk-user", x => x.id);
|
||||
},
|
||||
comment: "用户实体,演示数据库 CRUD 操作");
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "weather-forecast",
|
||||
columns: table => new
|
||||
{
|
||||
id = table.Column<int>(type: "integer", nullable: false, comment: "天气预报主键")
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||
date = table.Column<DateOnly>(type: "date", nullable: false, comment: "预报日期"),
|
||||
temperaturec = table.Column<int>(name: "temperature-c", type: "integer", nullable: false, comment: "摄氏温度"),
|
||||
summary = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: true, comment: "天气摘要"),
|
||||
createdat = table.Column<DateTime>(name: "created-at", type: "timestamp with time zone", nullable: false, comment: "创建时间"),
|
||||
updatedat = table.Column<DateTime>(name: "updated-at", type: "timestamp with time zone", nullable: false, comment: "更新时间")
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("pk-weather-forecast", x => x.id);
|
||||
},
|
||||
comment: "天气预报数据实体");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "idx-api-refresh-token-hash",
|
||||
table: "api-refresh-token",
|
||||
column: "token-hash",
|
||||
unique: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "idx-api-refresh-token-user-id",
|
||||
table: "api-refresh-token",
|
||||
column: "user-id");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "api-refresh-token");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "user");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "weather-forecast");
|
||||
}
|
||||
}
|
||||
}
|
||||
Generated
+190
@@ -0,0 +1,190 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using Avalonia_EFCore.Database;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Avalonia_EFCore.Migrations.PostgreSQL
|
||||
{
|
||||
[DbContext(typeof(PostgreSqlAppDataContext))]
|
||||
[Migration("20260520083254_AutoMigration_20260520163216")]
|
||||
partial class AutoMigration_20260520163216
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "10.0.7")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
||||
|
||||
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
||||
|
||||
modelBuilder.Entity("Avalonia_EFCore.Models.ApiRefreshTokenEntity", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint")
|
||||
.HasColumnName("id");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("created-at");
|
||||
|
||||
b.Property<string>("Device")
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("character varying(200)")
|
||||
.HasColumnName("device");
|
||||
|
||||
b.Property<DateTime>("ExpiresAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("expires-at");
|
||||
|
||||
b.Property<string>("IpAddress")
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("character varying(64)")
|
||||
.HasColumnName("ip-address");
|
||||
|
||||
b.Property<string>("ReplacedByTokenHash")
|
||||
.HasMaxLength(128)
|
||||
.HasColumnType("character varying(128)")
|
||||
.HasColumnName("replaced-by-token-hash");
|
||||
|
||||
b.Property<DateTime?>("RevokedAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("revoked-at");
|
||||
|
||||
b.Property<string>("TokenHash")
|
||||
.IsRequired()
|
||||
.HasMaxLength(128)
|
||||
.HasColumnType("character varying(128)")
|
||||
.HasColumnName("token-hash");
|
||||
|
||||
b.Property<int>("UserId")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("user-id");
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("pk-api-refresh-token");
|
||||
|
||||
b.HasIndex("TokenHash")
|
||||
.IsUnique()
|
||||
.HasDatabaseName("idx-api-refresh-token-hash");
|
||||
|
||||
b.HasIndex("UserId")
|
||||
.HasDatabaseName("idx-api-refresh-token-user-id");
|
||||
|
||||
b.ToTable("api-refresh-token", t =>
|
||||
{
|
||||
t.HasComment("API refresh token");
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Avalonia_EFCore.Models.UserEntity", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("id")
|
||||
.HasComment("用户主键");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("created-at")
|
||||
.HasComment("创建时间");
|
||||
|
||||
b.Property<string>("Email")
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("character varying(200)")
|
||||
.HasColumnName("email")
|
||||
.HasComment("用户邮箱");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("character varying(100)")
|
||||
.HasColumnName("name")
|
||||
.HasComment("用户名称");
|
||||
|
||||
b.Property<string>("PasswordHash")
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("character varying(200)")
|
||||
.HasColumnName("password-hash")
|
||||
.HasComment("密码哈希值");
|
||||
|
||||
b.Property<string>("PhoneNumber")
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("character varying(50)")
|
||||
.HasColumnName("phone-number")
|
||||
.HasComment("电话号码");
|
||||
|
||||
b.Property<DateTime>("UpdatedAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("updated-at")
|
||||
.HasComment("更新时间");
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("pk-user");
|
||||
|
||||
b.ToTable("user", t =>
|
||||
{
|
||||
t.HasComment("用户实体,演示数据库 CRUD 操作");
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Avalonia_EFCore.Models.WeatherForecastEntity", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("id")
|
||||
.HasComment("天气预报主键");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("created-at")
|
||||
.HasComment("创建时间");
|
||||
|
||||
b.Property<DateOnly>("Date")
|
||||
.HasColumnType("date")
|
||||
.HasColumnName("date")
|
||||
.HasComment("预报日期");
|
||||
|
||||
b.Property<string>("Summary")
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("character varying(200)")
|
||||
.HasColumnName("summary")
|
||||
.HasComment("天气摘要");
|
||||
|
||||
b.Property<int>("TemperatureC")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("temperature-c")
|
||||
.HasComment("摄氏温度");
|
||||
|
||||
b.Property<DateTime>("UpdatedAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("updated-at")
|
||||
.HasComment("更新时间");
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("pk-weather-forecast");
|
||||
|
||||
b.ToTable("weather-forecast", t =>
|
||||
{
|
||||
t.HasComment("天气预报数据实体");
|
||||
});
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Avalonia_EFCore.Migrations.PostgreSQL
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AutoMigration_20260520163216 : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "password-hash",
|
||||
table: "user",
|
||||
type: "character varying(200)",
|
||||
maxLength: 200,
|
||||
nullable: true,
|
||||
comment: "密码哈希值");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "password-hash",
|
||||
table: "user");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,187 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using Avalonia_EFCore.Database;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Avalonia_EFCore.Migrations.PostgreSQL
|
||||
{
|
||||
[DbContext(typeof(PostgreSqlAppDataContext))]
|
||||
partial class PostgreSqlAppDataContextModelSnapshot : ModelSnapshot
|
||||
{
|
||||
protected override void BuildModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "10.0.7")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
||||
|
||||
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
||||
|
||||
modelBuilder.Entity("Avalonia_EFCore.Models.ApiRefreshTokenEntity", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint")
|
||||
.HasColumnName("id");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("created-at");
|
||||
|
||||
b.Property<string>("Device")
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("character varying(200)")
|
||||
.HasColumnName("device");
|
||||
|
||||
b.Property<DateTime>("ExpiresAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("expires-at");
|
||||
|
||||
b.Property<string>("IpAddress")
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("character varying(64)")
|
||||
.HasColumnName("ip-address");
|
||||
|
||||
b.Property<string>("ReplacedByTokenHash")
|
||||
.HasMaxLength(128)
|
||||
.HasColumnType("character varying(128)")
|
||||
.HasColumnName("replaced-by-token-hash");
|
||||
|
||||
b.Property<DateTime?>("RevokedAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("revoked-at");
|
||||
|
||||
b.Property<string>("TokenHash")
|
||||
.IsRequired()
|
||||
.HasMaxLength(128)
|
||||
.HasColumnType("character varying(128)")
|
||||
.HasColumnName("token-hash");
|
||||
|
||||
b.Property<int>("UserId")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("user-id");
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("pk-api-refresh-token");
|
||||
|
||||
b.HasIndex("TokenHash")
|
||||
.IsUnique()
|
||||
.HasDatabaseName("idx-api-refresh-token-hash");
|
||||
|
||||
b.HasIndex("UserId")
|
||||
.HasDatabaseName("idx-api-refresh-token-user-id");
|
||||
|
||||
b.ToTable("api-refresh-token", t =>
|
||||
{
|
||||
t.HasComment("API refresh token");
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Avalonia_EFCore.Models.UserEntity", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("id")
|
||||
.HasComment("用户主键");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("created-at")
|
||||
.HasComment("创建时间");
|
||||
|
||||
b.Property<string>("Email")
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("character varying(200)")
|
||||
.HasColumnName("email")
|
||||
.HasComment("用户邮箱");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("character varying(100)")
|
||||
.HasColumnName("name")
|
||||
.HasComment("用户名称");
|
||||
|
||||
b.Property<string>("PasswordHash")
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("character varying(200)")
|
||||
.HasColumnName("password-hash")
|
||||
.HasComment("密码哈希值");
|
||||
|
||||
b.Property<string>("PhoneNumber")
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("character varying(50)")
|
||||
.HasColumnName("phone-number")
|
||||
.HasComment("电话号码");
|
||||
|
||||
b.Property<DateTime>("UpdatedAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("updated-at")
|
||||
.HasComment("更新时间");
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("pk-user");
|
||||
|
||||
b.ToTable("user", t =>
|
||||
{
|
||||
t.HasComment("用户实体,演示数据库 CRUD 操作");
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Avalonia_EFCore.Models.WeatherForecastEntity", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("id")
|
||||
.HasComment("天气预报主键");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("created-at")
|
||||
.HasComment("创建时间");
|
||||
|
||||
b.Property<DateOnly>("Date")
|
||||
.HasColumnType("date")
|
||||
.HasColumnName("date")
|
||||
.HasComment("预报日期");
|
||||
|
||||
b.Property<string>("Summary")
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("character varying(200)")
|
||||
.HasColumnName("summary")
|
||||
.HasComment("天气摘要");
|
||||
|
||||
b.Property<int>("TemperatureC")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("temperature-c")
|
||||
.HasComment("摄氏温度");
|
||||
|
||||
b.Property<DateTime>("UpdatedAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("updated-at")
|
||||
.HasComment("更新时间");
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("pk-weather-forecast");
|
||||
|
||||
b.ToTable("weather-forecast", t =>
|
||||
{
|
||||
t.HasComment("天气预报数据实体");
|
||||
});
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
using System;
|
||||
using Avalonia_EFCore.Database;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Avalonia_EFCore.Migrations.SQLite
|
||||
{
|
||||
[DbContext(typeof(SqliteAppDataContext))]
|
||||
[Migration("20260514000100_InitialCreate")]
|
||||
partial class InitialCreate
|
||||
{
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "10.0.0");
|
||||
|
||||
modelBuilder.Entity("Avalonia_EFCore.Models.UserEntity", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.HasComment("用户主键")
|
||||
.HasColumnName("id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasComment("创建时间")
|
||||
.HasColumnName("created-at");
|
||||
|
||||
b.Property<string>("Email")
|
||||
.HasComment("用户邮箱")
|
||||
.HasColumnName("email")
|
||||
.HasMaxLength(200);
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasComment("用户名称")
|
||||
.HasColumnName("name")
|
||||
.HasMaxLength(100);
|
||||
|
||||
b.Property<DateTime>("UpdatedAt")
|
||||
.HasComment("更新时间")
|
||||
.HasColumnName("updated-at");
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("pk-user");
|
||||
|
||||
b.ToTable("user", t =>
|
||||
{
|
||||
t.HasComment("用户实体,演示数据库 CRUD 操作");
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Avalonia_EFCore.Models.WeatherForecastEntity", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.HasComment("天气预报主键")
|
||||
.HasColumnName("id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasComment("创建时间")
|
||||
.HasColumnName("created-at");
|
||||
|
||||
b.Property<DateOnly>("Date")
|
||||
.HasComment("预报日期")
|
||||
.HasColumnName("date");
|
||||
|
||||
b.Property<string>("Summary")
|
||||
.HasComment("天气摘要")
|
||||
.HasColumnName("summary")
|
||||
.HasMaxLength(200);
|
||||
|
||||
b.Property<int>("TemperatureC")
|
||||
.HasComment("摄氏温度")
|
||||
.HasColumnName("temperature-c");
|
||||
|
||||
b.Property<DateTime>("UpdatedAt")
|
||||
.HasComment("更新时间")
|
||||
.HasColumnName("updated-at");
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("pk-weather-forecast");
|
||||
|
||||
b.ToTable("weather-forecast", t =>
|
||||
{
|
||||
t.HasComment("天气预报数据实体");
|
||||
});
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Avalonia_EFCore.Migrations.SQLite
|
||||
{
|
||||
/// <summary>
|
||||
/// 初始数据库基线。后续软件版本只追加新的 Migration,不修改已发布 Migration。
|
||||
/// </summary>
|
||||
public partial class InitialCreate : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "user",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(name: "id", nullable: false, comment: "用户主键")
|
||||
.Annotation("SqlServer:Identity", "1, 1")
|
||||
.Annotation("Sqlite:Autoincrement", true)
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||
Name = table.Column<string>(name: "name", maxLength: 100, nullable: true, comment: "用户名称"),
|
||||
Email = table.Column<string>(name: "email", maxLength: 200, nullable: true, comment: "用户邮箱"),
|
||||
CreatedAt = table.Column<DateTime>(name: "created-at", nullable: false, comment: "创建时间"),
|
||||
UpdatedAt = table.Column<DateTime>(name: "updated-at", nullable: false, comment: "更新时间")
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("pk-user", x => x.Id);
|
||||
},
|
||||
comment: "用户实体,演示数据库 CRUD 操作");
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "weather-forecast",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(name: "id", nullable: false, comment: "天气预报主键")
|
||||
.Annotation("SqlServer:Identity", "1, 1")
|
||||
.Annotation("Sqlite:Autoincrement", true)
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||
Date = table.Column<DateOnly>(name: "date", nullable: false, comment: "预报日期"),
|
||||
TemperatureC = table.Column<int>(name: "temperature-c", nullable: false, comment: "摄氏温度"),
|
||||
Summary = table.Column<string>(name: "summary", maxLength: 200, nullable: true, comment: "天气摘要"),
|
||||
CreatedAt = table.Column<DateTime>(name: "created-at", nullable: false, comment: "创建时间"),
|
||||
UpdatedAt = table.Column<DateTime>(name: "updated-at", nullable: false, comment: "更新时间")
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("pk-weather-forecast", x => x.Id);
|
||||
},
|
||||
comment: "天气预报数据实体");
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(name: "weather-forecast");
|
||||
migrationBuilder.DropTable(name: "user");
|
||||
}
|
||||
}
|
||||
}
|
||||
+113
@@ -0,0 +1,113 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using Avalonia_EFCore.Database;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Avalonia_EFCore.Migrations.SQLite
|
||||
{
|
||||
[DbContext(typeof(SqliteAppDataContext))]
|
||||
[Migration("20260515072045_AutoMigration_20260515152037")]
|
||||
partial class AutoMigration_20260515152037
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder.HasAnnotation("ProductVersion", "10.0.0");
|
||||
|
||||
modelBuilder.Entity("Avalonia_EFCore.Models.UserEntity", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("id")
|
||||
.HasComment("用户主键");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("created-at")
|
||||
.HasComment("创建时间");
|
||||
|
||||
b.Property<string>("Email")
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("email")
|
||||
.HasComment("用户邮箱");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("name")
|
||||
.HasComment("用户名称");
|
||||
|
||||
b.Property<string>("PhoneNumber")
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("phone-number")
|
||||
.HasComment("电话号码");
|
||||
|
||||
b.Property<DateTime>("UpdatedAt")
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("updated-at")
|
||||
.HasComment("更新时间");
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("pk-user");
|
||||
|
||||
b.ToTable("user", t =>
|
||||
{
|
||||
t.HasComment("用户实体,演示数据库 CRUD 操作");
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Avalonia_EFCore.Models.WeatherForecastEntity", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("id")
|
||||
.HasComment("天气预报主键");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("created-at")
|
||||
.HasComment("创建时间");
|
||||
|
||||
b.Property<DateOnly>("Date")
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("date")
|
||||
.HasComment("预报日期");
|
||||
|
||||
b.Property<string>("Summary")
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("summary")
|
||||
.HasComment("天气摘要");
|
||||
|
||||
b.Property<int>("TemperatureC")
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("temperature-c")
|
||||
.HasComment("摄氏温度");
|
||||
|
||||
b.Property<DateTime>("UpdatedAt")
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("updated-at")
|
||||
.HasComment("更新时间");
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("pk-weather-forecast");
|
||||
|
||||
b.ToTable("weather-forecast", t =>
|
||||
{
|
||||
t.HasComment("天气预报数据实体");
|
||||
});
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Avalonia_EFCore.Migrations.SQLite
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AutoMigration_20260515152037 : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "phone-number",
|
||||
table: "user",
|
||||
type: "TEXT",
|
||||
maxLength: 50,
|
||||
nullable: true,
|
||||
comment: "电话号码");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "phone-number",
|
||||
table: "user");
|
||||
}
|
||||
}
|
||||
}
|
||||
+173
@@ -0,0 +1,173 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using Avalonia_EFCore.Database;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Avalonia_EFCore.Migrations.SQLite
|
||||
{
|
||||
[DbContext(typeof(SqliteAppDataContext))]
|
||||
[Migration("20260515085847_AutoMigration_20260515165835")]
|
||||
partial class AutoMigration_20260515165835
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder.HasAnnotation("ProductVersion", "10.0.0");
|
||||
|
||||
modelBuilder.Entity("Avalonia_EFCore.Models.ApiRefreshTokenEntity", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("id");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("created-at");
|
||||
|
||||
b.Property<string>("Device")
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("device");
|
||||
|
||||
b.Property<DateTime>("ExpiresAt")
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("expires-at");
|
||||
|
||||
b.Property<string>("IpAddress")
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("ip-address");
|
||||
|
||||
b.Property<string>("ReplacedByTokenHash")
|
||||
.HasMaxLength(128)
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("replaced-by-token-hash");
|
||||
|
||||
b.Property<DateTime?>("RevokedAt")
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("revoked-at");
|
||||
|
||||
b.Property<string>("TokenHash")
|
||||
.IsRequired()
|
||||
.HasMaxLength(128)
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("token-hash");
|
||||
|
||||
b.Property<int>("UserId")
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("user-id");
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("pk-api-refresh-token");
|
||||
|
||||
b.HasIndex("TokenHash")
|
||||
.IsUnique()
|
||||
.HasDatabaseName("idx-api-refresh-token-hash");
|
||||
|
||||
b.HasIndex("UserId")
|
||||
.HasDatabaseName("idx-api-refresh-token-user-id");
|
||||
|
||||
b.ToTable("api-refresh-token", t =>
|
||||
{
|
||||
t.HasComment("API refresh token");
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Avalonia_EFCore.Models.UserEntity", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("id")
|
||||
.HasComment("用户主键");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("created-at")
|
||||
.HasComment("创建时间");
|
||||
|
||||
b.Property<string>("Email")
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("email")
|
||||
.HasComment("用户邮箱");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("name")
|
||||
.HasComment("用户名称");
|
||||
|
||||
b.Property<string>("PhoneNumber")
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("phone-number")
|
||||
.HasComment("电话号码");
|
||||
|
||||
b.Property<DateTime>("UpdatedAt")
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("updated-at")
|
||||
.HasComment("更新时间");
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("pk-user");
|
||||
|
||||
b.ToTable("user", t =>
|
||||
{
|
||||
t.HasComment("用户实体,演示数据库 CRUD 操作");
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Avalonia_EFCore.Models.WeatherForecastEntity", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("id")
|
||||
.HasComment("天气预报主键");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("created-at")
|
||||
.HasComment("创建时间");
|
||||
|
||||
b.Property<DateOnly>("Date")
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("date")
|
||||
.HasComment("预报日期");
|
||||
|
||||
b.Property<string>("Summary")
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("summary")
|
||||
.HasComment("天气摘要");
|
||||
|
||||
b.Property<int>("TemperatureC")
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("temperature-c")
|
||||
.HasComment("摄氏温度");
|
||||
|
||||
b.Property<DateTime>("UpdatedAt")
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("updated-at")
|
||||
.HasComment("更新时间");
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("pk-weather-forecast");
|
||||
|
||||
b.ToTable("weather-forecast", t =>
|
||||
{
|
||||
t.HasComment("天气预报数据实体");
|
||||
});
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Avalonia_EFCore.Migrations.SQLite
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AutoMigration_20260515165835 : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "api-refresh-token",
|
||||
columns: table => new
|
||||
{
|
||||
id = table.Column<long>(type: "INTEGER", nullable: false)
|
||||
.Annotation("Sqlite:Autoincrement", true),
|
||||
userid = table.Column<int>(name: "user-id", type: "INTEGER", nullable: false),
|
||||
tokenhash = table.Column<string>(name: "token-hash", type: "TEXT", maxLength: 128, nullable: false),
|
||||
createdat = table.Column<DateTime>(name: "created-at", type: "TEXT", nullable: false),
|
||||
expiresat = table.Column<DateTime>(name: "expires-at", type: "TEXT", nullable: false),
|
||||
revokedat = table.Column<DateTime>(name: "revoked-at", type: "TEXT", nullable: true),
|
||||
replacedbytokenhash = table.Column<string>(name: "replaced-by-token-hash", type: "TEXT", maxLength: 128, nullable: true),
|
||||
device = table.Column<string>(type: "TEXT", maxLength: 200, nullable: true),
|
||||
ipaddress = table.Column<string>(name: "ip-address", type: "TEXT", maxLength: 64, nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("pk-api-refresh-token", x => x.id);
|
||||
},
|
||||
comment: "API refresh token");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "idx-api-refresh-token-hash",
|
||||
table: "api-refresh-token",
|
||||
column: "token-hash",
|
||||
unique: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "idx-api-refresh-token-user-id",
|
||||
table: "api-refresh-token",
|
||||
column: "user-id");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "api-refresh-token");
|
||||
}
|
||||
}
|
||||
}
|
||||
+179
@@ -0,0 +1,179 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using Avalonia_EFCore.Database;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Avalonia_EFCore.Migrations.SQLite
|
||||
{
|
||||
[DbContext(typeof(SqliteAppDataContext))]
|
||||
[Migration("20260520083230_AutoMigration_20260520163216")]
|
||||
partial class AutoMigration_20260520163216
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder.HasAnnotation("ProductVersion", "10.0.7");
|
||||
|
||||
modelBuilder.Entity("Avalonia_EFCore.Models.ApiRefreshTokenEntity", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("id");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("created-at");
|
||||
|
||||
b.Property<string>("Device")
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("device");
|
||||
|
||||
b.Property<DateTime>("ExpiresAt")
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("expires-at");
|
||||
|
||||
b.Property<string>("IpAddress")
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("ip-address");
|
||||
|
||||
b.Property<string>("ReplacedByTokenHash")
|
||||
.HasMaxLength(128)
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("replaced-by-token-hash");
|
||||
|
||||
b.Property<DateTime?>("RevokedAt")
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("revoked-at");
|
||||
|
||||
b.Property<string>("TokenHash")
|
||||
.IsRequired()
|
||||
.HasMaxLength(128)
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("token-hash");
|
||||
|
||||
b.Property<int>("UserId")
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("user-id");
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("pk-api-refresh-token");
|
||||
|
||||
b.HasIndex("TokenHash")
|
||||
.IsUnique()
|
||||
.HasDatabaseName("idx-api-refresh-token-hash");
|
||||
|
||||
b.HasIndex("UserId")
|
||||
.HasDatabaseName("idx-api-refresh-token-user-id");
|
||||
|
||||
b.ToTable("api-refresh-token", t =>
|
||||
{
|
||||
t.HasComment("API refresh token");
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Avalonia_EFCore.Models.UserEntity", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("id")
|
||||
.HasComment("用户主键");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("created-at")
|
||||
.HasComment("创建时间");
|
||||
|
||||
b.Property<string>("Email")
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("email")
|
||||
.HasComment("用户邮箱");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("name")
|
||||
.HasComment("用户名称");
|
||||
|
||||
b.Property<string>("PasswordHash")
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("password-hash")
|
||||
.HasComment("密码哈希值");
|
||||
|
||||
b.Property<string>("PhoneNumber")
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("phone-number")
|
||||
.HasComment("电话号码");
|
||||
|
||||
b.Property<DateTime>("UpdatedAt")
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("updated-at")
|
||||
.HasComment("更新时间");
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("pk-user");
|
||||
|
||||
b.ToTable("user", t =>
|
||||
{
|
||||
t.HasComment("用户实体,演示数据库 CRUD 操作");
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Avalonia_EFCore.Models.WeatherForecastEntity", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("id")
|
||||
.HasComment("天气预报主键");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("created-at")
|
||||
.HasComment("创建时间");
|
||||
|
||||
b.Property<DateOnly>("Date")
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("date")
|
||||
.HasComment("预报日期");
|
||||
|
||||
b.Property<string>("Summary")
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("summary")
|
||||
.HasComment("天气摘要");
|
||||
|
||||
b.Property<int>("TemperatureC")
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("temperature-c")
|
||||
.HasComment("摄氏温度");
|
||||
|
||||
b.Property<DateTime>("UpdatedAt")
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("updated-at")
|
||||
.HasComment("更新时间");
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("pk-weather-forecast");
|
||||
|
||||
b.ToTable("weather-forecast", t =>
|
||||
{
|
||||
t.HasComment("天气预报数据实体");
|
||||
});
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Avalonia_EFCore.Migrations.SQLite
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AutoMigration_20260520163216 : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "password-hash",
|
||||
table: "user",
|
||||
type: "TEXT",
|
||||
maxLength: 200,
|
||||
nullable: true,
|
||||
comment: "密码哈希值");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "password-hash",
|
||||
table: "user");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,176 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using Avalonia_EFCore.Database;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Avalonia_EFCore.Migrations.SQLite
|
||||
{
|
||||
[DbContext(typeof(SqliteAppDataContext))]
|
||||
partial class SqliteAppDataContextModelSnapshot : ModelSnapshot
|
||||
{
|
||||
protected override void BuildModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder.HasAnnotation("ProductVersion", "10.0.7");
|
||||
|
||||
modelBuilder.Entity("Avalonia_EFCore.Models.ApiRefreshTokenEntity", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("id");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("created-at");
|
||||
|
||||
b.Property<string>("Device")
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("device");
|
||||
|
||||
b.Property<DateTime>("ExpiresAt")
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("expires-at");
|
||||
|
||||
b.Property<string>("IpAddress")
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("ip-address");
|
||||
|
||||
b.Property<string>("ReplacedByTokenHash")
|
||||
.HasMaxLength(128)
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("replaced-by-token-hash");
|
||||
|
||||
b.Property<DateTime?>("RevokedAt")
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("revoked-at");
|
||||
|
||||
b.Property<string>("TokenHash")
|
||||
.IsRequired()
|
||||
.HasMaxLength(128)
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("token-hash");
|
||||
|
||||
b.Property<int>("UserId")
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("user-id");
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("pk-api-refresh-token");
|
||||
|
||||
b.HasIndex("TokenHash")
|
||||
.IsUnique()
|
||||
.HasDatabaseName("idx-api-refresh-token-hash");
|
||||
|
||||
b.HasIndex("UserId")
|
||||
.HasDatabaseName("idx-api-refresh-token-user-id");
|
||||
|
||||
b.ToTable("api-refresh-token", t =>
|
||||
{
|
||||
t.HasComment("API refresh token");
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Avalonia_EFCore.Models.UserEntity", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("id")
|
||||
.HasComment("用户主键");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("created-at")
|
||||
.HasComment("创建时间");
|
||||
|
||||
b.Property<string>("Email")
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("email")
|
||||
.HasComment("用户邮箱");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("name")
|
||||
.HasComment("用户名称");
|
||||
|
||||
b.Property<string>("PasswordHash")
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("password-hash")
|
||||
.HasComment("密码哈希值");
|
||||
|
||||
b.Property<string>("PhoneNumber")
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("phone-number")
|
||||
.HasComment("电话号码");
|
||||
|
||||
b.Property<DateTime>("UpdatedAt")
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("updated-at")
|
||||
.HasComment("更新时间");
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("pk-user");
|
||||
|
||||
b.ToTable("user", t =>
|
||||
{
|
||||
t.HasComment("用户实体,演示数据库 CRUD 操作");
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Avalonia_EFCore.Models.WeatherForecastEntity", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("id")
|
||||
.HasComment("天气预报主键");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("created-at")
|
||||
.HasComment("创建时间");
|
||||
|
||||
b.Property<DateOnly>("Date")
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("date")
|
||||
.HasComment("预报日期");
|
||||
|
||||
b.Property<string>("Summary")
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("summary")
|
||||
.HasComment("天气摘要");
|
||||
|
||||
b.Property<int>("TemperatureC")
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("temperature-c")
|
||||
.HasComment("摄氏温度");
|
||||
|
||||
b.Property<DateTime>("UpdatedAt")
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("updated-at")
|
||||
.HasComment("更新时间");
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("pk-weather-forecast");
|
||||
|
||||
b.ToTable("weather-forecast", t =>
|
||||
{
|
||||
t.HasComment("天气预报数据实体");
|
||||
});
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
Generated
+184
@@ -0,0 +1,184 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using Avalonia_EFCore.Database;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Avalonia_EFCore.Migrations.SqlServer
|
||||
{
|
||||
[DbContext(typeof(SqlServerAppDataContext))]
|
||||
[Migration("20260520082607_AutoMigration_20260520162543")]
|
||||
partial class AutoMigration_20260520162543
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "10.0.7")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 128);
|
||||
|
||||
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
|
||||
|
||||
modelBuilder.Entity("Avalonia_EFCore.Models.ApiRefreshTokenEntity", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint")
|
||||
.HasColumnName("id");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("datetime2")
|
||||
.HasColumnName("created-at");
|
||||
|
||||
b.Property<string>("Device")
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("nvarchar(200)")
|
||||
.HasColumnName("device");
|
||||
|
||||
b.Property<DateTime>("ExpiresAt")
|
||||
.HasColumnType("datetime2")
|
||||
.HasColumnName("expires-at");
|
||||
|
||||
b.Property<string>("IpAddress")
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("nvarchar(64)")
|
||||
.HasColumnName("ip-address");
|
||||
|
||||
b.Property<string>("ReplacedByTokenHash")
|
||||
.HasMaxLength(128)
|
||||
.HasColumnType("nvarchar(128)")
|
||||
.HasColumnName("replaced-by-token-hash");
|
||||
|
||||
b.Property<DateTime?>("RevokedAt")
|
||||
.HasColumnType("datetime2")
|
||||
.HasColumnName("revoked-at");
|
||||
|
||||
b.Property<string>("TokenHash")
|
||||
.IsRequired()
|
||||
.HasMaxLength(128)
|
||||
.HasColumnType("nvarchar(128)")
|
||||
.HasColumnName("token-hash");
|
||||
|
||||
b.Property<int>("UserId")
|
||||
.HasColumnType("int")
|
||||
.HasColumnName("user-id");
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("pk-api-refresh-token");
|
||||
|
||||
b.HasIndex("TokenHash")
|
||||
.IsUnique()
|
||||
.HasDatabaseName("idx-api-refresh-token-hash");
|
||||
|
||||
b.HasIndex("UserId")
|
||||
.HasDatabaseName("idx-api-refresh-token-user-id");
|
||||
|
||||
b.ToTable("api-refresh-token", t =>
|
||||
{
|
||||
t.HasComment("API refresh token");
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Avalonia_EFCore.Models.UserEntity", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int")
|
||||
.HasColumnName("id")
|
||||
.HasComment("用户主键");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("datetime2")
|
||||
.HasColumnName("created-at")
|
||||
.HasComment("创建时间");
|
||||
|
||||
b.Property<string>("Email")
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("nvarchar(200)")
|
||||
.HasColumnName("email")
|
||||
.HasComment("用户邮箱");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)")
|
||||
.HasColumnName("name")
|
||||
.HasComment("用户名称");
|
||||
|
||||
b.Property<string>("PhoneNumber")
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("nvarchar(50)")
|
||||
.HasColumnName("phone-number")
|
||||
.HasComment("电话号码");
|
||||
|
||||
b.Property<DateTime>("UpdatedAt")
|
||||
.HasColumnType("datetime2")
|
||||
.HasColumnName("updated-at")
|
||||
.HasComment("更新时间");
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("pk-user");
|
||||
|
||||
b.ToTable("user", t =>
|
||||
{
|
||||
t.HasComment("用户实体,演示数据库 CRUD 操作");
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Avalonia_EFCore.Models.WeatherForecastEntity", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int")
|
||||
.HasColumnName("id")
|
||||
.HasComment("天气预报主键");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("datetime2")
|
||||
.HasColumnName("created-at")
|
||||
.HasComment("创建时间");
|
||||
|
||||
b.Property<DateOnly>("Date")
|
||||
.HasColumnType("date")
|
||||
.HasColumnName("date")
|
||||
.HasComment("预报日期");
|
||||
|
||||
b.Property<string>("Summary")
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("nvarchar(200)")
|
||||
.HasColumnName("summary")
|
||||
.HasComment("天气摘要");
|
||||
|
||||
b.Property<int>("TemperatureC")
|
||||
.HasColumnType("int")
|
||||
.HasColumnName("temperature-c")
|
||||
.HasComment("摄氏温度");
|
||||
|
||||
b.Property<DateTime>("UpdatedAt")
|
||||
.HasColumnType("datetime2")
|
||||
.HasColumnName("updated-at")
|
||||
.HasComment("更新时间");
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("pk-weather-forecast");
|
||||
|
||||
b.ToTable("weather-forecast", t =>
|
||||
{
|
||||
t.HasComment("天气预报数据实体");
|
||||
});
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Avalonia_EFCore.Migrations.SqlServer
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AutoMigration_20260520162543 : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "api-refresh-token",
|
||||
columns: table => new
|
||||
{
|
||||
id = table.Column<long>(type: "bigint", nullable: false)
|
||||
.Annotation("SqlServer:Identity", "1, 1"),
|
||||
userid = table.Column<int>(name: "user-id", type: "int", nullable: false),
|
||||
tokenhash = table.Column<string>(name: "token-hash", type: "nvarchar(128)", maxLength: 128, nullable: false),
|
||||
createdat = table.Column<DateTime>(name: "created-at", type: "datetime2", nullable: false),
|
||||
expiresat = table.Column<DateTime>(name: "expires-at", type: "datetime2", nullable: false),
|
||||
revokedat = table.Column<DateTime>(name: "revoked-at", type: "datetime2", nullable: true),
|
||||
replacedbytokenhash = table.Column<string>(name: "replaced-by-token-hash", type: "nvarchar(128)", maxLength: 128, nullable: true),
|
||||
device = table.Column<string>(type: "nvarchar(200)", maxLength: 200, nullable: true),
|
||||
ipaddress = table.Column<string>(name: "ip-address", type: "nvarchar(64)", maxLength: 64, nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("pk-api-refresh-token", x => x.id);
|
||||
},
|
||||
comment: "API refresh token");
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "user",
|
||||
columns: table => new
|
||||
{
|
||||
id = table.Column<int>(type: "int", nullable: false, comment: "用户主键")
|
||||
.Annotation("SqlServer:Identity", "1, 1"),
|
||||
name = table.Column<string>(type: "nvarchar(100)", maxLength: 100, nullable: true, comment: "用户名称"),
|
||||
email = table.Column<string>(type: "nvarchar(200)", maxLength: 200, nullable: true, comment: "用户邮箱"),
|
||||
phonenumber = table.Column<string>(name: "phone-number", type: "nvarchar(50)", maxLength: 50, nullable: true, comment: "电话号码"),
|
||||
createdat = table.Column<DateTime>(name: "created-at", type: "datetime2", nullable: false, comment: "创建时间"),
|
||||
updatedat = table.Column<DateTime>(name: "updated-at", type: "datetime2", nullable: false, comment: "更新时间")
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("pk-user", x => x.id);
|
||||
},
|
||||
comment: "用户实体,演示数据库 CRUD 操作");
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "weather-forecast",
|
||||
columns: table => new
|
||||
{
|
||||
id = table.Column<int>(type: "int", nullable: false, comment: "天气预报主键")
|
||||
.Annotation("SqlServer:Identity", "1, 1"),
|
||||
date = table.Column<DateOnly>(type: "date", nullable: false, comment: "预报日期"),
|
||||
temperaturec = table.Column<int>(name: "temperature-c", type: "int", nullable: false, comment: "摄氏温度"),
|
||||
summary = table.Column<string>(type: "nvarchar(200)", maxLength: 200, nullable: true, comment: "天气摘要"),
|
||||
createdat = table.Column<DateTime>(name: "created-at", type: "datetime2", nullable: false, comment: "创建时间"),
|
||||
updatedat = table.Column<DateTime>(name: "updated-at", type: "datetime2", nullable: false, comment: "更新时间")
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("pk-weather-forecast", x => x.id);
|
||||
},
|
||||
comment: "天气预报数据实体");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "idx-api-refresh-token-hash",
|
||||
table: "api-refresh-token",
|
||||
column: "token-hash",
|
||||
unique: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "idx-api-refresh-token-user-id",
|
||||
table: "api-refresh-token",
|
||||
column: "user-id");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "api-refresh-token");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "user");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "weather-forecast");
|
||||
}
|
||||
}
|
||||
}
|
||||
Generated
+190
@@ -0,0 +1,190 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using Avalonia_EFCore.Database;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Avalonia_EFCore.Migrations.SqlServer
|
||||
{
|
||||
[DbContext(typeof(SqlServerAppDataContext))]
|
||||
[Migration("20260520083242_AutoMigration_20260520163216")]
|
||||
partial class AutoMigration_20260520163216
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "10.0.7")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 128);
|
||||
|
||||
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
|
||||
|
||||
modelBuilder.Entity("Avalonia_EFCore.Models.ApiRefreshTokenEntity", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint")
|
||||
.HasColumnName("id");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("datetime2")
|
||||
.HasColumnName("created-at");
|
||||
|
||||
b.Property<string>("Device")
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("nvarchar(200)")
|
||||
.HasColumnName("device");
|
||||
|
||||
b.Property<DateTime>("ExpiresAt")
|
||||
.HasColumnType("datetime2")
|
||||
.HasColumnName("expires-at");
|
||||
|
||||
b.Property<string>("IpAddress")
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("nvarchar(64)")
|
||||
.HasColumnName("ip-address");
|
||||
|
||||
b.Property<string>("ReplacedByTokenHash")
|
||||
.HasMaxLength(128)
|
||||
.HasColumnType("nvarchar(128)")
|
||||
.HasColumnName("replaced-by-token-hash");
|
||||
|
||||
b.Property<DateTime?>("RevokedAt")
|
||||
.HasColumnType("datetime2")
|
||||
.HasColumnName("revoked-at");
|
||||
|
||||
b.Property<string>("TokenHash")
|
||||
.IsRequired()
|
||||
.HasMaxLength(128)
|
||||
.HasColumnType("nvarchar(128)")
|
||||
.HasColumnName("token-hash");
|
||||
|
||||
b.Property<int>("UserId")
|
||||
.HasColumnType("int")
|
||||
.HasColumnName("user-id");
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("pk-api-refresh-token");
|
||||
|
||||
b.HasIndex("TokenHash")
|
||||
.IsUnique()
|
||||
.HasDatabaseName("idx-api-refresh-token-hash");
|
||||
|
||||
b.HasIndex("UserId")
|
||||
.HasDatabaseName("idx-api-refresh-token-user-id");
|
||||
|
||||
b.ToTable("api-refresh-token", t =>
|
||||
{
|
||||
t.HasComment("API refresh token");
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Avalonia_EFCore.Models.UserEntity", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int")
|
||||
.HasColumnName("id")
|
||||
.HasComment("用户主键");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("datetime2")
|
||||
.HasColumnName("created-at")
|
||||
.HasComment("创建时间");
|
||||
|
||||
b.Property<string>("Email")
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("nvarchar(200)")
|
||||
.HasColumnName("email")
|
||||
.HasComment("用户邮箱");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)")
|
||||
.HasColumnName("name")
|
||||
.HasComment("用户名称");
|
||||
|
||||
b.Property<string>("PasswordHash")
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("nvarchar(200)")
|
||||
.HasColumnName("password-hash")
|
||||
.HasComment("密码哈希值");
|
||||
|
||||
b.Property<string>("PhoneNumber")
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("nvarchar(50)")
|
||||
.HasColumnName("phone-number")
|
||||
.HasComment("电话号码");
|
||||
|
||||
b.Property<DateTime>("UpdatedAt")
|
||||
.HasColumnType("datetime2")
|
||||
.HasColumnName("updated-at")
|
||||
.HasComment("更新时间");
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("pk-user");
|
||||
|
||||
b.ToTable("user", t =>
|
||||
{
|
||||
t.HasComment("用户实体,演示数据库 CRUD 操作");
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Avalonia_EFCore.Models.WeatherForecastEntity", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int")
|
||||
.HasColumnName("id")
|
||||
.HasComment("天气预报主键");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("datetime2")
|
||||
.HasColumnName("created-at")
|
||||
.HasComment("创建时间");
|
||||
|
||||
b.Property<DateOnly>("Date")
|
||||
.HasColumnType("date")
|
||||
.HasColumnName("date")
|
||||
.HasComment("预报日期");
|
||||
|
||||
b.Property<string>("Summary")
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("nvarchar(200)")
|
||||
.HasColumnName("summary")
|
||||
.HasComment("天气摘要");
|
||||
|
||||
b.Property<int>("TemperatureC")
|
||||
.HasColumnType("int")
|
||||
.HasColumnName("temperature-c")
|
||||
.HasComment("摄氏温度");
|
||||
|
||||
b.Property<DateTime>("UpdatedAt")
|
||||
.HasColumnType("datetime2")
|
||||
.HasColumnName("updated-at")
|
||||
.HasComment("更新时间");
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("pk-weather-forecast");
|
||||
|
||||
b.ToTable("weather-forecast", t =>
|
||||
{
|
||||
t.HasComment("天气预报数据实体");
|
||||
});
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Avalonia_EFCore.Migrations.SqlServer
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AutoMigration_20260520163216 : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "password-hash",
|
||||
table: "user",
|
||||
type: "nvarchar(200)",
|
||||
maxLength: 200,
|
||||
nullable: true,
|
||||
comment: "密码哈希值");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "password-hash",
|
||||
table: "user");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,187 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using Avalonia_EFCore.Database;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Avalonia_EFCore.Migrations.SqlServer
|
||||
{
|
||||
[DbContext(typeof(SqlServerAppDataContext))]
|
||||
partial class SqlServerAppDataContextModelSnapshot : ModelSnapshot
|
||||
{
|
||||
protected override void BuildModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "10.0.7")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 128);
|
||||
|
||||
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
|
||||
|
||||
modelBuilder.Entity("Avalonia_EFCore.Models.ApiRefreshTokenEntity", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint")
|
||||
.HasColumnName("id");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("datetime2")
|
||||
.HasColumnName("created-at");
|
||||
|
||||
b.Property<string>("Device")
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("nvarchar(200)")
|
||||
.HasColumnName("device");
|
||||
|
||||
b.Property<DateTime>("ExpiresAt")
|
||||
.HasColumnType("datetime2")
|
||||
.HasColumnName("expires-at");
|
||||
|
||||
b.Property<string>("IpAddress")
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("nvarchar(64)")
|
||||
.HasColumnName("ip-address");
|
||||
|
||||
b.Property<string>("ReplacedByTokenHash")
|
||||
.HasMaxLength(128)
|
||||
.HasColumnType("nvarchar(128)")
|
||||
.HasColumnName("replaced-by-token-hash");
|
||||
|
||||
b.Property<DateTime?>("RevokedAt")
|
||||
.HasColumnType("datetime2")
|
||||
.HasColumnName("revoked-at");
|
||||
|
||||
b.Property<string>("TokenHash")
|
||||
.IsRequired()
|
||||
.HasMaxLength(128)
|
||||
.HasColumnType("nvarchar(128)")
|
||||
.HasColumnName("token-hash");
|
||||
|
||||
b.Property<int>("UserId")
|
||||
.HasColumnType("int")
|
||||
.HasColumnName("user-id");
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("pk-api-refresh-token");
|
||||
|
||||
b.HasIndex("TokenHash")
|
||||
.IsUnique()
|
||||
.HasDatabaseName("idx-api-refresh-token-hash");
|
||||
|
||||
b.HasIndex("UserId")
|
||||
.HasDatabaseName("idx-api-refresh-token-user-id");
|
||||
|
||||
b.ToTable("api-refresh-token", t =>
|
||||
{
|
||||
t.HasComment("API refresh token");
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Avalonia_EFCore.Models.UserEntity", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int")
|
||||
.HasColumnName("id")
|
||||
.HasComment("用户主键");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("datetime2")
|
||||
.HasColumnName("created-at")
|
||||
.HasComment("创建时间");
|
||||
|
||||
b.Property<string>("Email")
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("nvarchar(200)")
|
||||
.HasColumnName("email")
|
||||
.HasComment("用户邮箱");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)")
|
||||
.HasColumnName("name")
|
||||
.HasComment("用户名称");
|
||||
|
||||
b.Property<string>("PasswordHash")
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("nvarchar(200)")
|
||||
.HasColumnName("password-hash")
|
||||
.HasComment("密码哈希值");
|
||||
|
||||
b.Property<string>("PhoneNumber")
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("nvarchar(50)")
|
||||
.HasColumnName("phone-number")
|
||||
.HasComment("电话号码");
|
||||
|
||||
b.Property<DateTime>("UpdatedAt")
|
||||
.HasColumnType("datetime2")
|
||||
.HasColumnName("updated-at")
|
||||
.HasComment("更新时间");
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("pk-user");
|
||||
|
||||
b.ToTable("user", t =>
|
||||
{
|
||||
t.HasComment("用户实体,演示数据库 CRUD 操作");
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Avalonia_EFCore.Models.WeatherForecastEntity", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int")
|
||||
.HasColumnName("id")
|
||||
.HasComment("天气预报主键");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("datetime2")
|
||||
.HasColumnName("created-at")
|
||||
.HasComment("创建时间");
|
||||
|
||||
b.Property<DateOnly>("Date")
|
||||
.HasColumnType("date")
|
||||
.HasColumnName("date")
|
||||
.HasComment("预报日期");
|
||||
|
||||
b.Property<string>("Summary")
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("nvarchar(200)")
|
||||
.HasColumnName("summary")
|
||||
.HasComment("天气摘要");
|
||||
|
||||
b.Property<int>("TemperatureC")
|
||||
.HasColumnType("int")
|
||||
.HasColumnName("temperature-c")
|
||||
.HasComment("摄氏温度");
|
||||
|
||||
b.Property<DateTime>("UpdatedAt")
|
||||
.HasColumnType("datetime2")
|
||||
.HasColumnName("updated-at")
|
||||
.HasComment("更新时间");
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("pk-weather-forecast");
|
||||
|
||||
b.ToTable("weather-forecast", t =>
|
||||
{
|
||||
t.HasComment("天气预报数据实体");
|
||||
});
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace Avalonia_EFCore.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// API refresh token。只保存哈希,不保存明文 token。
|
||||
/// </summary>
|
||||
[Comment("API refresh token")]
|
||||
[Table("api-refresh-token")]
|
||||
public class ApiRefreshTokenEntity
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取或设置主键 ID(自增)。
|
||||
/// </summary>
|
||||
[Key]
|
||||
[Column("id")]
|
||||
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||
public long Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取或设置关联的用户 ID。
|
||||
/// </summary>
|
||||
[Column("user-id")]
|
||||
public int UserId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取或设置 Token 的 SHA256 哈希值,用于安全存储和查询。
|
||||
/// </summary>
|
||||
[Column("token-hash")]
|
||||
[MaxLength(128)]
|
||||
public string TokenHash { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 获取或设置 Token 创建时间。
|
||||
/// </summary>
|
||||
[Column("created-at")]
|
||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||
|
||||
/// <summary>
|
||||
/// 获取或设置 Token 过期时间。
|
||||
/// </summary>
|
||||
[Column("expires-at")]
|
||||
public DateTime ExpiresAt { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取或设置 Token 撤销时间,null 表示尚未撤销。
|
||||
/// </summary>
|
||||
[Column("revoked-at")]
|
||||
public DateTime? RevokedAt { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取或设置替换此 Token 的新 Token 哈希值(轮换时设置)。
|
||||
/// </summary>
|
||||
[Column("replaced-by-token-hash")]
|
||||
[MaxLength(128)]
|
||||
public string? ReplacedByTokenHash { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取或设置创建设备标识(如 User-Agent)。
|
||||
/// </summary>
|
||||
[Column("device")]
|
||||
[MaxLength(200)]
|
||||
public string? Device { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取或设置创建时的客户端 IP 地址。
|
||||
/// </summary>
|
||||
[Column("ip-address")]
|
||||
[MaxLength(64)]
|
||||
public string? IpAddress { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取 Token 是否有效(未被撤销且未过期)。
|
||||
/// </summary>
|
||||
public bool IsActive => RevokedAt is null && ExpiresAt > DateTime.UtcNow;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace Avalonia_EFCore.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// 用户实体 —— 演示数据库 CRUD 操作。
|
||||
/// </summary>
|
||||
[Comment("用户实体,演示数据库 CRUD 操作")]
|
||||
[Table("user")]
|
||||
public class UserEntity
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取或设置用户主键 ID(自增)。
|
||||
/// </summary>
|
||||
[Key]
|
||||
[Comment("用户主键")]
|
||||
[Column("id")]
|
||||
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||
public int Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取或设置用户名称。
|
||||
/// </summary>
|
||||
[Comment("用户名称")]
|
||||
[Column("name")]
|
||||
[MaxLength(100)]
|
||||
public string? Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取或设置用户密码哈希值。
|
||||
/// </summary>
|
||||
[Comment("密码哈希值")]
|
||||
[Column("password-hash")]
|
||||
[MaxLength(200)]
|
||||
public string? PasswordHash { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取或设置用户邮箱。
|
||||
/// </summary>
|
||||
[Comment("用户邮箱")]
|
||||
[Column("email")]
|
||||
[MaxLength(200)]
|
||||
public string? Email { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取或设置用户电话号码。
|
||||
/// </summary>
|
||||
[Comment("电话号码")]
|
||||
[Column("phone-number")]
|
||||
[MaxLength(50)]
|
||||
public string? PhoneNumber { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取或设置用户创建时间。
|
||||
/// </summary>
|
||||
[Comment("创建时间")]
|
||||
[Column("created-at")]
|
||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||
|
||||
/// <summary>
|
||||
/// 获取或设置用户最后更新时间。
|
||||
/// </summary>
|
||||
[Comment("更新时间")]
|
||||
[Column("updated-at")]
|
||||
public DateTime UpdatedAt { get; set; } = DateTime.UtcNow;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
namespace Avalonia_EFCore.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// 天气预报数据模型(内存/DTO 用,非数据库实体)。
|
||||
/// </summary>
|
||||
public class WeatherForecast
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取或设置预报日期。
|
||||
/// </summary>
|
||||
public DateOnly Date { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取或设置摄氏温度。
|
||||
/// </summary>
|
||||
public int TemperatureC { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取华氏温度(根据摄氏温度自动计算)。
|
||||
/// </summary>
|
||||
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
|
||||
|
||||
/// <summary>
|
||||
/// 获取或设置天气摘要。
|
||||
/// </summary>
|
||||
public string? Summary { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace Avalonia_EFCore.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// 天气预报数据实体。
|
||||
/// </summary>
|
||||
[Comment("天气预报数据实体")]
|
||||
[Table("weather-forecast")]
|
||||
public class WeatherForecastEntity
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取或设置天气预报主键 ID(自增)。
|
||||
/// </summary>
|
||||
[Key]
|
||||
[Comment("天气预报主键")]
|
||||
[Column("id")]
|
||||
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||
public int Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取或设置预报日期。
|
||||
/// </summary>
|
||||
[Comment("预报日期")]
|
||||
[Column("date")]
|
||||
public DateOnly Date { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取或设置摄氏温度。
|
||||
/// </summary>
|
||||
[Comment("摄氏温度")]
|
||||
[Column("temperature-c")]
|
||||
public int TemperatureC { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取或设置天气摘要。
|
||||
/// </summary>
|
||||
[Comment("天气摘要")]
|
||||
[Column("summary")]
|
||||
[MaxLength(200)]
|
||||
public string? Summary { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取或设置记录创建时间。
|
||||
/// </summary>
|
||||
[Comment("创建时间")]
|
||||
[Column("created-at")]
|
||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||
|
||||
/// <summary>
|
||||
/// 获取或设置记录最后更新时间。
|
||||
/// </summary>
|
||||
[Comment("更新时间")]
|
||||
[Column("updated-at")]
|
||||
public DateTime UpdatedAt { get; set; } = DateTime.UtcNow;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user