Abp: Replace ValueObject implementation

Created on 9 Feb 2019  路  5Comments  路  Source: abpframework/abp

The current ValueObject implementation has three drawbacks:

  1. It uses reflection to get the properties,
  2. Unless you know the code it's hard to modify equality, e.g. if equality should be case-insensitive and
  3. the declaration is somewhat awkward with with the generic parameter that has to be the same as the class.

The ValueObject proposed here or here (Microsoft) seems to solve all those issues and seems to me the most commonly used pattern in newer projects.

Example from Microsoft:

```c#
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();
}

public override int GetHashCode()
{
    return GetAtomicValues()
     .Select(x => x != null ? x.GetHashCode() : 0)
     .Aggregate((x, y) => x ^ y);
}        
// Other utilility methods

}

Usage:

```c#
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;
    }
}

I'd suggest using this implementation instead.

abp-framework breaking change enhancement

Most helpful comment

Thank you for your explanation. It seems reasonable, I will change it :)

All 5 comments

I used this instead of the ABP built-in ValueObject

Thank you for your explanation. It seems reasonable, I will change it :)

@hikalkan Will you implement this enhancement for ABP too?

We can not replace the current one since it is a big breaking change, but we may have two base classes (one is existing generic, and other one is this new one). What do you think? I've just created an issue for it: https://github.com/aspnetboilerplate/aspnetboilerplate/issues/4304

since this is a new project and has not release 1.0 , so we can use the new implement,short pain is better than long pain

Was this page helpful?
0 / 5 - 0 ratings

Related issues

wakuflair picture wakuflair  路  3Comments

hikalkan picture hikalkan  路  3Comments

wocar picture wocar  路  3Comments

wocar picture wocar  路  3Comments

ChangYinShung picture ChangYinShung  路  3Comments