Skip to content

Repository files navigation

TransparentValueObjects

Source generator and analyzer to create Value Objects.

Example

usingSystem;usingTransparentValueObjects;[ValueObject<Guid>]publicreadonlypartialstructMyId{}

The attribute ValueObject<TInnerValue> will generate a partial implementation of this readonly struct with the following defaults:

  • A public readonly TInnerValue Value field.
  • A public static T From(TInnerValue innerValue) method that uses the private constructor.
  • A private constructor used by the From method.
  • A public constructor marked as Obsolete with error: true that will throw an exception if called (this behavior can be overwritten using Augments).
  • GetHashCode and ToString implementations that call the same methods on the inner value.
  • Equality methods: object.Equals, IEquatable<T> and IEquatable<TInnerValue>.
    • == and != operators.
    • Additional Equals method with IEqualityComparer<TInnerValue> parameter.
  • Explicit cast operators.
  • IComparable<T> and IComparable<TInnerValue> if TInnerValue implements IComparable<TInnerValue>.
    • <, <=, > and >= operators.
  • For Guid only: NewId method that calls From(Guid.NewGuid()).

You can check the test files to view the generated output.

Augments

The biggest selling point of this project is the augment feature. You can use the IAugmentWith interface to "augment" your Value Object with additional functionality:

usingSystem;usingTransparentValueObjects;[ValueObject<Guid>]publicreadonlypartialstructSampleGuidValueObject:IAugmentWith<DefaultValueAugment,JsonAugment,EfCoreAugment>{/// <inheritdoc/>publicstaticSampleGuidValueObjectDefaultValue=>From(Guid.Empty);}

The following augments are currently available:

Default Value

Augments the Value Object with the IDefaultValue interface that has a static member DefaultValue:

publicstaticabstractTValueObjectDefaultValue{get;}
usingSystem;usingTransparentValueObjects;[ValueObject<Guid>]publicreadonlypartialstructSampleGuidValueObject:IAugmentWith<DefaultValueAugment>{/// <inheritdoc/>publicstaticSampleGuidValueObjectDefaultValue=>From(Guid.Empty);}

You have to implement the DefaultValue member when using this augment. The public constructor will now also be available:

publicSampleGuidValueObject(){Value=DefaultValue.Value;}

Default Equality Comparer

Augments the Value Object with the IDefaultEqualityComparer interface that has a static member InnerValueDefaultEqualityComparer:

publicstaticabstractIEqualityComparer<TInnerValue>InnerValueDefaultEqualityComparer{get;}
[ValueObject<string>]publicreadonlypartialstructSampleStringValueObject:IAugmentWith<DefaultEqualityComparerAugment>{/// <inheritdoc/>publicstaticIEqualityComparer<string>InnerValueDefaultEqualityComparer=>StringComparer.OrdinalIgnoreCase;}

This default equality comparer will be used by the following methods:

publicboolEquals(SampleStringValueObjectother)=>Equals(other.Value);publicboolEquals(string?other)=>InnerValueDefaultEqualityComparer.Equals(Value,other);publicoverrideintGetHashCode()=>InnerValueDefaultEqualityComparer.GetHashCode(Value);

This augment is especially great for strings if you want to always use the case-insensitive equality comparer.

Json Augment

Augments the Value Object with a JSON converter:

[ValueObject<string>]publicreadonlypartialstructSampleStringValueObject:IAugmentWith<JsonAugment>{}

OnlySystem.Text.Json is currently supported! See #11 for Newtonsoft.Json support.

[JsonConverter(typeof(JsonConverter))]readonlypartialstructSampleStringValueObject{publicclassJsonConverter:JsonConverter<SampleStringValueObject>{/* omitted */}}

The JsonConverterAttribute will be added to the Value Object, meaning that you can use the added converter without needing to manually add it to the JSON options.

The source generator will create a custom converter for the following types:

  • string
  • Guid
  • Int16
  • Int32
  • Int64
  • UInt16
  • UInt32
  • UInt64

All other types will use a fallback converter that fetches an existing converter for TInnerValue.

Note: If the inner value type is a reference type, like string, then you will need to also augment the Value Object with the DefaultValueAugment.

EF Core Augment

Augments the Value Object with a ValueConverter<T, TInnerValue> and ValueComparer<T>:

[ValueObject<string>]publicreadonlypartialstructSampleStringValueObject:IAugmentWith<EfCoreAugment>{}
publicclassEfCoreValueConverter:ValueConverter<SampleStringValueObject,string>{publicEfCoreValueConverter():this(mappingHints:null){}publicEfCoreValueConverter(ConverterMappingHints?mappingHints=null):base(static value =>value.Value,static innerValue =>From(innerValue),mappingHints){}}publicclassEfCoreValueComparer:ValueComparer<SampleStringValueObject>{publicEfCoreValueComparer():base(static(left,right)=>left.Equals(right),static value =>value.GetHashCode(),static value =>From(value.Value)){}/// <inheritdoc/>publicoverrideboolEquals(SampleStringValueObjectleft,SampleStringValueObjectright)=>left.Equals(right);/// <inheritdoc/>publicoverrideSampleStringValueObjectSnapshot(SampleStringValueObjectinstance)=>From(instance.Value);/// <inheritdoc/>publicoverrideintGetHashCode(SampleStringValueObjectinstance)=>instance.GetHashCode();}

License

See LICENSE for details.

About

Source generator for Value Objects.

Topics

Resources

Stars

8 stars

Watchers

1 watching

Forks

Releases

Used by

Contributors

Languages