读写数据库 如何在Avalonia中建立数据库连接,如何在数据库中插入数据
Mvvm设计模式:Model、ViewModel、View 客户端开发的一种设计模式(MVC是服务端开发的设计模式)
从Model开始,添加新类作为模型类 1 2 3 4 5 public class Poetry { public int Id { get ; set ; } public string Name { get ; set ; } = string .Empty; }
service层,添加新类作为服务类 接口IPoetryStorage,实现类PoetryStorage 1 2 3 public interface IPoetryStorage { Task InsertAsync (Poetry poetry ) ; }
这是一个异步操作,函数名以Async结尾,返回值是Task,参数是Poetry类型的对象异步操作:不会阻塞当前线程,而是在另一个线程上执行,执行完毕后会通知当前线程
什么时候用异步操作? 当操作比较耗时的时候,比如读写文件、读写数据库、网络请求
1 2 3 4 public class PoetryStorage : IPoetryStorage { public async Task InsertAsync (Poetry poetry ) { throw new NotImplementedException(); }
.NET代码优先,code first,怎么把数据库表建起来? 根据模型类建立数据库表
运行在服务器,数据库在服务器上,但是我们的代码运行在用户的本机上,怎么让我们的代码和数据库建立连接?
嵌入式数据库,SQLite 行业标准 程序的一部分,不需要安装,不需要配置,不需要启动
通过NuGet安装sqlite-net-pcl
确定数据库怎么找,先确定数据库的文件名,在PoetryStorage类中添加一个常量1 public const string DbName = "poetrydb.sqlite3" ;
重点问题:放在哪?放在哪个目录下? 放用户总目录下,有一个位置专门存放应用程序
新建Helpers文件夹,添加一个新类PathHelper 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 public static class PathHelper { private static string _localFolder = string .Empty; private static string LocalFolder { get { if (!string .IsNullOrEmpty(_localFolder)) { return _localFolder; } _localFolder = Path.Combine( Environment.GetFolderPath(Environment.SpecialFolder .LocalApplicationData), nameof (Mvvm)); if (!Directory.Exists(_localFolder)) { Directory.CreateDirectory(_localFolder); } return _localFolder; } } public static string GetLocalFilePath (string fileName ) { return Path.Combine(LocalFolder, fileName); } }
回到PoetryStorage类,可以获取数据库文件的完整路径 1 2 public static readonly string PoetryDbPath = PathHelper.GetLocalFilePath(DbName);
public const string 和 public static readonly string 的区别?
public const string 是常量,编译时就确定了值,不能修改
public static readonly string 是只读的,运行时确定值,可以修改
建立数据库连接 1 2 3 4 5 private SQLiteAsyncConnection _connection;private SQLiteAsyncConnection Connection => _connection ??= new SQLiteAsyncConnection(PoetryDbPath);
??= 是什么意思? 空合并运算符,左边的值为空时,才会计算右边的值
建立数据库表
现在IPoetryStorage接口中添加一个新方法
在PoetryStorage类中实现这个方法,根据模型类建立数据库表,不用写SQL语句1 2 3 public async Task InitializeAsync () { await Connection.CreateTableAsync<Poetry>(); }
初探MVVM 通过像数据库中插入数据,来学习MVVM设计模式,Avolonia是如何把UI连起来的
实现插入诗词的功能,在PoetryStorage类中实现InsertAsync方法 (Service层) 1 2 3 public async Task InsertAsync (Poetry poetry ) { await Connection.InsertAsync(poetry); }
软件是分层的,每一层都有自己的职责,UI层(View)只负责显示,业务逻辑层(ViewModel)负责业务逻辑,Service层负责数据存取,Model层负责数据结构
向上一层,添加一个新类PoetryViewModel ViewModel中的属性,是UI中的数据源,UI中的控件绑定到ViewModel中的属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 public partial class MainWindowViewModel : ViewModelBase { private readonly IPoetryStorage _poetryStorage; public MainWindowViewModel (IPoetryStorage poetryStorage ) { _poetryStorage = poetryStorage; } private string _message; public string Message { get => _message; set => SetProperty(ref _message, value ); } public void SayHello () { Message = "Hello, Avalonia!" ; } }
基本CURD 单元测试 Mock Mock2 Behaviors 无限滚动及其原理 MVVM 访问Json Web服务 MVVM + IService架构 导航的原理 Avalonia.Samples-main\src\Avalonia.Samples\Routing\BasicViewLocatorSample
从导航的按钮出发 MainWindow.axaml 1 <Button Command="{Binding NavigateNextCommand}" Content="Next" />
NavigateNextCommand是什么? 1 NavigateNextCommand = ReactiveCommand.Create(NavigateNext, canNavNext);
NavigateNext是什么? 1 2 3 4 5 6 7 8 private void NavigateNext (){ var index = Pages.IndexOf(CurrentPage) + 1 ; CurrentPage = Pages[index]; }
Pages和CurrentPage是什么? 1 2 3 4 5 public PageViewModelBase CurrentPage{ get { return _CurrentPage; } private set { this .RaiseAndSetIfChanged(ref _CurrentPage, value ); } }
CurrentPage是PageViewModelBase类型的对象,继承于ViewModelBase,也就是说CurrentPage是一个ViewModel1 2 3 4 5 6 7 private readonly PageViewModelBase[] Pages = { new FirstPageViewModel(), new SecondPageViewModel(), new ThirdPageViewModel() };
Pages是一个数组,数组中的元素是PageViewModelBase类型的对象,也就是说Pages是一个ViewModel数组
怎么做到把ViewModel一改变,View就跟着改变?
在MainWindow.axaml中,有一个TransitioningContentControl控件,但只是用来呈现内容的,不是用来导航的 1 <TransitioningContentControl Content="{Binding CurrentPage}" />
有一个抽象的机制ViewLocater 这里有一个函数Match,每次对按钮赋值Content的时候,都会调用这个函数
1 2 3 4 public bool Match (object ? data ){ return data is ViewModelBase; }
打了三次断点:Back、Next、FirstPageViewModel
这个函数用来判断当前的ViewModel是否匹配,如果匹配就返回true,否则返回false
返回true之后跳到了上面的方法Build
也就是说只有content的赋值为ViewModelBase子类的时候,才会调用Build方法1 var name = data.GetType().FullName!.Replace("ViewModel" , "View" );
通过ViewModel的类型,找到对应的View的类型
通过反射,找到View的类型,然后实例化View1 2 3 4 5 var type = Type.GetType(name);if (type != null ){ return (Control)Activator.CreateInstance(type)!; }
返回一个View的实例给了MainWindow.axaml中的TransitioningContentControl控件的Content