NHibernate.3.0.Cookbook第一章第五节Setting up a base entity class
Setting up a base entity class
设置一个实体类的基类
在这节中,我将给你展示怎么样去为我们的实体类设置一个通用的基类。
准备工作
完成前面三节的任务
如何去做
1.在Entity.cs中,为我们的Entity类输入如下代码:
public abstract class Entity<TId>
{
public virtual TId Id { get; protected set; }
public override bool Equals(object obj)
{
return Equals(obj as Entity<TId>);
}
private static bool IsTransient(Entity<TId> obj)
{
return obj != null &&
Equals(obj.Id, default(TId));
}
private Type GetUnproxiedType()
{
return GetType();
}
public virtual bool Equals(Entity<TId> other)
{
if (other == null)
return false;
if (ReferenceEquals(this, other))
return true;
if (!IsTransient(this) &&
!IsTransient(other) &&
Equals(Id, other.Id))
{
var otherType = other.GetUnproxiedType();
var thisType = GetUnproxiedType();
return thisType.IsAssignableFrom(otherType) ||
otherType.IsAssignableFrom(thisType);
}
return false;
}
public override int GetHashCode()
{
if (Equals(Id, default(TId)))
return base.GetHashCode();
return Id.GetHashCode();
}
}
2. 在这个文件中,我们添加另一个额外的Entity类,代码如下所示:
public abstract class Entity : Entity<Guid>
{
}
分析原理
NHibernate需要依赖于Equals方法进行等同性判断。该方法默认定义于System.Object类中,它在引用类型中使用引用相等性判断(即内存地址相同则认为相等),即,x.Equals(y)仅仅在x和y指向相同的对象实例时才是true。这个默认的行为在大多数情况下工作的很好。
为了支持延迟加载,NHibernate使用代理对象。就像我们之前学习过的,这些代理对象实际上是真正的实体类的子类,为了支持延迟加载,它的每个成员都已经被重写过了。
这些代理对象,在你的应用程序中这个默认的Equals方法的行为会导致一些隐含和意想不到的bug。一个应用程序对于使用代理对象应该是不知不觉的,是透明的,所以我们期望的情况是,一个代理对象和一个真实的对象如果它们描述的是同一个实体对象,则它们就应该是相等的。如果一个Product对象的ID是8,另一个Product对象的ID也是8,或者有一个代理的Product对象的ID是8,我们应该认为这三个对象是相等的,具有等同性。要做到它们具有等同性,我们必须重写默认的Equals方法,以改变该方法的默认行为(按内存地址判断相等性)。在我们上面的Entity基类中,我们重写了Equals方法,使得该方法基于POID来决定相等性。在Equals(Object obj)方法中,我们简单的调用了其重载方法Equals(Entity<TId> other),尝试把object类型转换为Entity类型,如果转换失败,则会把null值作为参数传递给Equals(Entity<TId> other)方法,如果参数other的值为null,那么这两个对象就不相等。这个有两种情况,第一,x.Equals(null)永远返回false,第二,someEntity.Equals(notAnEntity)也返回false。接下来,我们比较两者的引用地址,很显然,如果两者的变量引用的是同一个实例对象(相同内存地址)则它们必定相等。如果ReferenceEquals(this,other)返回的是true,则我们也返回true。
再接下来,我们比较Id和默认的Id值,用来判断实体对象是否是临时态,一个处于临时态的对象是一个尚未持久化至数据库中的对象。default(TId)始终返回的是TId的默认值,对于Guid来说,它的默认值是Guid.Empty,对于string和其他所有的引用类型来说,它们的默认值是null,对于数字类型,则默认值为0。如果这个Id属性和它的默认值相等,则该实体对象处于临时态。如果一个或者两个实体处于临时态,则我们认为它们必定是不等的(如果待比较的两个对象中存在一个以上的对象处于临时态,则它们必定不等),需要返回false。
如果实体对象处于持久态,并且它们都拥有POID,我们就可以通过比较它们的POID值来确定相等性。如果POID不相等,我们就认为待比较的两个实体对象是不相等的(不等同),我们返回false。
最后,我们还要作最后一次的判断。我们知道至此为止,待比较的对象肯定是处于持久态了,并且它们都有相同的Id值,但是这也不能证明肯定就是相等的。如果一个ActorRole实体和一个Product实体具有相同的POID值,那么如果仅仅是上面的代码的话也会被认为是相等的。我们最后的判断就是要去比较两者的类型,如果其中的一个类型派生了另一个类型,则我们认为它们是相等的。
假如other参数是一个Product的代理对象,并且描述的是一个book实体,而真正的book实例描述的是同一个对象,那么this.Equals(other)应该返回true,因为它们都描述的是同一个实体对象。然而,不幸的是other.GetType()不是返回Product,而是返回ProductProxy12398712938,而typeof(ProductProxy12398712938).IsAssignableFrom(typeof(Book))的结果会是false,在这种情况下,我们的Equals方法将不可行。正因为如此,我们需要使用other. GetUnproxiedType()方法,它会透过代理层从而返回实际的实体类型,因为typeof(Product).IsAssignableFrom(typeof(Book))返回true,所以我们的Equals实现可以工作的很好。
因为我们重写了Equals方法,所以我们也必须重写GetHashCode方法,这也是.NET Framework框架规范的要求。如果x.Equals(y),那么x.GetHashCode()和y.GetHashCode()应该返回相同的值,而反过来并不是必须的(如果x.GetHashCode()和y.GetHashCode()返回相同的值,则x.Equals(y)可以返回false),当它们不相等的时候x和y也可以共享一个hash code。在我们的Entity基类中,我们简单地使用了Id的hash code值。
补充知识
更多的关于Equals和GetHashCode的知识,请参阅MSDN文档的相关内容,http://msdn.microsoft.com/en-us/library/system. object.aspx.
补充:
1. 第四节的映射集合中,对ISet集合的处理我们曾经提到过需要重写Equals 和 GetHashCode方法,判断元素是否已经存在于ISet中就需要使用Equals来进行判断。
2. 上面的泛型中TId如果也是一个引用类型的话则也必须重写其Equals 和 GetHashCode方法。
NHibernate.3.0.Cookbook第一章第五节Setting up a base entity class的更多相关文章
- NHibernate.3.0.Cookbook第一章第六节Handling versioning and concurrency的翻译
NHibernate.3.0.Cookbook第一章第六节Handling versioning and concurrency的翻译 第一章第二节Mapping a class with XML ...
- tensorflow2.0学习笔记第一章第五节
1.5简单神经网络实现过程全览
- 【软件构造】第三章第五节 ADT和OOP中的等价性
第三章第五节 ADT和OOP中的等价性 在很多场景下,需要判定两个对象是否 “相等”,例如:判断某个Collection 中是否包含特定元素. ==和equals()有和区别?如何为自定义 ADT正确 ...
- 第一百一十五节,JavaScript,DOM操作表格
JavaScript,DOM操作表格 学习要点: 1.操作表格 DOM在操作生成HTML上,还是比较简明的.不过,由于浏览器总是存在兼容和陷阱,导致最终的操作就不是那么简单方便了.本章主要了解一下DO ...
- tensorflow2.0学习笔记第一章第四节
1.4神经网络实现鸢尾花分类 import tensorflow as tf from sklearn import datasets import pandas as pd import numpy ...
- 04373 C++程序设计 2019版 第一章习题五、程序设计题
题目: 1.编写一个程序,将从键盘输入的n个字符串保存在一个一维数组A中.在输入字符串之前,先输入n的值.要求,数组A需要动态申请空间,程序运行结束前再释放掉. #include <iostre ...
- (第一章第五部分)TensorFlow框架之变量OP
系列博客链接: (一)TensorFlow框架介绍:https://www.cnblogs.com/kongweisi/p/11038395.html (二)TensorFlow框架之图与Tensor ...
- 第一章-第五题(你所在的学校有计算机科学专业和软件工程专业么?相关专业的教学计划和毕业出路有什么不同?阅读有关软件工程和计算机科学的区别的文章,谈谈你的看法。)--By 侯伟婷
我所在的本科学校和研究生学校都有计算机科学专业和软件工程专业.具体的教学计划无从得到,所以此情况无从对比,但是我从本科教务处网站找到了计算机科学专业和软件工程专业有关专业方面的课程,现列表如下. 表格 ...
- 第一百零五节,JavaScript正则表达式
JavaScript正则表达式 学习要点: 1.什么是正则表达式 2.创建正则表达式 3.获取控制 4.常用的正则 假设用户需要在HTML表单中填写姓名.地址.出生日期等.那么在将表单提交到服务器进一 ...
随机推荐
- C++开源项目等收集
VLC 是一款自由.开源的跨平台多媒体播放器及框架,可播放大多数多媒体文件,以及 DVD.音频 CD.VCD 及各类流媒体协议. Downloading vlc-2.2.4.tar.xz Thanks ...
- javascript删除数组,索引出现问题解决办法。
var data = [ { isRemove: 0, name: "项目1" }, { isRemove: 1, name: "项目2" }, { isRem ...
- SQL Server死锁产生原因及解决办法 .
其实所有的死锁最深层的原因就是一个:资源竞争 表现一: 一个用户A 访问表A(锁住了表A),然后又访问表B,另一个用户B 访问表B(锁住了表B),然后企图访问表A,这时用户A由于用户B已经锁住表B,它 ...
- Main.storyboard: WKWebView before iOS 11.0 (NSCoding support was broken in previous versions)
在工程里用 故事板写了 wkwebview 如果运行在 ios11以下 就会报这个错误,如果要支持iOS 11 以下的用户,请重写View部分,使用代码调用WKWebView,而不用使用故事版来加 ...
- 2013-2015 Aaronyang的又一总结,牧童遥指纳尼村
我没有时间去唠叨自己的事,可是你们是我喜欢的人,ay很愿意写给你们分享:去年的万人阅读的总结链接:<没学历的IT人生没那么悲催,献给程序员们> 提前声明:本文不良反应:请自备垃圾桶,准备装 ...
- sed 简明教程 (转)
sed 简明教程 2013年2月20日 awk于1977年出生,今年36岁本命年,sed比awk大2-3岁,awk就像林妹妹,sed就是宝玉哥哥了.所以 林妹妹跳了个Topless,他的哥哥sed ...
- latex学习(四)tlmgr
官网说明文档:https://tug.org/texlive/doc/tlmgr.html,2018版已经被冻结了,所以tlmgr也不会更新了,要等到下一个大的版本才能更新. 1.用tlmgr查看已经 ...
- C#sql语句
SQL语句大全删除数据库 drop database databasename SQL语句大全备份 --- 创建备份数据的 device USE master EXEC sp_addumpdevice ...
- python bottle框架 解决跨域问题的正确方式
经查询,网上有几种说法 https://www.cnblogs.com/EmptyFS/p/6138923.html 我首先查到的就是这个,我采用了文中所说的修改源码的方式, 但是经测试发现,修改源码 ...
- vue2.0 实现click点击当前li,动态切换class
1,文件内容 ----//为item添加不存在的属性,需要使用vue提供的Vue.set( object, key, value )方法. 看详解:https://cn.vuejs.org/v2/a ...