当计算字段作为查询条件[记一次需求导致的问题]

724 阅读4分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

如题,Model中一字段值为其他字段计算后所得,不会存储到数据库中,但是一需求:以此字段为条件进行查询数据

具体问题

在实体类中我有一个售出日期字段、售出月份字段,后面又添加了一个售出年份字段,只有售出日期是从表单中获取值,另外的直接从售出日期中获取。部分代码如下

/// <summary>
/// 售出日期
/// </summary>
[Column]
[Required]
public DateTime SaleDate { get; set; }

/// <summary>
/// 售出月份
/// </summary>
[NotMapped]//生成表中字段
public string Month => SaleDate.Month.ToString();

刚开始想法是,把Month字段也存到数据库表中,但是售出日期变动后,此Month字段也要跟着更新,这里还需要修改多处代码,后期没有任何优化空间了。解决问题还是要从根源上解决,而不是为了解决问题不折手段....

解决办法(并非我想要的)

换条思路,能不能不要Month了,直接截取售出日期,进行查询,理论上是可行的,用sql语句也能很方便实现。 image.png 注:为了测试,特意找有一些数据的数据库表,这样才能看到效果,未采用查询本文开头对应表(无数据),下面例子也是,特此说明下。
上图为SqlServer数据库,其他数据库应该也有类似DATEPARTMONTH 函数,具体自行搜索吧,本文重点不是这里,就不深入了
为方便大家获取关键代码,上图具体代码如下

/*方式一:使用DATEPART*/
select DATEPART(Month, CreateTime) as '月份', Count(1) as '条数' from T_OptionRecord GROUP BY DATEPART(MONTH, CreateTime)

/*方式二:使用函数MONTH*/
select DATEPART(mm,CreateTime)  as '月份', Count(1) as '条数' from T_OptionRecord GROUP BY MONTH(CreateTime)

使用Linq 同样也能实现该需求,直接上图

image.png

图中代码如下

db.GetList<OperationLog>()
    .GroupBy(x => x.CreateTime.Month)
    .Select(x => new { Name = x.Key, Count = x.Count() })
    .ToList();

其他解决办法(最终方法)

上面已经展示了三种解决办法,但是不是我想要的,我就要用Month字段来作为查询条件!不然我写这个字段的意义在哪!不能仅仅展示使用吧。
要用该字段作为查询条件,数据库表必须要存储该字段,否则会报错:Invalid column name 'Month'
静下心来,思考一下,Month字段其实只有get访问器,没有set访问器,也就是只读(面向对象三大特性之 封装...)
其实他的值是SaleDate的set访问器中自动赋值的(自动随之改变)
那么完全可以把赋值行为写到SaleDate的set中
接下来就是想办法,如何写set(其实很简单,但是不容易)
先来回顾一下相关基础知识点吧

  • 类中成员包括字段、属性、方法
  • 字段:类中的变量,可以有private/protected/public等不同修饰符。说白了就是--->存储数据的变量
  • 属性:使用get/set包装的方法,本质是方法,是方法的简写。作用就是增强对字段的存取控制!
  • 如果类中一个字段是private,那外部是不能访问的,但是可以通过属性的get访问器来读取他的值,也可以通过set访问器来更新(写)他的值
  • 属性的get/set可以限制字段的一些功能,以达到某种目的!
  • 属性是没有存储功能的(本质是函数/方法),数据都存储在字段中,所以只有修改了字段的数据才能修改数据,改属性值没用!
  • 属性其实就是对字段的封装,字段在类内部使用,属性安全性高一点,所以可以在外部使用!
  • 属性和方法的区别:属性没有参数列表,方法必须有,哪怕是空的,也要写个空括号在那里!属性至少要有get/set其中一个访问器(不可读不可写的属性是没有意义的!)。两者都可以判定字段中数据的合法性!即都可以对字段的值起限定作用!

综上,总结一下就是:字段是存储容器,属性是操作数据的方法(写、读)

铺垫有了,那就思考一下如何写吧,看看你们能不能写出来
我的代码实现如下:

/// <summary>
/// 售出日期
/// </summary>
private DateTime saleDate;//字段

/// <summary>
/// 售出日期
/// </summary>
[Column]
[Required]
public DateTime SaleDate//属性
{
    get
    {
        return saleDate;
    }
    set
    {
        saleDate = value;//将值赋值给字段
        Month = saleDate.Month.ToString();
    }
}

/// <summary>
/// 售出日期
/// </summary>
[Column]
public string Month { get; set; }

经测试,完全可行,查询也没问题了
简单查询代码如下

var list = db.GetList<Vehicle>(x => x.Month == month);

最后总结

回想一下,其实我的最终方法并不是最优解,甚至不如上面sql和linq方法好,因为他有个最大的缺陷,就是如果数据已经存在了,而且量很大,突然增加一个字段,是没有值的,而且不符合 尽量不改动表结构的原则,越到后面数据积累了越不要改动表!但是他也是一种实现的方式,这种方式可以深刻理解到字段和属性,set和get访问器等基础知识点的重要性!