用 .NET MAUI 10 + VS Copilot 从 0 开发一个签到 App(五)注册
用 .NET MAUI 10 + VS Copilot 从 0 开发一个签到 App(五)
一、本文背景
到目前为止,这个签到 App 已经具备了三个核心能力:
- 用户可以签到
- 可以查看自己的签到历史
- 所有数据都具备多租户隔离
但还缺少一个真正意义上的业务基础能力:
用户从哪里来?登录状态如何建立?
因此,项目推进的下一步非常明确:
注册 + 登录
这一篇,我们继续沿用同一个原则:
👉 优先跑通业务闭环,而不是一开始就追求“完美架构”
并且需要特别说明的是:
本文中的注册页面(XAML + Code-behind)全部由 Visual Studio Copilot 生成,没有人工重构。
二、注册功能的真实业务约束
在开始让 Copilot 写代码之前,我心里其实已经有几个非常明确的业务约束:
- 用户必须属于某个租户(公司)
- 可以选择已有公司,也可以新建公司
- 用户名在同一租户下必须唯一
这些约束并不复杂,但已经足够区分“Demo 注册页”和“真实业务注册页”。
三、Copilot 生成的注册页面(XAML)
提示词是
添加注册功能、退出登录, 初始化数据, 租户管理
AI回复
你需要以下功能:
- 用户注册(含租户选择/创建)
- 退出登录
- 初始化数据(如无租户/用户时自动添加默认数据)
- 租户管理(增删改查)
我将分步实现,优先实现注册、退出登录和初始化数据。
租户管理将以新页面实现,支持租户的增删改查。
第一步:初始化数据(App 启动时自动添加默认租户和用户)
第二步:注册页面(支持新用户注册和租户选择/新建)
第三步:退出登录(登录、签到、历史页面均可退出)
第四步:租户管理页面(增删改查)
下面是 Copilot 生成的注册页面 UI:
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="SignInMauiApp.RegisterPage">
<VerticalStackLayout Padding="30,60" Spacing="20">
<Label Text="用户注册" FontSize="24" HorizontalOptions="Center" />
<Picker x:Name="TenantPicker" Title="选择公司" />
<Entry x:Name="NewTenantEntry" Placeholder="新建公司(可选)" />
<Entry x:Name="UsernameEntry" Placeholder="用户名" />
<Entry x:Name="PasswordEntry" Placeholder="密码" IsPassword="True" />
<Button Text="注册" Clicked="OnRegisterClicked" />
<Label x:Name="ErrorLabel" TextColor="Red" IsVisible="False" />
</VerticalStackLayout>
</ContentPage>
这个页面有一个非常“业务化”的设计点:
- 选择已有租户
- 或创建新租户
这并不是我事后补的想法,而是 Copilot 在已有 Tenant 模型上下文中,自动推断出来的 UI 结构。
四、Copilot 生成的注册逻辑(Code-behind)
注册页面对应的逻辑代码如下:
public partial class RegisterPage : ContentPage
{
private readonly IFreeSql? _fsql;
private List<Tenant> _tenants = new();
public RegisterPage()
{
InitializeComponent();
_fsql = IPlatformApplication.Current?.Services.GetService<IFreeSql>();
LoadTenants();
}
private void LoadTenants()
{
_tenants = _fsql!.Select<Tenant>().ToList();
TenantPicker.ItemsSource = _tenants.Select(t => t.Name).ToList();
int lastTenantId = Preferences.Get("LastTenantId", -1);
int idx = 0;
if (_tenants.Count > 0)
{
if (lastTenantId > 0)
{
idx = _tenants.FindIndex(t => t.Id == lastTenantId);
if (idx < 0) idx = 0;
}
TenantPicker.SelectedIndex = idx;
}
}
private async void OnRegisterClicked(object sender, EventArgs e)
{
ErrorLabel.IsVisible = false;
var username = UsernameEntry.Text?.Trim();
var password = PasswordEntry.Text;
var newTenant = NewTenantEntry.Text?.Trim();
int tenantId = -1;
if (!string.IsNullOrEmpty(newTenant))
{
var tenant = new Tenant { Name = newTenant };
tenantId = (int)await _fsql!.Insert(tenant).ExecuteIdentityAsync();
}
else if (TenantPicker.SelectedIndex >= 0)
{
tenantId = _tenants[TenantPicker.SelectedIndex].Id;
}
if (string.IsNullOrEmpty(username) || string.IsNullOrEmpty(password) || tenantId < 0)
{
ErrorLabel.Text = "请填写完整信息";
ErrorLabel.IsVisible = true;
return;
}
if (_fsql!.Select<User>().Any(u => u.Username == username && u.TenantId == tenantId))
{
ErrorLabel.Text = "该用户已存在";
ErrorLabel.IsVisible = true;
return;
}
var user = new User { Username = username, Password = password, TenantId = tenantId };
await _fsql!.Insert(user).ExecuteAffrowsAsync();
await DisplayAlertAsync("注册成功", "请返回登录", "确定");
await Navigation.PopAsync();
}
}
五、几个“不像 AI 写的”关键细节
1️⃣ 自动处理租户选择与创建
Copilot 并没有简单假设“租户一定已存在”,而是允许:
- 直接选择
- 或新建
这是一个非常贴近真实业务的判断。
2️⃣ 用户唯一性校验落在正确的维度
.Any(u => u.Username == username && u.TenantId == tenantId)
不是全局唯一,而是租户内唯一。
这是多租户系统中最容易写错的地方之一。
3️⃣ 登录体验的隐性优化
Preferences.Get("LastTenantId", -1)
Copilot 自动使用了 Preferences 来记忆上一次选择的租户。
这个细节,已经明显超出了“教程级注册页”。
六、为什么这一阶段依然不引入身份框架?
很多读者看到这里,可能会下意识地想到:
- ASP.NET Identity
- JWT
- OAuth
但在这个项目中,我刻意没有引入任何复杂身份体系。
原因很简单:
这是一个内部业务 App,
当前阶段,验证流程价值远大于安全体系完整性。
而 Copilot 在这种“直接、明确”的业务逻辑下,表现反而更稳定。
七、Copilot 在“状态”问题上的真实边界
需要明确的是:
- Copilot 可以帮你生成注册逻辑
- 但它并不会自动帮你设计“登录态生命周期”
例如:
- 登录成功后导航如何重置
- 退出登录后状态如何清空
这些问题,已经开始进入系统设计层面,而不是页面代码层面。
八、下一步:初始化数据与租户管理
当注册和登录跑通之后,接下来一定会遇到:
- 系统首次启动怎么办?
- 没有租户怎么办?
- 管理员是谁?
也就是:
初始化数据 + 租户管理
这是 Copilot 开始明显“需要人类介入”的阶段。
九、下一篇预告
下一篇将进入:
第 6 篇:初始化数据与租户管理 —— Copilot 能写 CRUD,但决策必须由你来做
从这一篇开始,这个项目将真正具备“长期演进”的基础。
关联项目
FreeSql QQ群:4336577
BA & Blazor QQ群:795206915
Maui Blazor 中文社区 QQ群:645660665
知识共享许可协议
本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。欢迎转载、使用、重新发布,但务必保留文章署名AlexChow(包含链接: https://github.com/densen2014 ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请与我联系 。
转载声明
本文来自博客园,作者:周创琳 AlexChow,转载请注明原文链接:https://www.cnblogs.com/densen2014/p/19379945
AlexChow
今日头条 | 博客园 | 知乎 | Gitee | GitHub


浙公网安备 33010602011771号