如何在SQLite数据库存储并取回DatePicker和TimePicker控件的值
问题描述
我需要让用户通过WinUI 3的DatePicker/TimePicker选择时间后,将数据存储到SQLite数据库中,但SQLite不支持控件使用的DateTimeOffset格式。我希望转换格式时不丢失任何数据,并且从数据库取回后能重新绑定到DatePicker/TimePicker控件。当前使用WinUI 3和Entity Framework Core。
XAML代码
<DatePicker x:Name="StartDatePicker" Header="Start Date" Grid.Row="0" Grid.Column="0" /> <TimePicker x:Name="StartTimePicker" Header="Start Time" Grid.Row="0" Grid.Column="1"/> <DatePicker x:Name="EndDatePicker" Header="End Date" Grid.Row="1" Grid.Column="0" /> <TimePicker x:Name="EndTimePicker" Header="End Time" Grid.Row="1" Grid.Column="1"/>
页面后台代码
public AddShiftDialog() { this.InitializeComponent(); Binding startDateBinding = new Binding(); startDateBinding.Source = Shift; startDateBinding.Path = new PropertyPath("StartDateTemp"); startDateBinding.Mode = BindingMode.TwoWay; startDateBinding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged; BindingOperations.SetBinding(StartDatePicker, DatePicker.DateProperty, startDateBinding); Binding endDateBinding = new Binding(); endDateBinding.Source = Shift; endDateBinding.Path = new PropertyPath("EndDateTemp"); endDateBinding.Mode = BindingMode.TwoWay; endDateBinding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged; BindingOperations.SetBinding(EndDatePicker, DatePicker.DateProperty, endDateBinding); Binding startTimeBinding = new Binding(); startTimeBinding.Source = Shift; startTimeBinding.Path = new PropertyPath("StartTimeTemp"); startTimeBinding.Mode = BindingMode.TwoWay; startTimeBinding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged; BindingOperations.SetBinding(StartTimePicker, TimePicker.TimeProperty, startTimeBinding); Binding endTimeBinding = new Binding(); endTimeBinding.Source = Shift; endTimeBinding.Path = new PropertyPath("EndTimeTemp"); endTimeBinding.Mode = BindingMode.TwoWay; endTimeBinding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged; BindingOperations.SetBinding(EndTimePicker, TimePicker.TimeProperty, endTimeBinding); }
模型代码
public partial class Shift: BaseModel { public int Id { get; set; } [ObservableProperty] private DateTimeOffset _startDate; [ObservableProperty] private DateTimeOffset _endDate; [ObservableProperty] private DateTimeOffset _startTime; [ObservableProperty] private DateTimeOffset _endTime; [NotMapped] public DateTimeOffset StartDateTemp { get { if(StartDate == string.Empty) { return DateTimeOffset.Now; } return DateTimeOffset.Parse(StartDate); } set { StartDate = value.ToString(); } } [NotMapped] public DateTimeOffset EndDateTemp { get { if (EndDate == string.Empty) { return DateTimeOffset.Now; } return DateTimeOffset.Parse(EndDate); } set { EndDate = value.ToString(); } } [NotMapped] public DateTimeOffset StartTimeTemp { get { if (StartTime == string.Empty) { return DateTimeOffset.Now; } return DateTimeOffset.Parse(StartTime); } set { StartTime = value.ToString(); } } [NotMapped] public DateTimeOffset EndTimeTemp { get { if (EndTime == string.Empty) { return DateTimeOffset.Now; } return DateTimeOffset.Parse(EndTime); } set { EndTime = value.ToString(); } } }
最佳处理方案
针对SQLite不支持DateTimeOffset的问题,推荐以下两种无损存储的方法,无需额外维护Temp属性,直接绑定原始属性即可:
方法1:使用EF Core值转换器(推荐)
EF Core支持通过值转换器将DateTimeOffset映射为SQLite支持的类型(如ISO 8601字符串),自动处理转换逻辑,无需手动编写转换代码。
步骤:
- 简化模型:删除所有
Temp属性,保留原始的StartDate、EndDate、StartTime、EndTime(类型为DateTimeOffset)。 - 配置DbContext:在
OnModelCreating中添加全局或局部值转换器:
protected override void OnModelCreating(ModelBuilder modelBuilder) { // 定义转换器:DateTimeOffset ↔ ISO 8601字符串 var dateTimeOffsetConverter = new ValueConverter<DateTimeOffset, string>( v => v.ToString("o"), v => DateTimeOffset.Parse(v)); // 全局配置:所有DateTimeOffset属性自动转换 foreach (var entityType in modelBuilder.Model.GetEntityTypes()) { foreach (var property in entityType.GetProperties()) { if (property.ClrType == typeof(DateTimeOffset) || property.ClrType == typeof(DateTimeOffset?)) { property.SetValueConverter(dateTimeOffsetConverter); } } } // 或单独为Shift类配置(二选一即可) // modelBuilder.Entity<Shift>() // .Property(s => s.StartDate) // .HasConversion(dateTimeOffsetConverter); // modelBuilder.Entity<Shift>() // .Property(s => s.EndDate) // .HasConversion(dateTimeOffsetConverter); }
- 修改绑定:直接绑定原始属性,无需Temp属性:
BindingOperations.SetBinding(StartDatePicker, DatePicker.DateProperty, new Binding { Source = Shift, Path = new PropertyPath("StartDate"), Mode = BindingMode.TwoWay, UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged });
方法2:手动转换为ISO 8601格式字符串存储
如果不想使用值转换器,可以将DateTimeOffset转换为标准ISO 8601格式字符串存储,该格式包含完整时区信息,解析时不会丢失数据。
修改模型:
将日期时间属性改为string类型,同时提供用于绑定控件的DateTimeOffset属性:
public partial class Shift: BaseModel { public int Id { get; set; } private string _startDate; public string StartDate { get => _startDate; set { SetProperty(ref _startDate, value); OnPropertyChanged(nameof(StartDateOffset)); } } [NotMapped] public DateTimeOffset StartDateOffset { get => string.IsNullOrEmpty(StartDate) ? DateTimeOffset.Now : DateTimeOffset.Parse(StartDate); set => StartDate = value.ToString("o"); } // 同理实现EndDate、StartTime、EndTime及对应的Offset属性 }
绑定控件:
直接绑定到StartDateOffset等属性,逻辑与之前的Temp属性一致,但避免了重复代码。
注意事项
- 禁止使用默认
ToString()方法,不同环境下格式可能不一致,必须使用标准格式(如"o"表示ISO 8601)。 - 当前模型存在编译错误:
StartDate是DateTimeOffset类型,代码中却判断StartDate == string.Empty,需修正属性类型或判断逻辑。
内容的提问来源于stack exchange,提问作者Nuvolari
相关产品推荐
相关产品推荐

