关于EF实体类的一点思考

  在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”);),分别可以获取到调用函数(准确讲应该是成员)名称,调用文件及调用行号。示例:

public void WriteError(object message, [CallerMemberName] string memberName = "", [CallerFilePath] string sourceFilePath = "", [CallerLineNumber] int sourceLineNumber = 0)
{
    _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中一般是怎么做的呢?

扫描二维码关注公众号,回复: 3173168 查看本文章
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.csdn.net/x333vxhl/article/details/54969013https://github.com/Fody/PropertyChanged/wiki

如果感兴趣,可以继续研究下AOP面向切面编程,如Postsharp(需要下载安装,但代码发布后会自动添加一个dll,即服务器上不用安装)、Mono.Cecil:

https://www.cnblogs.com/pokemon/p/5479509.html(基于Mono.Cecil的静态注入)

https://blog.csdn.net/linxinfa/article/details/51803200(Mono.Cecil简介与示例)

https://blog.csdn.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#的代码生成与注入)

猜你喜欢

转载自www.cnblogs.com/jaign/p/9640364.html
今日推荐