学习笔记(Maui 02 Sqlite)

Maui 学习笔记(2)

MVVM + IService 架构下的数据服务(对应P3-P6)

MVVM + IService 架构下程序结构与功能

  • Model:数据(Models文件夹)
  • View:显示数据(Views文件夹)
  • ViewModel:准备显示数据(ViewModels文件夹)
  • IService: 数据库服务操作(Services文件夹)

1 键值存储:偏好存储

1.1 键值存储的接口

Services 文件夹内创建接口 IStorageKeyValue,定义键值存取相关操作。

public interface IStorageKeyValue
{
	string Get(string key, string defaultValue);	// 读数据
    void Set(string key, string value);             // 写数据
}

1.2 键值存储的实现

Services 文件夹内创建类 StorageKeyValue,实现键值存取相关操作。

public class StorageKeyValue : IStorageKeyValue
{
	public string Get(string key, string defaultValue)
    {
    	return Preferences.Get(key, defaultValue);	
    }
    
    public void Set(string key, string value)
    {
    	Preferecnes.Set(key, value);
    }
}

Preferences 由 Maui 提供,实现键值存储。

1.3 ViewModel 与 StorageKeyValue 的连接:依赖注入

在 ServiceLocator 中实现依赖注入的注册

serviceCollection.AddSingleton();

ViewModel 中的 IStorageKeyValue 调用

private IStorageKeyValue _StroageKeyValue;
public ViewModelMainPage(IStorageKeyValue storageKeyValue)		// 用构造函数实现依赖注入的解析
{
	_StorageKeyValue = storagekeyValue;
}
  • 接口与实现不是一对一的情况,依赖注入的解析问题?
  • ViewModel 必须通过服务接口获得服务,不能直接调用服务 ———— 单元测试的需要 + 应对用户需求改变

2 嵌入式数据库 Sqlite :传统数据库

2.1 设计“表”对应的类

Nuget 安装包 sqlite-net-pcl, Models 文件夹下生成类 Poetry

public class Poetry
{
	[PrimaryKey, AutoIncrement]						// 选择主键 Id,并且自增
	public int Id { get; set;}
    public string Title { get; set;}
    public string Content { get; set;}
}
  • 该类用于生成数据库的一张表
  • Model 只承载数据,无业务代码

2.2 数据库存储的接口

Services 文件夹内创建接口 IStoragePoetry,定义数据库存取相关操作。
理想化的接口实现

public interface IStoragePoetry
{
	void Initialize();							// 建库
    void Add(Poetry poetry);	  				// 存
    IEnumerable List();      		    // 全取   
}

考虑异步机制以后,现实化的接口实现改为

public interface IStoragePoetry
{
	Task InitializeAsync();						// 建库
    Task AddAsync(Poetry poetry);	  			// 存
    Task> ListAsync();      // 全取   
}

接口设计必须考虑实现

2.3 数据库存储的实现

Services 文件夹内创建类 StoragePoetry,实现数据库存取相关操作。

public class StoragePoetry : IStoragePoetry
{
	// 数据库路径
	public const string DbFileName = "poetrydb.sqlite3";
    public static readonly string DbPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), DbFileName);    	// .Net 提供跨平台位置获取机制,可以安全读写数据

	// 定义并打开数据库连接
	private SQLiteAsyncCoonection _Connection;
    public SQLiteAsyncCoonection Connection =>
    _Connection ??= new SQLiteAsyncCoonection(PoetryDbPath);
    
	public async Task InitializeAsync()
    {
    	await Connection.CreateTableAsync();		// 创建 Poetry 表
    }
    
    public async Task AddAsync(Poetry poetry)
    {
    	await Connection.InsertAsync(poetry);
    }
    
    public async Task> ListAsync()
    {
    	return await Connection.Table().ToListAsync();
    }
}
  • 嵌入式数据库保存在文件(xxx.sqlite)里
  • 跨平台开发必须考虑平台兼容性,选用操作系统无关的标准化技术
  • const 常量,编译时确定;readonly 常量,运行时确定;static 类的,而不是对象的,全局的
  • await 确保 Async 函数按预期运行,函数必须用 async 标记,返回值改为 Task 类型
  • 函数名的 Async 是约定,非法定
  • 函数签名:名字、返回值、参数

2.4 ViewModel 与 StoragePoetry 的连接:依赖注入

在 ServiceLocator 中实现依赖注入的注册

serviceCollection.AddSingleton();

ViewModel 中的 IStoragePoetry 调用

public class ViewModelMainPage : ObservableObject
{
    private IStoragePoetry _StoragePoetry;
    public ViewModelMainPage(IStoragePoetry storagePoetry)				// 用构造函数实现依赖注入的解析
    {
        _StoragePoetry = storagePoetry ;
    }
    
    public ObservableCollection Poetries { get; } = new ();		 // 属性定义语法糖
    
    private RelayCommand _CommandInitial;
    public RelayCommand CommandInitial => _CommandInitial ??= new RelayCommand(async () => 
    {
    	await _StoragePoetry.InitializeAsync();
    }); 
    
    private RelayCommand _CommandAdd;
    public RelayCommand CommandAdd => _CommandAdd ??= new RelayCommand(async () => 
    {        	
    	await _StoragePoetry.AddAsync(new Poetry 
        {
        	Title = "title", 
            Content = "content"
        });
    }); 
    
    private RelayCommand _CommandList;
    public RelayCommand CommandList => _CommandList ??= new RelayCommand(async () => 
    {
    	var list = await _StoragePoetry.ListAsync();
        foreach (var p in list)
        {
        	Poetries.Add(p);
        }
    }); 
}    
  • ObservableCollection 自带 MVVM 功能
  • VS 有自带的代码模板功能

2.5 UI 设计

2.5.1 布局控件

参考 maui 官方文档
“*”父控件有多少占多少;“auto”自己有多少占多少
常规操作,略

2.5.2 ListView 显示集合数据

显示类名


显示数据



	
    	
        	
            	
        
    


  • 自动根据上下文绑定,布局中上下文在变化
  • Padding 衬距对内部;Margin 边距对外部

2.6 将 Title 和 Content 绑定的办法

2.6.1 拼接属性

在类 Poetry 中添加

[Ignore]												// 不进入数据库
public string TitleContent => Title + Content;
2.6.2 属性转换器

后面讲

2.6.3 适配器模式

添加 Wrapper 类

public class WrapperPoetry
{
	// 包装 Poetry,实现额外功能
}

3 Web 服务:远程数据

3.1 Web 服务的接口

Services 文件夹内创建接口 IServiceToken,定义 Web 服务相关操作。

public interface IServiceToken
{
	Task GetTokenAsync();       
}

3.2 Web 服务的实现

Services 文件夹内创建类 ServiceToken,实现 Web 服务相关操作。

public class ServiceToken : IServiceToken
{
	public async Task GetTokenAsync()
    {
    	var httpClient = new HttpClient();
        var response = await httpClient.GetAsync("https://v2.jinrishici.com/token");            
        var json = await response.Content.ReadAsStringAsync();
        
        return json;
    }
}

3.3 ViewModel 与 ServiceToken 的连接:依赖注入

在 ServiceLocator 中实现依赖注入的注册

serviceCollection.AddSingleton();

ViewModel 中的 IServiceToken 调用

public class ViewModelMainPage : ObservableObject
{
    private IServiceToken _ServiceToken;
    public ViewModelMainPage(IServiceToken serviceToken)		// 用构造函数实现依赖注入的解析
    {            
        _ServiceToken = serviceToken;
    }
    
	private string _Json;
    public string Json
    {
    	get => _Json;
        set => SetProperty(ref _Json, value);
    }
	
    private RelayCommand _CommandLoadJson;
    public RelayCommand CommandLoadJson => _CommandLoadJson ??= new RelayCommand(async ()=> 
    {
    	Json = await _ServiceToken.GetTokenAsync();
    });
}

你可能感兴趣的:(笔记,.net)