我见过某些面向对象的专家建议域对象(POCO)应该是不可变的。
也就是说,他们的状态应该在构造时完全确定,并且对状态的更改应该需要创建一个全新的实例。
但是说我使用ORM坚持使用关系数据库 - 如何确定要保留哪些属性,以及如何根据数据库中的数据重建域对象?
例如,假设我想将此对象写入数据库:
class User
{
private string _emailAddress;
public User(string emailAddress, string password)
{
// ... generate hash & seed
_emailAddress = emailAddress;
}
public string PasswordHash { get; private set; }
public string PasswordSeed { get; private set; }
public bool ValidatePassword(string password)
{
// validate it against the stored hash
}
}
在上面的例子中,我想存储对象的状态并在以后检索它。这意味着存储PasswordHash,PasswordSeed和EmailAddress。
但是班级的结构使我无法这样做。
PasswordHash和PasswordSeed - 我可以存储它们,但是当我想从数据库中读取它们并重建对象时会发生什么?没有构造函数参数可以传递它们,我甚至无法构造没有它们的对象,因为必须提供“密码”,我不知道原始密码是什么。我无法设置'PasswordHash'或'PasswordSeed'属性,因为它们是'私有设置'。
EmailAddress - 我无法存储它,因为即使它是对象状态的一部分,它也被标记为私有,无法在内部访问。
是的,我可以将对象中的每个属性都公开并进行读/写。但那么,我的封装和不变性在哪里?
请参阅,我想将密码存储为哈希/种子并隐藏电子邮件地址。我想在我的域对象中强制执行某个工作流程。
但是当涉及到存储它们时,我必须撤消所有这些约束并将我的域对象转换为哑键/值存储,没有约束。
基本问题似乎是关系数据库没有封装概念。表中的每一列都是“公共”,每个值都是“可变的”。
这是否意味着如果我要拥有一个不可变的域模型,我必须完全抛弃关系映射?或者是否有一些众所周知的解决这个问题的策略,我还不知道呢?
答案 0 :(得分:3)
首先 - 声明域对象应该是不可变的是错误的 只有值对象应该是不可变的。用户最有可能是一个实体。
通常,ORM会使用一些技巧来解决这个问题。
E.g。 NHibernate利用像LinFu之类的代理库来静默子类化您使用动态创建的类映射的每个类。执行此操作时,它会为每个检查状态是否已被修改的方法附加处理程序,并触发脏实体的持续更改。
NHibernate强制映射对象具有受保护的,参数较少的构造函数,所有属性和方法都可以覆盖。通过此构造函数将对象创建为“空”,并通过覆盖属性设置状态。
答案 1 :(得分:1)
私人字段也可以序列化 - 请参阅http://msdn.microsoft.com/en-us/library/system.serializableattribute.aspx
在这种情况下,您希望将值序列化为数据库并将其读回...
有几种方法可以做到这一点,例如:
实现2个静态方法作为类成员,可以访问所有内部/私有字段,从而序列化和重构类中的任何字段
实现一些用于序列化/反序列化的接口,您可以将其用作静态方法的参数,以减少序列化/反序列化过程与类之间的依赖关系
编辑 - 根据评论:
在静态方法中,您可以自由选择要在DB中保留哪些字段以及如何(二进制,XML或每个字段等一列)。只需相应地实现序列化/反序列化接口。
答案 2 :(得分:0)
您可以阅读对象的序列化和反序列化,并将数据存储在数据库中(其中一些是特定于语言的,因此可能无法跨语言工作)。如果要存储所有字段。这将解决您的密码问题,但如果有人要获取访问权限并反序列化您的实例并获取明文密码(如果它存储在对象中),则会带来安全风险。
以下是如何执行此操作的基本示例:http://blog.kowalczyk.info/article/Serialization-in-C.html
答案 3 :(得分:0)
我建议您将域图层与数据层分离。换句话说:
问题解决了!编写所有映射代码似乎很痛苦(并且您需要对其进行测试,因为映射非常容易出错)但最终,它将解决因使用一个类尝试服务器2目的而导致的头痛。 / p>