Implement ObjectKey-Based IEquatable in Entity Base Class

Thread is closed for posting
1 posts, 0 answers
  1. 0DF06410-134C-45EF-8878-7D60E5A4B081
    0DF06410-134C-45EF-8878-7D60E5A4B081 avatar
    93 posts
    Member since:
    Jul 2007

    Posted 29 Sep 2011 Link to this post

    Requirements

    OpenAccess ORM  version 2011.2.908.1
    .NET version 3.5+
    Visual Studio version 2010
    Programming language VB/C#

    PROJECT DESCRIPTION
    Sometimes when OA entities are disconnected (for example, due to loading from different domain contexts or from stored procedures or due to the use of a caching or storage mechanism), two copies of the same entity will not return true from a call to object.equals.  We can fix this problem by implementing IEquatable and using ObjectKeys.

    You can do this in one of a few ways.  The attached example builds on my other example that creates a common base class for all entities using code generation templates.  You can alternatively manually create the base class and create partial class definitions for all of your entities and add the inheritance on each entity.  The other option is to create a helper class that implements IEqualityComparer<T> or IEqualityComparer and use this whenever you need an ObjectKey-based equality comparison.

    In order to implement the IEquatable<T> interface and override the base equals and GetHashCode functions, all you need to add is the following code to your base entities class (I'm assuming your base class is called EntitiesBase - change this as needed):

    C#:

    using System;
    using Telerik.OpenAccess;
    using Telerik.OpenAccess.SPI.dataobjects;
     
    namespace SofiaCarRental
    {
        public partial class EntitiesBase : IEquatable<EntitiesBase>
        {
            bool System.IEquatable<EntitiesBase>.Equals(EntitiesBase other)
            {
                if (ReferenceEquals(this, other))
                {
                    return true;
                }
                if (other == null)
                {
                    return false;
                }
                if (Database.GetContext(this) != null && Database.GetContext(other) != null)
                {
                    object myKey = ObjectKey.CreateWithVersion(this);
                    object otherKey = ObjectKey.CreateWithVersion(other);
     
                    return myKey.Equals(otherKey);
                }
                else
                {
                    return base.Equals(other);
                }
            }
     
            public override bool Equals(object obj)
            {
                if (ReferenceEquals(this, obj))
                {
                    return true;
                }
                if (obj == null)
                {
                    return false;
                }
                object pcObj = obj as PersistenceCapable;
                if (pcObj != null)
                {
                    if (Database.GetContext(this) != null && Database.GetContext(pcObj) != null)
                    {
                        object myKey = ObjectKey.CreateWithVersion(this);
                        object otherKey = ObjectKey.CreateWithVersion(pcObj);
     
                        return myKey.Equals(otherKey);
                    }
                }
                //otherwise, use base implementation
                return base.Equals(obj);
            }
     
            public override int GetHashCode()
            {
                if (Database.GetContext(this) != null)
                {
                    object myKey = ObjectKey.CreateWithVersion(this);
                    return myKey.GetHashCode();
                }
                else
                {
                    return base.GetHashCode();
                }
            }
     
        }
    }

    VB:

    Imports Telerik.OpenAccess
    Imports Telerik.OpenAccess.SPI.dataobjects
     
    Partial Public Class EntitiesBase
        Implements IEquatable(Of EntitiesBase)
     
        Public Function Equals1(other As EntitiesBase) As Boolean Implements System.IEquatable(Of EntitiesBase).Equals
            If ReferenceEquals(Me, other) Then
                Return True
            End If
            If other Is Nothing Then
                Return False
            End If
            If Database.GetContext(Me) IsNot Nothing AndAlso Database.GetContext(other) IsNot Nothing Then
                Dim myKey = ObjectKey.CreateWithVersion(Me)
                Dim otherKey = ObjectKey.CreateWithVersion(other)
     
                Return myKey.Equals(otherKey)
            Else
                Return MyBase.Equals(other)
            End If
        End Function
     
        Public Overrides Function Equals(obj As Object) As Boolean
            If ReferenceEquals(Me, obj) Then
                Return True
            End If
            If obj Is Nothing Then
                Return False
            End If
            Dim pcObj = TryCast(obj, PersistenceCapable)
            If pcObj IsNot Nothing Then
                If Database.GetContext(Me) IsNot Nothing AndAlso Database.GetContext(pcObj) IsNot Nothing Then
                    Dim myKey = ObjectKey.CreateWithVersion(Me)
                    Dim otherKey = ObjectKey.CreateWithVersion(pcObj)
     
                    Return myKey.Equals(otherKey)
                End If
            End If
            'otherwise, use base implementation
            Return MyBase.Equals(obj)
        End Function
     
        Public Overrides Function GetHashCode() As Integer
            If Database.GetContext(Me) IsNot Nothing Then
                Dim myKey = ObjectKey.CreateWithVersion(Me)
                Return myKey.GetHashCode()
            Else
                Return MyBase.GetHashCode()
            End If
        End Function
     
    End Class

    In implementation using IEqualityComparer<T> or IEqualityComparer would be very similar to the System.IEquatable<EntitiesBase>.Equals and Equals() definitions, respectively.

    Please note that if you are using my common base class example, do not put this code in the EntitiesBase.generated.[cs/vb] file or it will be overwritten.  Also, as with the common base class example, be aware that you will need to update the location of the code generation template and the RLINQ file before using the sample.  To do this, open the domain model with an XML editor, find the following XML paths and update the node values (representing directories and files) to match your local ones:
    DomainModel \ ModelSettings \ CodeGenerationSettings \ OutputPath - the project folder
    DomainModel \ ModelSettings \ CodeGenerationSettings \ CustomTemplateFileName -  location of the DefaultTemplate[Language].tt file
    DomainModel \ ModelSettings \ SchemaUpdateSetting \ DeploymentDirectory - the project folder

    The example solution also includes a console application the demonstrates the difference between the base object.equals and the ObjectKey based implementation.

    Hope this helps.
Back to Top

This Code Library is part of the product documentation and subject to the respective product license agreement.