我们继续上一个案例,实现一个可以修改当前用户信息功能。
当用户点击某个信息时,跳转到信息详情页,然后可以点击编辑按钮导航到编辑页面。
我们首先在ViewModels目录下创建UserDetailViewModel。
实现从详情信息页面导航到编辑页面。
这里要使用一个字典来传输对象。
public partial class UserDetailViewModel:ObservableObject,IQueryAttributable
{
[ObservableProperty] private User itemUser;
public Func ParentRefreshAction { get;set; }
[RelayCommand]
async Task ShowEditFormAsync()
{
var context = new UserContext();
var editedItem = context.Users.FirstOrDefault(u => u.Id == ItemUser.Id);
await Shell.Current.GoToAsync(nameof(UserEditPage),
parameters: new Dictionary
{
{ "ParentRefreshAction", (Func)ItemEditedAsync },
{ "Item", editedItem }
});
}
async Task ItemEditedAsync(User user) {
ItemUser = user;
await ParentRefreshAction(user);
}
public virtual void ApplyQueryAttributes(IDictionary query)
{
if (query.TryGetValue("Item",out object currentItem))
{
ItemUser = (User)currentItem;
}
if (query.TryGetValue("ParentRefreshAction",out object parentRefreshAction))
{
ParentRefreshAction = (Func)parentRefreshAction;
}
query.Clear();
}
}
创建用户详情页面,用来显示用户的全部信息。
在ToolbarItem中添加一个命令导航到编辑页面。
更新UserEditViewModel,我们让他直接继承自UserDetailViewModel.
public partial class UserEditViewModel:UserDetailViewModel
{
[ObservableProperty] private bool isNewItem;
[RelayCommand]
private async Task SaveAsync()
{
await using var context = new UserContext();
if (IsNewItem)
{
context.Users.Add(ItemUser);
}
else
{
context.Users.Attach(ItemUser);
context.Entry(ItemUser).State = Microsoft.EntityFrameworkCore.EntityState.Modified;
}
context.SaveChangesAsync();
await ParentRefreshAction(ItemUser);
await Shell.Current.GoToAsync("..");
}
public override void ApplyQueryAttributes(IDictionary query) {
if (query.TryGetValue("IsNewItem", out object isNew)) {
IsNewItem = (bool)isNew;
}
base.ApplyQueryAttributes(query);
}
}
修改MainViewModel。
public partial class MainViewModel:ObservableObject
{
[ObservableProperty]
ObservableCollection users;
[ObservableProperty] private bool refreshing;
[RelayCommand]
private async Task LoadUsersAsync()
{
await Task.Run(() =>
{
using var context = new UserContext();
Users = new ObservableCollection(context.Users);
});
Refreshing = false;
}
[RelayCommand]
private void Showing()
{
Refreshing = true;
}
[RelayCommand]
private void DeleteUser(User user)
{
var context = new UserContext();
context.Users.Remove(user);
context.SaveChanges();
Users.Remove(user);
}
[RelayCommand]
private async Task ShowNewFormAsync()
{
await Shell.Current.GoToAsync(nameof(UserEditPage),parameters:new Dictionary
{
{"ParentRefreshAction",(Func)RefreshAddedAsync},
{"Item",new User()},
{"IsNewItem",true}
});
}
Task RefreshAddedAsync(User addedUser)
{
Users.Add(addedUser);
return Task.CompletedTask;
}
[RelayCommand]
async Task ShowDetailFormAsync(User user)
{
await Shell.Current.GoToAsync(nameof(UserDetailPage),parameters:new Dictionary
{
{"ParentRefreshAction",(Func)RefreshEditedAsync},
{"Item",user},
});
}
async Task RefreshEditedAsync(User updataUser)
{
int editedItemIndex = -1;
await Task.Run(() =>
{
editedItemIndex = Users.Select(
(user, index) => new { user, index })
.First(x => x.user.Id == updataUser.Id)
.index;
});
if (editedItemIndex==-1)
{
return;
}
Users[editedItemIndex] = updataUser;
}
}
注册路由
public partial class AppShell : Shell
{
public AppShell()
{
InitializeComponent();
Routing.RegisterRoute(nameof(UserEditPage),typeof(UserEditPage));
Routing.RegisterRoute(nameof(UserDetailPage),typeof(UserDetailPage));
}
}
在主页添加GestureRecognizers,对详情页面的跳转
我们实现了查看用户详情,并且可以修改
我们要在数据库和视图模型之间提供一个抽象层,它能使项目有不同的模块区分,更明确的分离开。
在项目中往往需要对多张表进行操作,我们创建一个泛型接口,来抽象对数据库中CURD。
在Models中添加一个IRepository.cs文件
public interface IRepository where T:class
{
Task GetByIdAsync(int id);
Task> GetAllAsync();
Task AddAsync(T item);
Task UpdateAsync(T item);
Task DeleteAsync(T item);
}
实现接口
public class UserRepository: IRepository
{
private readonly DbSet DbSet;
private readonly UserContext Context;
public UserRepository(UserContext context)
{
Context = context;
DbSet = Context.Set();
}
public async Task GetByIdAsync(int id)
{
return await Task.Run(() => DbSet.Find(id));
}
public async Task> GetAllAsync()
{
return await Task.Run(() => DbSet.ToList());
}
public async Task AddAsync(User item)
{
DbSet.Add(item);
await Task.CompletedTask;
}
public async Task UpdateAsync(User item)
{
DbSet.Attach(item);
Context.Entry(item).State = EntityState.Modified;
await Task.CompletedTask;
}
public async Task DeleteAsync(User item)
{
DbSet.Remove(item);
await Task.CompletedTask;
}
创建工作单元,它的作用主要是作用于不同的数据表。
public class DbUnitOfWork:IDisposable,IUnitOfWork
{
readonly UserContext Context=new UserContext();
private IRepository userRepository;
public IRepository Items => userRepository ??= new UserRepository(Context);
public void Dispose()
{
Context.Dispose();
}
public async Task SaveAsync()
{
await Task.Run(() => Context.SaveChangesAsync());
}
}
public interface IUnitOfWork where T : class {
IRepository Items { get; }
Task SaveAsync();
}
LoadUserAsync
[RelayCommand]
private async Task LoadUsersAsync()
{
using var uniOfWork = new DbUnitOfWork();
Users = new ObservableCollection(await uniOfWork.Items.GetAllAsync());
Refreshing = false;
}
DeleteUserAsync
[RelayCommand]
private async Task DeleteUser(User user)
{
using var uniOfWork = new DbUnitOfWork();
await uniOfWork.Items.DeleteAsync(user);
await uniOfWork.SaveAsync();
Users.Remove(user);
}
SaveAsync
[RelayCommand]
private async Task SaveAsync()
{
using var unitOfWork = new DbUnitOfWork();
if (IsNewItem)
await unitOfWork.Items.AddAsync(ItemUser);
else
await unitOfWork.Items.UpdateAsync(ItemUser);
await unitOfWork.SaveAsync();
await ParentRefreshAction(ItemUser);
await Shell.Current.GoToAsync("..");
}
ShowEditFormAsync
[RelayCommand]
async Task ShowEditFormAsync()
{
using var unitOfWork = new DbUnitOfWork();
var editedItem = await unitOfWork.Items.GetByIdAsync(ItemUser.Id);
await Shell.Current.GoToAsync(nameof(UserEditPage),
parameters: new Dictionary
{
{ "ParentRefreshAction", (Func)ItemEditedAsync },
{ "Item", editedItem }
});
}
很多时候我们需要对用户输入的数据进行验证,有很多方法和形式,我们来看看在数据库层面如何做错误验证处理。并反馈在页面给用户。
我们在UserContext中进行简单的数据约束
使用try catch来捕获异常
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
//用户邮箱唯一
modelBuilder.Entity().HasIndex(u => u.Email).IsUnique();
//用户名不能为空
modelBuilder.Entity().Property(u => u.Name).IsRequired();
//初始化数据
modelBuilder.Entity().HasData(new User
{
Id = 1,
Name = "张三",
Email = "张三@163.com",
Phone = "123456789"
});
base.OnModelCreating(modelBuilder);
}
DeleteCustomerAsync
[RelayCommand]
private async Task DeleteUserAsync(User user)
{
using var uniOfWork = new DbUnitOfWork();
try
{
await uniOfWork.Items.DeleteAsync(user);
await uniOfWork.SaveAsync();
}
catch (Exception ex)
{
await Shell.Current.DisplayAlert("Error", ex.Message, "OK");
return;
}
Users.Remove(user);
}
SaveAsync
[RelayCommand]
private async Task SaveAsync()
{
using var unitOfWork = new DbUnitOfWork();
try
{
if (IsNewItem)
await unitOfWork.Items.AddAsync(ItemUser);
else
await unitOfWork.Items.UpdateAsync(ItemUser);
await unitOfWork.SaveAsync();
}
catch (Exception ex)
{
await Shell.Current.DisplayAlert("Error", ex.Message, "OK");
return;
}
await ParentRefreshAction(ItemUser);
await Shell.Current.GoToAsync("..");
}
这里如果用户没有添加用户名,程序会提出错误信息。
程序中,在将数据提交到数据库之前可以在UI层执行某些验证规则,可以在用户保存某些修改时告知用户,从而改善用户体验。
在编辑用户页面我们添加对用户名和邮箱的验证,并关联到保存命令上。
[NotifyCanExecuteChangedFor(nameof(SaveCommand))] [ObservableProperty]
private bool isEmailValid;
[NotifyCanExecuteChangedFor(nameof(SaveCommand))] [ObservableProperty]
private bool isNameValid;
bool CanSave() => IsEmailValid&& IsNameValid;
[RelayCommand(CanExecute = nameof(CanSave))]
private async Task SaveAsync()
{
using var unitOfWork = new DbUnitOfWork();
try
{
if (IsNewItem)
await unitOfWork.Items.AddAsync(ItemUser);
else
await unitOfWork.Items.UpdateAsync(ItemUser);
await unitOfWork.SaveAsync();
}
catch (Exception ex)
{
await Shell.Current.DisplayAlert("Error", ex.Message, "OK");
return;
}
await ParentRefreshAction(ItemUser);
await Shell.Current.GoToAsync("..");
}
在UserEditPage文件中,我们添加一个样式来反馈给用户,并使用工具包中的ValidationBehavior来进行验证和绑定命令。
这个验证很简单,当用户输入的内容不满足条件时,会使用我们设置的样式颜色来显示,保存按钮无法点击,当满足条件时颜色变为正常并可以保存内容。
//验证是否时正常的邮箱格式