实体框架 4.0 和 DDD 模式
我使用 EntityFramework 作为 ORM,并且有简单的 POCO 域模型,其中有两个表示值对象和实体对象模式 (Evans) 的基类。这两种模式都是关于两个对象的相等性,因此我重写了 Equals 和 GetHashCode 方法。这是这两个类:
public abstract class EntityObject<T>{
protected T _ID = default(T);
public T ID {
get { return _ID; }
protected set { _ID = value; }
}
public sealed override bool Equals(object obj) {
EntityObject<T> compareTo = obj as EntityObject<T>;
return (compareTo != null) &&
((HasSameNonDefaultIdAs(compareTo) ||
(IsTransient && compareTo.IsTransient)) &&
HasSameBusinessSignatureAs(compareTo));
}
public virtual void MakeTransient() {
_ID = default(T);
}
public bool IsTransient {
get {
return _ID == null || _ID.Equals(default(T));
}
}
public override int GetHashCode() {
if (default(T).Equals(_ID))
return 0;
return _ID.GetHashCode();
}
private bool HasSameBusinessSignatureAs(EntityObject<T> compareTo) {
return ToString().Equals(compareTo.ToString());
}
private bool HasSameNonDefaultIdAs(EntityObject<T> compareTo) {
return (_ID != null && !_ID.Equals(default(T))) &&
(compareTo._ID != null && !compareTo._ID.Equals(default(T))) &&
_ID.Equals(compareTo._ID);
}
public override string ToString() {
StringBuilder str = new StringBuilder();
str.Append(" Class: ").Append(GetType().FullName);
if (!IsTransient)
str.Append(" ID: " + _ID);
return str.ToString();
}
}
public abstract class ValueObject<T, U> : IEquatable<T> where T : ValueObject<T, U> {
private static List<PropertyInfo> Properties { get; set; }
private static Func<ValueObject<T, U>, PropertyInfo, object[], object> _GetPropValue;
static ValueObject() {
Properties = new List<PropertyInfo>();
var propParam = Expression.Parameter(typeof(PropertyInfo), "propParam");
var target = Expression.Parameter(typeof(ValueObject<T, U>), "target");
var indexPar = Expression.Parameter(typeof(object[]), "indexPar");
var call = Expression.Call(propParam, typeof(PropertyInfo).GetMethod("GetValue", new[] { typeof(object), typeof(object[]) }),
new[] { target, indexPar });
var lambda = Expression.Lambda<Func<ValueObject<T, U>, PropertyInfo, object[], object>>(call, target, propParam, indexPar);
_GetPropValue = lambda.Compile();
}
public U ID { get; protected set; }
public override Boolean Equals(Object obj) {
if (ReferenceEquals(null, obj)) return false;
if (obj.GetType() != GetType()) return false;
return Equals(obj as T);
}
public Boolean Equals(T other) {
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
foreach (var property in Properties) {
var oneValue = _GetPropValue(this, property, null);
var otherValue = _GetPropValue(other, property, null);
if (null == oneValue && null == otherValue) return false;
if (false == oneValue.Equals(otherValue)) return false;
}
return true;
}
public override Int32 GetHashCode() {
var hashCode = 36;
foreach (var property in Properties) {
var propertyValue = _GetPropValue(this, property, null);
if (null == propertyValue)
continue;
hashCode = hashCode ^ propertyValue.GetHashCode();
}
return hashCode;
}
public override String ToString() {
var stringBuilder = new StringBuilder();
foreach (var property in Properties) {
var propertyValue = _GetPropValue(this, property, null);
if (null == propertyValue)
continue;
stringBuilder.Append(propertyValue.ToString());
}
return stringBuilder.ToString();
}
protected static void RegisterProperty(Expression<Func<T, Object>> expression) {
MemberExpression memberExpression;
if (ExpressionType.Convert == expression.Body.NodeType) {
var body = (UnaryExpression)expression.Body;
memberExpression = body.Operand as MemberExpression;
}
else
memberExpression = expression.Body as MemberExpression;
if (null == memberExpression)
throw new InvalidOperationException("InvalidMemberExpression");
Properties.Add(memberExpression.Member as PropertyInfo);
}
}
一切都很好,直到我尝试删除一些相关对象(具有两个标记为级联删除的依赖对象的聚合根对象):我遇到了一个异常“关系无法更改,因为一个或多个外键属性不可为空”。我用谷歌搜索了这个并发现http://blog.abodit.com/2010/05/the-relationship-could-not-be-changed-because-one-or-更多外键属性不可为空/ 我将 GetHashCode 更改为 base.GetHashCode() 并且错误消失了。但现在它破坏了我的所有代码:我无法覆盖 POCO 对象的 GetHashCode =>我无法覆盖 Equals =>我无法为 POCO 对象实现值对象和实体对象模式。所以,我很欣赏这里的任何解决方案、解决方法等。
I use EntityFramework as ORM and I have simple POCO Domain Model with two base classes that represent Value Object and Entity Object Patterns (Evans). These two patterns is all about equality of two objects, so I overrode Equals and GetHashCode methods. Here are these two classes:
public abstract class EntityObject<T>{
protected T _ID = default(T);
public T ID {
get { return _ID; }
protected set { _ID = value; }
}
public sealed override bool Equals(object obj) {
EntityObject<T> compareTo = obj as EntityObject<T>;
return (compareTo != null) &&
((HasSameNonDefaultIdAs(compareTo) ||
(IsTransient && compareTo.IsTransient)) &&
HasSameBusinessSignatureAs(compareTo));
}
public virtual void MakeTransient() {
_ID = default(T);
}
public bool IsTransient {
get {
return _ID == null || _ID.Equals(default(T));
}
}
public override int GetHashCode() {
if (default(T).Equals(_ID))
return 0;
return _ID.GetHashCode();
}
private bool HasSameBusinessSignatureAs(EntityObject<T> compareTo) {
return ToString().Equals(compareTo.ToString());
}
private bool HasSameNonDefaultIdAs(EntityObject<T> compareTo) {
return (_ID != null && !_ID.Equals(default(T))) &&
(compareTo._ID != null && !compareTo._ID.Equals(default(T))) &&
_ID.Equals(compareTo._ID);
}
public override string ToString() {
StringBuilder str = new StringBuilder();
str.Append(" Class: ").Append(GetType().FullName);
if (!IsTransient)
str.Append(" ID: " + _ID);
return str.ToString();
}
}
public abstract class ValueObject<T, U> : IEquatable<T> where T : ValueObject<T, U> {
private static List<PropertyInfo> Properties { get; set; }
private static Func<ValueObject<T, U>, PropertyInfo, object[], object> _GetPropValue;
static ValueObject() {
Properties = new List<PropertyInfo>();
var propParam = Expression.Parameter(typeof(PropertyInfo), "propParam");
var target = Expression.Parameter(typeof(ValueObject<T, U>), "target");
var indexPar = Expression.Parameter(typeof(object[]), "indexPar");
var call = Expression.Call(propParam, typeof(PropertyInfo).GetMethod("GetValue", new[] { typeof(object), typeof(object[]) }),
new[] { target, indexPar });
var lambda = Expression.Lambda<Func<ValueObject<T, U>, PropertyInfo, object[], object>>(call, target, propParam, indexPar);
_GetPropValue = lambda.Compile();
}
public U ID { get; protected set; }
public override Boolean Equals(Object obj) {
if (ReferenceEquals(null, obj)) return false;
if (obj.GetType() != GetType()) return false;
return Equals(obj as T);
}
public Boolean Equals(T other) {
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
foreach (var property in Properties) {
var oneValue = _GetPropValue(this, property, null);
var otherValue = _GetPropValue(other, property, null);
if (null == oneValue && null == otherValue) return false;
if (false == oneValue.Equals(otherValue)) return false;
}
return true;
}
public override Int32 GetHashCode() {
var hashCode = 36;
foreach (var property in Properties) {
var propertyValue = _GetPropValue(this, property, null);
if (null == propertyValue)
continue;
hashCode = hashCode ^ propertyValue.GetHashCode();
}
return hashCode;
}
public override String ToString() {
var stringBuilder = new StringBuilder();
foreach (var property in Properties) {
var propertyValue = _GetPropValue(this, property, null);
if (null == propertyValue)
continue;
stringBuilder.Append(propertyValue.ToString());
}
return stringBuilder.ToString();
}
protected static void RegisterProperty(Expression<Func<T, Object>> expression) {
MemberExpression memberExpression;
if (ExpressionType.Convert == expression.Body.NodeType) {
var body = (UnaryExpression)expression.Body;
memberExpression = body.Operand as MemberExpression;
}
else
memberExpression = expression.Body as MemberExpression;
if (null == memberExpression)
throw new InvalidOperationException("InvalidMemberExpression");
Properties.Add(memberExpression.Member as PropertyInfo);
}
}
Everything was OK until I tried to delete some related objects (aggregate root object with two dependent objects which was marked for cascade deletion): I've got an exception "The relationship could not be changed because one or more of the foreign-key properties is non-nullable". I googled this and found http://blog.abodit.com/2010/05/the-relationship-could-not-be-changed-because-one-or-more-of-the-foreign-key-properties-is-non-nullable/
I changed GetHashCode to base.GetHashCode() and error disappeared. But now it breaks all my code: I can't override GetHashCode for my POCO objects => I can't override Equals => I can't implement Value Object and Entity Object patters for my POCO objects. So, I appreciate any solutions, workarounds here etc.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
如果要重写GetHashCode,则必须直接解决问题。问题是:
所以,
1. 找到用作外键的不可为空字段并使其可为空(这样当您删除记录时 - fk 可以为空)。
2. 不要将依赖标记为级联删除。
If you want to override GetHashCode, you have to solve the problem directly. The problem sais:
So,
1. Find the not nullable field that used as a foreign key and make it nullable (so when you delete the record - the fk can be null).
2. Don't mark the dependency as cascade deletion.