DDD Entity class - c#

I'm trying to implement DDD approach in my head.
So far I know that entity is unique and identified by combination of its attributes.
Entity is abstract class which will be implemented by other entity classes.
I know so far that Version property is used to manage concurrency.
Need help with rest of this class.
I'm in process of learning DDD so please describe your thoughts on this or share useful concrete theme links.
public abstract class Entity<T>
{
#region Properties
protected T _Id;
private int _Version;
#endregion
private static bool IsTransient(Entity<T> obj)
{
return (obj != null) &&
Equals(obj.Id, default(T));
}
private Type GetUnproxiedType()
{
return GetType();
}
public virtual T Id
{
get
{
return _Id;
}
protected set
{
_Id = value;
}
}
public virtual int Version
{
get
{
return _Version;
}
set
{
_Version = value;
}
}
public override bool Equals(object obj)
{
return Equals(obj as Entity<T>);
}
public virtual bool Equals(Entity<T> 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(T)))
{
return base.GetHashCode();
}
return Id.GetHashCode();
}
public static bool operator ==(Entity<T> e1, Entity<T> e2)
{
return Equals(e1, e2);
}
public static bool operator !=(Entity<T> e1, Entity<T> e2)
{
return !Equals(e1, e2);
}
}
Updated
After some researching I'm concluded with this
Using Equals method will come handy when we try to compare two objects. For example imaging place in application where we have the same object in more than one instance. One from the database and one from the client. We need to find out if that object is identical and yet not the same.
For this we will be using Equals method.

An entity is not uniquely identified by the value of its attributes; An entity has an identity, but that does not mean that the identity is made up by the value of its attributes.
That would mean that, if you change one if the attributes of an entity, it's identity will change, and you do not want that behaviour.
(For instance, if your address changes, your identity doesn't change, you're still the same person, living at another address).
(I see that you've implemented identity/equality by using an Id property, which is a good thing).
Next to entities, you have value - objects. The identity of a value object is made up by the values of its attributes, and therefore, it is better that a value object is immutable, since, changing the value object would also mean that you change its identity.
But, what is your question ?

Related

Equals is not implemented

I have the following class in C# 10:
public sealed record Country : IEquatable<Country>
{
public string Language { get; set; } = "xx";
public bool Equals(Country? other)
{
if (other is null)
{
return false;
}
return Language == other.Language;
}
public override int GetHashCode() => Language.GetHashCode();
}
Which when compiling will bring the following compile time error:
Interface member 'bool System.IEquatable<Country?>.Equals(Country?)' is not implemented
Which is... a very weird error message for the type in question. I tried varying the nullability of the Country parameter, and I tried generating the "missing" method, which will generate the following method for my IDE:
public bool Equals(Country? other) => throw new NotImplementedException();
And then, of course, the error switches to
Member with the same signature is already declared
Generating the Equals() method directly will just override the existing method.
What is going on here? Not all of my colleagues have the same error message for this class, so it might just be something that has nothing to do with the class. (I think I'm the only one on Windows, for example.) dotnet build works, so it might just be a problem with Rider 2021.3.2, but that's the IDE my colleagues use as well.
Its related to your record usage, records already implement this interface and IEquatable is not needed
https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-9.0/records#equality-members
Quick test in Visual studio and its likely IDE issue.
Records implement equality automatically: that's one of the main advantages of using one.
We can see this in SharpLab. If we paste in:
public sealed record Country
{
public string Language { get; set; } = "xx";
}
and select "Results: C#", we can see that the generated code includes:
public override int GetHashCode()
{
return EqualityComparer<Type>.Default.GetHashCode(EqualityContract) * -1521134295 + EqualityComparer<string>.Default.GetHashCode(<Language>k__BackingField);
}
[System.Runtime.CompilerServices.NullableContext(2)]
public override bool Equals(object obj)
{
return Equals(obj as Country);
}
[System.Runtime.CompilerServices.NullableContext(2)]
public bool Equals(Country other)
{
return (object)this == other || ((object)other != null && EqualityContract == other.EqualityContract && EqualityComparer<string>.Default.Equals(<Language>k__BackingField, other.<Language>k__BackingField));
}
as well as definitions of the == and != operators.
See here.
If you do want to override equality in a record, then you do that as you have done, by specifying your own bool Equals(Counter? other) and int GetHashCode() methods. There's no need to specify that the record implements IEquatable<Country> however, because the compiler will automatically add this interface for records.
Note that it's good practice to put in a test for EqualityContract == other.EqualityContract. This is used to make sure that you're not comparing a record of type Country with one which is a subclass of Country:
public bool Equals(Country? other)
{
if (ReferenceEquals(this, other))
return true;
if (other is null || EqualityContract != other.EqualityContract)
return false;
return Language == other.Language;
}
public override int GetHashCode() => Language.GetHashCode();
See on SharpLab.

Implement Equals to make either reference equality or key-based equality?

I would like to override the Equals and GetHashCode of my EntityBase class to support checking equality based on reference (as by default) or by the entity key (in case the references not matching).
Here is the code:
public abstract class EntityBase
{
protected virtual object Keys { get { return this; } }
public override bool Equals(object obj)
{
if (Keys == this) return base.Equals(obj);
var entity = obj as EntityBase;
if (entity == null) return false;
var re = ReferenceEquals(entity, this);
return re || Equals(entity.Keys, Keys);
}
public override int GetHashCode()
{
if (Keys == this) return base.GetHashCode();
return base.GetHashCode() * 17 + (Keys?.GetHashCode() ?? 0);
}
}
Now in the derived class it can be like this:
public class Entity : EntityBase {
protected override object Keys {
get {
return SomeKeyProperty;
}
}
}
So I expect that it should work, but the BindingSource I'm using showed that it does not work, like this:
//the myEntity here is contained (as reference) in myBindingSource
var index = myBindingSource.IndexOf(myEntity);
The code above gives the correct result if I don't override Equals for my EntityBase class, but with that overriding, the result will be wrong, looks like it always tries to look for the item based on the Keys value. I don't really understand what's wrong here.
When debugging it does not even hit the breakpoint I set in the Equals method. The code just runs through the IndexOf call like as it's a black box.
Could you explain to me what's wrong here and give me some suggestion about a possible fix (or even let me know if what I want to achieve is possible).
Actually the code I posted runs just like what it should. I want that the reference equality should have higher priority, but really when finding an item (like IndexOf) in a collection, what comes first will be the one found (if the references does not match then keys will be used).
The reason it did not work as I expect because the 2 ones compared here both have Keys equal to 0. So if key-based equality is used, that case should be considered as unequal. I need to add another property to determine which value is considered as null or empty Keys. Here is the code working as I expected:
public abstract class EntityBase
{
protected virtual object Keys { get { return this; } }
protected virtual object EmptyKeys {get { return null;} }
public override bool Equals(object obj)
{
if (Keys == this) return base.Equals(obj);
var entity = obj as EntityBase;
if (entity == null) return false;
var re = ReferenceEquals(entity, this);
return re || GetType() == entity.GetType() && Equals(entity.Keys, Keys) && !Equals(Keys, EmptyKeys);
}
public override int GetHashCode()
{
if (Keys == this) return base.GetHashCode();
return base.GetHashCode() * 17 + (Keys?.GetHashCode() ?? 0);
}
}
In the derived class, we need to override the EmptyKeys to indicate which value is considered as empty, NOTE that for numeric key (like long, ...) the value for EmptyKeys should be of the exact type with Keys, otherwise the Equals won't work.
public class Entity : EntityBase {
protected override object Keys {
get {
return SomeKeyProperty;//suppose this is a long property
}
}
protected override object EmptyKeys {
get {
return 0L;//here 0 value should also be a long value.
}
}
}
After that adjusting, the code works expectedly.

How do the `EqualOperator()` and `NotEqualOperator()` methods work in this `ValueObject` implementation (Microsoft Docs)?

In Domain Driven Design, we're introduced to the concept of a ValueObject, where objects don't carry an identity.
Microsoft have provided an implementation of their ValueObject in their Microservices series, where they override Equals() so that two ValueObject's with the same values are considered identical.
I've included their implementation below, but my question is relating to the EqualOperator() and NotEqualOperator() methods - how does this work? when are they called?
I'm familiar with operator overloads, but this seems to be an implementation I've not seen before, and I can't find any documentation around it.
Here is the implementation:
public abstract class ValueObject
{
protected static bool EqualOperator(ValueObject left, ValueObject right)
{
if (ReferenceEquals(left, null) ^ ReferenceEquals(right, null))
{
return false;
}
return ReferenceEquals(left, null) || left.Equals(right);
}
protected static bool NotEqualOperator(ValueObject left,
ValueObject right)
{
return !(EqualOperator(left, right));
}
protected abstract IEnumerable<object> GetAtomicValues();
public override bool Equals(object obj)
{
if (obj == null || obj.GetType() != GetType())
{
return false;
}
ValueObject other = (ValueObject)obj;
IEnumerator<object> thisValues = GetAtomicValues().GetEnumerator();
IEnumerator<object> otherValues =
other.GetAtomicValues().GetEnumerator();
while (thisValues.MoveNext() && otherValues.MoveNext())
{
if (ReferenceEquals(thisValues.Current, null) ^
ReferenceEquals(otherValues.Current, null))
{
return false;
}
if (thisValues.Current != null &&
!thisValues.Current.Equals(otherValues.Current))
{
return false;
}
}
return !thisValues.MoveNext() && !otherValues.MoveNext();
}
// Other utilility methods
}
Here's an example of their object in use:
public class Address : ValueObject
{
public String Street { get; private set; }
public String City { get; private set; }
public String State { get; private set; }
public String Country { get; private set; }
public String ZipCode { get; private set; }
private Address() { }
public Address(string street, string city, string state, string country,
string zipcode)
{
Street = street;
City = city;
State = state;
Country = country;
ZipCode = zipcode;
}
protected override IEnumerable<object> GetAtomicValues()
{
// Using a yield return statement to return
// each element one at a time
yield return Street;
yield return City;
yield return State;
yield return Country;
yield return ZipCode;
}
}
Actually, I find it astonishing that Microsoft implemented a value type using a class. Usually, structs are much better for this purpose, unless your value objects get very large. For most usages of value types such as coordinates or colors, this is not the case.
Leaving this discussion aside, what happens here is the following: If you implement a value object, you need to implement Equals and GetHashCode correctly, which includes consistently to each other. However, these two methods are actually not very difficult but verbose to implement: You need to cast the object and then check each of its properties. In case you are using classes, you have an additional boilerplate factor which is the fact that you typically want to speed up equality check using the reference equality check. That is, two objects need not be the same to be equal, but if they are the same, then they are also equal.
The class you depicted here is an attempt to support this consistency problem and boilerplate problem for value objects that are using classes by abstracting away quite a few commonalities. All you need to provide are the fields that make up the identity. In most cases, these are simply all fields. You iterate them using a co-method.
Now, for the actual question of when EqualOperator and NotEqualOperator are actually called, I would guess they are simply helper functions to make the implementation of operators more easy: You would provide an overloaded == operator that simply returns EqualOperator and != that simply returns NotEqualOperator. You might ask why the value type base class does not have these operators? Well, I guess this is because this would mean that the compiler would allow you to apply == and != to different types of value objects using the overloaded operator.

Entity typed primary key IDs with Entity Framework - expression tree insanity

I want to find a way to get strongly-typed primary keys in EF Code First. My goal is to make it impossible to accidentally assign one type of ID to another. Here's an example of what an entity with such a beast applied to it might look like:
public partial class AccessMethod : BaseEntity
{
internal virtual Guid ID_
{
get { return ID.Value; }
set { ID = new ID<AccessMethod>(value); }
}
public ID<AccessMethod> ID { get; set; }
internal virtual Guid LocationID_
{
get { return LocationID.Value; }
set { LocationID = new ID<Location>(value); }
}
public ID<Location> LocationID { get; set; }
public virtual string Description { get; set; }
#region Navigation properties
public virtual Location Location { get; set; }
#endregion
}
Here's the code for ID<>:
public class StrongID<T, TEntity> : IEquatable<StrongID<T, TEntity>>
{
readonly T _value;
public StrongID()
{
_value = default(T);
}
public StrongID(T value)
{
_value = value;
}
public T Value { get { return _value; } }
public override bool Equals(object obj)
{
if (obj is StrongID<T, TEntity>)
{
return Equals(obj as StrongID<T, TEntity>);
}
return false;
}
public override int GetHashCode()
{
return base.GetHashCode() ^ (_value == null ? 0 : _value.GetHashCode());
}
public override string ToString()
{
return "ID<" + typeof(T).Name + ">(" + _value + ")";
}
public bool Equals(StrongID<T, TEntity> other)
{
if (other == null) return false;
var thisIsNull = this._value == null;
var otherIsNull = other._value == null;
if (thisIsNull || otherIsNull) return thisIsNull == otherIsNull;
return this._value.Equals(other._value);
}
//commented for now
//public static implicit operator T(StrongID<T, TEntity> id)
//{
// return id.Value;
//}
}
public class ID<TEntity> : StrongID<Guid, TEntity>
where TEntity : BaseEntity
{
public ID() { }
public ID(Guid id) : base(id) { }
}
The idea is that EF is made aware of ID_, but this property is hidden from the rest of the project and is exposed through ID, which as you can see includes the type of the entity.
Since I'm using explicit mappings for my entities, I was able to write a base class which uses reflection to automatically ignore StrongID<> properties and map properties with the same name plus an underscore to a column with the underscore removed.
I know that EF doesn't support custom types at all for primary keys, only .NET's built-in scalar types. So I'm going the insane route of modifying the expression tree of all queries, changing all accesses to ID to point to ID_ instead (and do the same for other ID<> properties like LocationID).
I have an IQueryProvider wrapper that invokes an ExpressionVisitor in its CreateQuery() methods. This seems to work fine. I was going to try hooking into the Execute() methods instead, but ToList() uses GetEnumerator(), which never calls Execute(), so that wasn't going to work. This way will probably work better in the end anyway.
Unfortunately, this is where my plan starts to fall apart. Expression trees are ridiculously complicated to work with and have many nuances about them, and it's impossible to know where you'll have access to what without running real code and examining things.
This is what I have so far:
public class ConvertStrongIDExpressionVisitor : ExpressionVisitor
{
protected override Expression VisitMember(MemberExpression node)
{
if (node.Expression is ParameterExpression)
{
if (IsTypeStrongID(node.Type))
{
var ntex = node.Expression as ParameterExpression;
if (typeof(BaseEntity).IsAssignableFrom(ntex.Type))
{
//ntex.Type is an entity type, node refers to a strongid member on that type
var property = ntex.Type.GetProperty(node.Member.Name + "_", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
return Expression.MakeMemberAccess(node.Expression, property);
}
}
}
return base.VisitMember(node);
}
protected override Expression VisitConstant(ConstantExpression node)
{
if (IsTypeStrongID(node.Type))
{
var value = node.Type.GetProperty("Value").GetMethod.Invoke(node.Value, null);
return Expression.Constant(value);
}
return base.VisitConstant(node);
}
protected override Expression VisitLambda<T>(Expression<T> node)
{
return base.VisitLambda<T>(node);
}
bool IsTypeStrongID(Type t)
{
var strongIDType = typeof(StrongID<,>);
while (t != null)
{
if (t.IsGenericType && t.GetGenericTypeDefinition() == strongIDType) return true;
t = t.BaseType;
}
return false;
}
}
I'm not sure what other methods I will need to override, and I have no idea if I'm using ExpressionVisitor correctly to begin with. VisitMember() and VisitConstant() both seem to work okay. Currently, I get this exception (it happens to be generated while in VisitLambda()):
Reference equality is not defined for the types 'System.Guid' and 'Core.ID`1[Core.Domain.Location]'.
The expression that it's barfing on is this:
o => o.LocationID == new ID<Location>(locationID)
I thought that the expression new ID<Location>(locationID) was being turned into a constant? It seems to not be? This is why I'm unsure of my usage of ExpressionVisitor. It seems like expressions don't get visited in any particular order. Or maybe I'm doing something wrong.
UGH, I just realized that this won't work with anonymous types at all, because they won't have the mirror properties. I'd have to dynamically create a new type, swap it into the expression tree, and fix up everywhere that any of the original type's properties is referenced. Doable, but that sounds like a NIGHTMARE.
And then there's the currently-unknown performance implications of all of the above...
So I guess I'm wondering things like, does anyone have any ideas as to how I can make this concept work well? Has anyone tried anything like this before, either with EF or another ORM? Should I submit a feature request to the EF folks and start a discussion there?
Your thoughts are appreciated!

Equals implementation of NHibernate Entities, unproxy question

In NHibernate 3.0 Cookbook, there is a sample implementation for a base Entity type. The equals is implemented like this:
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(this) && Equals(Id, other.Id))
{
var otherType = other.GetUnproxiedType();
var thisType = GetUnproxiedType();
return thisType.IsAssignableFrom(otherType) ||
otherType.IsAssignableFrom(thisType);
}
return false;
}
}
The reason for the GetUnproxiedType() method is this: There is an abstract base class Product, a concrete class Book which inherits from Product and a dynamic proxy class ProductProxy used by NHibernate for lazy loading. If a ProductProxy representing a Book and a concrete Book have the same Ids, they should be treated as equal. However I don't really see why calling GetType() on a ProductProxy instance should return Product in this case, and how it helps. Any ideas?
I actually went ahead and wrote to the author of the book about this code. It turns out this is due to how the proxy wrapping works. Here is his response:
"If you don't understand how the proxy frameworks work, the idea can seem magical.
When NHibernate returns a proxy for the purposes of lazy loading, it returns a proxy instance inherited from the actual type. There are a few members we can access without forcing a load from the database. Among these are proxy's Id property or field, GetType(), and in some circumstances Equals() and GetHashCode(). Accessing any other member will force a load from the database.
When that happens, the proxy creates an internal instance. So, for example, a lazy loaded instance of Customer (CustomerProxy102987098721340978), when loaded, will internally create a new Customer instance with all of the data from the database. The proxy then does something like this:
public overrides string Name
{
get {
return _loadedInstance.Name;
}
set { _loadedInstance.Name = value; }
}
Incidentally, it's this overriding that requires everything to be virtual on entities that allow lazy loaded.
So, all calls to the Name property on the proxy are relayed to the internal Customer instance that has the actual data.
GetUnproxiedType() takes advantage of this. A simple call to GetType() on the proxy will return typeof(CustomerProxy02139487509812340). A call to GetUnproxiedType() will be relayed to the internal customer instance, and the internal customer instance will return typeof(Customer)."
With current (v5.x) NHibernate proxy factories (static or dynamic, static being available since v5.1), this pattern is actually broken. The v5 built-in proxy factories do not intercept private methods.
And I think that was already the case for v4 ones.
For this pattern to work with current built-in proxy factories, GetUnproxiedType should be virtual (so not private by the way, but protected).
Otherwise, use NHibernateUtil.GetClass, which is meant for this and does not rely on brittle tricks. Its documentation warns it will initialize the proxy by side effect, but anyway the GetUnproxiedType trick must do the same for working.
Of course using NHibernateUtil.GetClass means having a direct dependency to NHibernate in a domain model base class. But depending on an implementation trick specific to an external (from the domain viewpoint) library implementation is no better in my opinion.
Moreover, some changes may cause the GetUnproxiedType trick to be even more broken in the future, like some ideas for reducing the number of cases causing a proxy to get initialized when it could be avoided. (See here by example.)
If you really want a GetUnproxiedType method not depending on a direct NHibernate reference, I think the only theoretically "safe" solution is to have it abstract and overridden in each concrete entity class for yielding typeof(YourEntityClass). But in practice, it would be cumbersome and error-prone (bad copy-paste for creating a new entity, forgetting to change that method...), while the abstract part won't help in case some concrete entity classes are further specialized through inheritance.
Another trick could be, from the type obtained by GetType, to check to which assembly it belongs (the proxy type will not belong to one of your assemblies), for searching the first type in the hierarchy belonging to your domain model assembly(ies).
Note that if the proxy is a proxy of a base class, not of a concrete class, and your helper method is set as private, it will yield the base class type, without initializing the proxy. Performance-wise, this is better. While a virtual GetUnproxiedType simply returning GetType would return the concrete class type with current proxy factories, but it would also initialize the proxy.
We use NH 2 and this example did not work for us. (It FAILED to unproxy the type and left proxy type, see below).
It said that 2 entities with the same id are not equal, when one of them is proxy(of COrganization) and other is not(DOrganization).
When we had a hierarchy:
class Organization
class AOrganization : Organization
class COrganization : Organization
{
public virtual COrganization GetConcrete()
{
return null;
}
}
class DOrganization : COrganization
{
public virtual COrganization GetConcrete()
{
return this;
}
}
AOrganization aOrganization;
COrganization cOrganization;
contract = new CContract(aOrganization, cOrganization as COrganization); //(COrganization)(cOrganization.GetConcrete()),
So CContract has a field of type COrganization. With a setter
public class Contract: Entity <short>
{
public virtual COrganization COrganization
{
get { return cOrganization; }
protected internal set
{
if (cOrganization != null && value != cOrganization) // != calls ==, which calls Equals, which calls GetUnproxiedType()
throw new Exception("Changing organization is not allowed.");
}
cOrganization = value;
}
}
private COrganization cOrganization;
}
We constructed new Contract, its constructor set the COrganization field pointing to some organization. Then we called UnitOfWork.Commit, NH tried to set COrganization field again (with the same id), GetUnproxiedType worked incorrectly, new and old values were recognized as non-equal, and exception was thrown...
Here is the place where error showed up:
var otherType = other.GetUnproxiedType();
var thisType = GetUnproxiedType();
return thisType.IsAssignableFrom(otherType) ||
otherType.IsAssignableFrom(thisType);
In debugger: otherType == COrganizationProxy - GetUnproxiedType failed...
thisType == DOrganization
COrganizationProxy and DOrganization both inherit COrganization.
So they are not IsAssignableFrom for each other...
Why does this example work for you?
Maybe because we have NH 2.0 or 2.1?
Or because of simple "cOrganization as COrganization" instead of "(COrganization)(cOrganization.GetConcrete())"?
Or because we have implementation of ==, != and Equals not only in Entity, but in Organization too?
public abstract class Organization : Entity<int>
{
public override bool Equals(object obj)
{
return base.Equals(obj);
}
public override int GetHashCode()
{
return base.GetHashCode();
}
public static bool operator ==(Organization object1, Organization object2)
{
return AreEqual(object1, object2);
}
public static bool operator !=(Organization object1, Organization object2)
{
return AreNotEqual(object1, object2);
}
}
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();
}
/// This method added by me
/// For == overloading
protected static bool AreEqual<TEntity>(TEntity entity1, TEntity entity2)
{
if ((object)entity1 == null)
{
return ((object)entity2 == null);
}
else
{
return entity1.Equals(entity2);
}
}
/// This method added by me
/// For != overloading
protected static bool AreNotEqual<TEntity>(TEntity entity1, TEntity entity2)
{
return !AreEqual(entity1, entity2);
}
}

Categories

Resources