2

私はEntityFrameworkをORMとして使用しており、値オブジェクトとエンティティオブジェクトパターン(Evans)を表す2つの基本クラスを持つ単純なPOCOドメインモデルがあります。これらの2つのパターンはすべて、2つのオブジェクトの同等性に関するものであるため、EqualsメソッドとGetHashCodeメソッドをオーバーライドします。これらの2つのクラスは次のとおりです。

 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);
        }
    }

いくつかの関連オブジェクト(カスケード削除のマークが付けられた2つの依存オブジェクトを持つ集約ルートオブジェクト)を削除しようとするまで、すべて問題ありませんでした。「1つ以上の外部キープロパティがあるため、関係を変更できませんでした。 null不可です」。私はこれをグーグルで検索し、http://blog.abodit.com/2010/05/the-relationship-could-not-be-changed-because-one-or-more-of-the-foreign-key-properties-isを見つけました-null不可/ GetHashCodeをbase.GetHashCode()に変更すると、エラーが消えました。しかし、今ではすべてのコードが壊れています。POCOオブジェクトのGetHashCodeをオーバーライドできません=>Equalsをオーバーライドできません=>POCOオブジェクトの値オブジェクトとエンティティオブジェクトのパターンを実装できません。だから、私はここでの解決策、回避策などに感謝します。

4

1 に答える 1

0

GetHashCode をオーバーライドしたい場合は、問題を直接解決する必要があります。問題は次のように述べています。

"The relationship could not be changed because one or more of the foreign-key properties is non-nullable"

したがって、
1.外部キーとして使用されているnull不可のフィールドを見つけて、null可能にします(レコードを削除すると、fkがnullになる可能性があります)。
2. 依存関係をカスケード削除としてマークしないでください。

于 2011-05-04T03:01:33.640 に答える