You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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字符串),自动处理转换逻辑,无需手动编写转换代码。

步骤:

  1. 简化模型:删除所有Temp属性,保留原始的StartDate、EndDate、StartTime、EndTime(类型为DateTimeOffset)。
  2. 配置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);
}
  1. 修改绑定:直接绑定原始属性,无需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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 14:24:53