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表单中填写姓名.地址.出生日期等.那么在将表单提交到服务器进一 ...
随机推荐
- 内核同步机制-RCU同步机制
转自:https://blog.csdn.net/nevil/article/details/7718375 转自http://www.360doc.com/content/09/0805/00/36 ...
- YUV 4:2:0 格式和YUV411格式区别
版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/coloriy/article/details/6668447 MPEG 储存的 YU(Cb)V(Cr ...
- C# ConcurrentDictionary实现
ConcurrentDictionary的源码看了很多遍,今天抽点时间整理一下,它的实现比Dictionary要复杂很多,至于线程安全我觉得比较简单,用的是lock的思想.首先我们来看看它的源码. p ...
- 如何唯一的标识一台Android设备?
UUID : (Universally Unique Identifier)全局唯一标识符,是指在一台机器上生成的数字,它保证对在同一时空中的所有机器都是唯一的.由以下几部分的组合:当前日期和时间(U ...
- Windows 下使用 MinGW 和 CMake 进行开发
CMake 是个非常棒的项目管理工具,这已经是毋庸置疑的. 一些小工具需要在 win 下开发,所以今天探索使用 MinGW 和 CMake 在 win 下的搭配使用.简单做记录. MinGW 使用 Q ...
- numpy 中的 broadcasting 理解
broadcast 是 numpy 中 array 的一个重要操作. 首先,broadcast 只适用于加减. 然后,broadcast 执行的时候,如果两个 array 的 shape 不一样,会先 ...
- 〖Java〗Eclispe安装和使用viplugin
习惯了VIM的操作,每次打开Eclipse都习惯性的按下 hjkl: 感觉蛋疼了使用一下VIPlugin,发现给编码速度造成了成吨的伤害- 这个插件对于习惯于使用VIM的程序员来说,简直太有必要了.. ...
- Android打造完美的刮刮乐效果控件
技术:Android+Java 概述 趁着元旦假期之际,首先在这里,我祝福大家在新的2019年都一个个的新健康,新收入,新顺利,新如意!!! 上一偏,我介绍了用Xfermode实现自定义圆角和椭圆 ...
- TLS/HTTPS 证书生成与验证
最近在研究基于ssl的传输加密,涉及到了key和证书相关的话题,走了不少弯路,现在总结一下做个备忘 科普:TLS.SSL.HTTPS以及证书 不少人可能听过其中的超过3个名词,但它们究竟有什么关联呢? ...
- Atiitt 使用java语言编写sql函数或存储过程
Atiitt 使用java语言编写sql函数或存储过程 1.1. java编写sql函数或存储过程的机制1 1.2. Java编写sp的优点1 1.3. 支持java源码,class文件,blog f ...