在EF中修改一条记录时,一般是先查出该条记录,然后再通过TryUpdateModel或其他方式更新对应的属性。但我很讨厌这种要更新一条记录时,还要先去把记录查询出来的做法。我喜欢像sql语句那样的直接更新需要更新的字段。
以前一般都是先写好数据库,再通过代码生成器生成实体类、DbContext对象等。这里没用EF的DBFirst的自动生成是因为它无法生成我想要的实体对象。举个例子,我希望的实体类大概是这样:
public class User { public HashSet<string> PropertiesHasChanged = new HashSet<string>(); private string _name; public string Name { get { return _name; } set { _name = value; PropertiesHasChanged.Add("Name"); } } }
这样,我就可以直接将一个实体进行附加,然后很方便的设置需要更改的字段。
当然,你也可以先让所有属性都为可空类型,然后根据属性值是否为null进行判断,但这有一个缺点,假如我就想更新数据库中某记录的字段值为null呢?
如果你使用了Automapper等DTO,你也可以反射出要修改的属性字段,不过这不是本文要论述的地方。
当然,上述代码还有一个更好点的写法:
public class User { public HashSet<string> PropertiesHasChanged = new HashSet<string>(); private string _name; public string Name { get { return _name; } set { _name = value; // PropertiesHasChanged.Add("Name"); AddPropertityName(); } } //通过CallerMemberName自动获取属性名称 private void AddPropertityName([System.Runtime.CompilerServices.CallerMemberName]string propertyName = null) { if(!string.IsNullOrEmpty(propertyName)) PropertiesHasChanged.Add(propertyName); } }
插一段题外话:
在.Net 4.5中引入了三个Attribute:CallerMemberName、CallerFilePath和CallerLineNumber 。在编译器的配合下(什么叫在编译器的配合下?通过ILSpy(打开url后注意这里:Download: latest release | latest CI build (master),点击后一个a标签,下载解压后即可使用)反编译上述代码的dll,我们可以发现,Name属性中set部分的代码变成了_name = value;AddPropertityName(“Name”);),分别可以获取到调用函数(准确讲应该是成员)名称,调用文件及调用行号。示例:
) { _log4Net.ErrorFormat("文件:{0} 行号:{1} 方法名:{2},消息:{3}", sourceFilePath, sourceLineNumber, memberName, message); } //摘抄自https://www.cnblogs.com/zeroes/p/6031651.html
但现在我想Codefirst了,也即实体类的代码都要一行一行的敲,意味着除非特殊情况,几乎所有的属性我都只能这样写:public string Name { get; set; }
那么,在这种情况下,有没有什么好的方式让我也能实现最初的目标呢?
首先想到的是WPF中的MVVM,这个不就是属性发生更改后就自动通知么?正好和我的需求吻合嘛。先看看WPF中一般是怎么做的呢?
public class User : INotifyPropertyChanged { private string _name; public string Name { get { return _name; } set { _name = value; OnPropertyChanged(); } } public event PropertyChangedEventHandler PropertyChanged; protected void OnPropertyChanged([CallerMemberName]string propertyName = "") { PropertyChangedEventHandler handler = PropertyChanged; if (handler != null) { handler(this, new PropertyChangedEventArgs(propertyName)); } } }
但我想要的是 { get; set; }的这种写法啊,不想要上面那种还要多写一串代码的方式啊。继续沿着WPF的思路,我们找到了PropertyChanged.Fody。先看看PropertyChanged.Fody的用法:
1.首先通过nuget在项目里引入PropertyChanged.Fody;
2.在项目里添加一FodyWeavers.xml文件,文件内容为:
<?xml version="1.0" encoding="utf-8" ?> <Weavers> <PropertyChanged/> </Weavers>
3.使用方式:
(1)github地址:https://github.com/Fody/PropertyChanged
(2)实现INotifyPropertyChanged接口,也即去掉上面代码中set内的“OnPropertyChanged();”部分,生成后,dll中set部分的代码就会自动变成:set { if(!string.Equals(_name, value, StringComparison.Ordinal)) { _name=value; OnPropertyChanged("Name"); } }
(3)给类添加“[PropertyChanged.AddINotifyPropertyChangedInterface]”特性,编译器会自动将该类继承INotifyPropertyChanged,并实现相关操作。
4.其他使用要点:(摘自:https://www.bbsmax.com/A/o75NWb0xdW/)
- 如果有某个属性不想实现通知事件,就在相应属性上加个[DoNotNotify]
- 假如该User类由这3个属性组成:FirstName, LastName, FullName,其中FullName是前两个属性的组合,那么假如前两个属性中任何一个变化都要通知都FullName变化,就给FirstName和LastName上加个[AlsoNotifyFor("FullName")],同理,如果FullName变了也让子属性变化,那就要在FullName上加上[DependsOn("FirstName","LastName")]
- [DoNotSetChanged]表示当FullName 的Set改变时,不通知到子属性
- [DoNotCheckEquality]:这个是说跳过相等的检查,即不要if(!string.Equals(_name, value, StringComparison.Ordinal))的判断,可用于类或者属性上。
5.更详细的请看:https://blog.****.net/x333vxhl/article/details/54969013、https://github.com/Fody/PropertyChanged/wiki等
如果感兴趣,可以继续研究下AOP面向切面编程,如Postsharp(需要下载安装,但代码发布后会自动添加一个dll,即服务器上不用安装)、Mono.Cecil:
https://www.cnblogs.com/pokemon/p/5479509.html(基于Mono.Cecil的静态注入)
https://blog.****.net/linxinfa/article/details/51803200(Mono.Cecil简介与示例)
https://blog.****.net/lee576/article/details/38780889(使用Mono.Cecil对MSIL进行注入)
https://www.jianshu.com/p/a5276aadccdd(使用Mono.Cecil实现IL代码注入)
https://www.cnblogs.com/tianqing/p/7610560.html(巧用Mono.Cecil反射加载类型和方法信息)
https://www.cnblogs.com/chejiangyi/p/5819129.html(.Net Aop(静态织入)框架 BSF.Aop)
https://www.cnblogs.com/WinHEC/p/Postsharp_5_X_Cracked.html( Postsharp 破解工具(通杀版,持续更新))
https://www.cnblogs.com/wuhuacong/p/6518748.html(在.NET项目中使用PostSharp,使用MemoryCache实现缓存的处理)
https://www.cnblogs.com/DragonStart/p/7744202.html(C# AOP 面向切面编程之 调用拦截)
http://www.cnblogs.com/farb/p/ABPAdvancedTheoryContent.html#AOPinDotNet(C#高级知识点&(ABP框架理论学习高级篇)——白金版)(值得阅读)
http://gad.qq.com/article/detail/27977(使用MSIL采用Emit方式实现C#的代码生成与注入)