【发布时间】:2011-09-23 15:40:06
【问题描述】:
我在从复杂的 Linq-to-Sql 查询返回默认 DateTime 值时遇到问题。
希望下面的简化示例能说明问题(虽然我没有运行这个确切的代码):
users.Select(u =>
new MyDomainObject(
u.Id,
u.Transactions
.Where(t => false) // empty results set
.Select(t => t.TransactionTime) // TransactionTime is DATETIME NOT NULL
.OrderByDescending(x => x)
.FirstOrDefault() // I want DateTime.MinValue (or SqlDateTime.MinValue)
)
);
所以我想要最后一个时间戳,如果没有结果,我想要一些 MinValue 时间戳。
枚举上面的查询会报错
The null value cannot be assigned to a member with type System.DateTime
更新
好的,我不确定我上面的示例是否足以说明问题。我认为该错误可能与我尝试对第三个链接表执行子查询的方式有关。
以下示例重新创建了确切的错误:
所以我有一辆汽车,我可以把它带到机械师那里,有时(但不总是)由机械师维修。
要求是通过对汽车表的查询找到每辆汽车自上次维修以来进行了多少次维修。问题是汽车从未维修过,因此数据如下所示:
Car
-------------
Id: 1
MechanicVisit
-------------
Id: 1
CarId: 1
ServiceRecordId: NULL
VisitDate: 1 Jan 2011
ServiceRecord
-------------
<empty>
因此,显示错误的一个简单示例是查询上次服务时间列表:
var test = _dataContext.GetTable<Car>
.Select(c =>
c.MechanicVisits
.Select(m => m.ServiceRecord)
.Select(s => s.ServiceDate)
.OrderByDescending(d => d)
.FirstOrDefault()
).ToList();
这给出了前面描述的尝试将 null 分配给非可空类型的错误,我需要做的是在日期为 null 时返回 DateTime.MinValue 或 SqlDateTime.MinValue(这样我就可以执行实际查询是自上次服务以来的机械师访问次数)
解决方案
我使用了 Jon Skeet 建议的变体,使用了 DateTime? 的强制转换和空合并运算符:
var test = _dataContext.GetTable<Car>
.Select(c =>
c.MechanicVisits
.Select(m => m.ServiceRecord)
.Select(s => (DateTime?)s.ServiceDate)
.OrderByDescending(d => d)
.FirstOrDefault() ?? new DateTime(1900, 1, 1)
).ToList();
注意“默认”日期的参数化构造函数的使用 - DateTime.MinValue 不能在此处使用,因为它在转换为 SQL 时会引发超出范围的异常,并且 SqlDateTime.MinValue 不能用作它不可为空(因此合并运算符无效)。
我仍然不明白为什么会发生最初的错误,而且这个解决方案确实有点老套,但我找不到任何更巧妙的方法来修复它。
【问题讨论】:
-
哪个成员属于 DateTime 类型?目前还不清楚发生了什么......
-
t.TrnsactionTime是数据库中的datetime not null类型,MyDomainObject的第二个参数应该是DateTime。哪一点不清楚?我会尽力解释得更好。 -
好的,这更清楚了。只是为了检查一下,
t.TransactionTime在您的 LINQ to SQL 模型中也不能为空吗? -
Users和Transactions是什么关系? -
@Jon 是的,在 DB 和 Linq 实体中都不可为空
标签: c# linq-to-sql default-value value-type