This repository has been archived by the owner on Jul 5, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* added Length value object; * added EF converter and tests; * fixed typo; * added changes related to PR comments; * added unit tests for measurement related enum comparisson; --------- Co-authored-by: Dejan Bratic <dejan.bratic@igwplc.com>
- Loading branch information
1 parent
112f838
commit d30d73a
Showing
17 changed files
with
660 additions
and
50 deletions.
There are no files selected for viewing
12 changes: 12 additions & 0 deletions
12
src/Nox.Types.EntityFramework/Types/Length/LengthConverter.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion; | ||
|
||
namespace Nox.Types.EntityFramework.Types; | ||
|
||
public class LengthToFootConverter : ValueConverter<Length, double> | ||
{ | ||
public LengthToFootConverter() : base(length => (double)length.ToFeet(), lengthValue => Length.FromFeet(lengthValue)) { } | ||
} | ||
public class LengthToMeterConverter : ValueConverter<Length, double> | ||
{ | ||
public LengthToMeterConverter() : base(length => (double)length.ToMeters(), lengthValue => Length.FromMeters(lengthValue)) { } | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
|
||
namespace Nox.Common; | ||
|
||
internal sealed class MeasurementConversionFactor | ||
{ | ||
private static readonly Dictionary<(MeasurementTypeUnit, MeasurementTypeUnit), double> DefinedConversionFactors = new() | ||
{ | ||
{ (MeasurementTypeUnit.Foot, MeasurementTypeUnit.Meter), 0.30480000033}, | ||
{ (MeasurementTypeUnit.Meter, MeasurementTypeUnit.Foot), 3.28083989142}, | ||
{ (MeasurementTypeUnit.Kilometer, MeasurementTypeUnit.Mile), 0.62137119102}, | ||
{ (MeasurementTypeUnit.Mile, MeasurementTypeUnit.Kilometer), 1.60934400315}, | ||
{ (MeasurementTypeUnit.SquareFoot, MeasurementTypeUnit.SquareMeter), 0.09290304}, | ||
{ (MeasurementTypeUnit.SquareMeter, MeasurementTypeUnit.SquareFoot), 10.76391042}, | ||
}; | ||
|
||
public double Value { get; } | ||
|
||
public MeasurementConversionFactor(MeasurementTypeUnit sourceUnit, MeasurementTypeUnit targetUnit) | ||
{ | ||
Value = ResolveConversionFactor(sourceUnit, targetUnit); | ||
} | ||
|
||
private static double ResolveConversionFactor(MeasurementTypeUnit sourceUnit, MeasurementTypeUnit targetUnit) | ||
{ | ||
var conversion = (sourceUnit, targetUnit); | ||
|
||
if (DefinedConversionFactors.ContainsKey(conversion)) | ||
return DefinedConversionFactors[conversion]; | ||
|
||
else if (sourceUnit == targetUnit) | ||
return 1; | ||
|
||
throw new NotImplementedException($"No conversion defined from {sourceUnit} to {targetUnit}."); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
namespace Nox.Common; | ||
|
||
internal enum MeasurementTypeUnit | ||
{ | ||
// Length | ||
Foot = 1, | ||
Meter = 2, | ||
Kilometer = 3, | ||
Mile = 4, | ||
|
||
// Area | ||
SquareFoot = 5, | ||
SquareMeter = 6, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,126 @@ | ||
using Nox.Common; | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Globalization; | ||
|
||
namespace Nox.Types; | ||
|
||
/// <summary> | ||
/// Represents a Nox <see cref="Length"/> type and value object. | ||
/// </summary> | ||
public sealed class Length : ValueObject<QuantityValue, Length> | ||
{ | ||
private const int QuantityValueDecimalPrecision = 6; | ||
|
||
public LengthTypeUnit Unit { get; private set; } = LengthTypeUnit.Meter; | ||
|
||
public Length() { Value = 0; } | ||
|
||
/// <summary> | ||
/// Creates a new instance of <see cref="Length"/> object in meters. | ||
/// </summary> | ||
/// <param name="value">The value to create the <see cref="Length"/> with</param> | ||
/// <returns></returns> | ||
/// <exception cref="TypeValidationException"></exception> | ||
public static Length FromMeters(QuantityValue value) | ||
=> From(value, LengthTypeUnit.Meter); | ||
|
||
/// <summary> | ||
/// Creates a new instance of <see cref="Length"/> object in feet. | ||
/// </summary> | ||
/// <param name="value">The origin value to create the <see cref="Length"/> with</param> | ||
/// <returns></returns> | ||
/// <exception cref="TypeValidationException"></exception> | ||
public static Length FromFeet(QuantityValue value) | ||
=> From(value, LengthTypeUnit.Foot); | ||
|
||
/// <summary> | ||
/// Represents a Nox <see cref="Length"/> type and value object. | ||
/// Creates a new instance of <see cref="Length"/> object in meters. | ||
/// </summary> | ||
/// <remarks>Placeholder, needs to be implemented</remarks> | ||
public sealed class Length : ValueObject<QuantityValue, Length> | ||
/// <param name="value">The value to create the <see cref="Length"/> with</param> | ||
/// <returns></returns> | ||
/// <exception cref="TypeValidationException"></exception> | ||
public new static Length From(QuantityValue value) | ||
=> From(value, LengthTypeUnit.Meter); | ||
|
||
/// <summary> | ||
/// Creates a new instance of <see cref="Length"/> object with the specified <see cref="LengthTypeUnit"/>. | ||
/// </summary> | ||
/// <param name="value">The value to create the <see cref="Length"/> with</param> | ||
/// <param name="unit">The <see cref="LengthTypeUnit"/> to create the <see cref="Length"/> with</param> | ||
/// <returns></returns> | ||
/// <exception cref="TypeValidationException"></exception> | ||
public static Length From(QuantityValue value, LengthTypeUnit unit) | ||
{ | ||
var newObject = new Length | ||
{ | ||
Value = Round(value), | ||
Unit = unit, | ||
}; | ||
|
||
var validationResult = newObject.Validate(); | ||
|
||
if (!validationResult.IsValid) | ||
{ | ||
throw new TypeValidationException(validationResult.Errors); | ||
} | ||
|
||
return newObject; | ||
} | ||
|
||
/// <summary> | ||
/// Validates a <see cref="Length"/> object. | ||
/// </summary> | ||
/// <returns>true if the <see cref="Length"/> value is valid.</returns> | ||
internal override ValidationResult Validate() | ||
{ | ||
var result = Value.Validate(); | ||
|
||
if (Value < 0 && !double.IsNaN((double)Value) && !double.IsInfinity((double)Value)) | ||
{ | ||
result.Errors.Add(new ValidationFailure(nameof(Value), $"Could not create a Nox Length type as negative length value {Value} is not allowed.")); | ||
} | ||
|
||
if (!Enum.IsDefined(typeof(LengthTypeUnit), Unit)) | ||
{ | ||
result.Errors.Add(new ValidationFailure(nameof(Unit), $"Could not create a Nox Length type as unit {Unit} is not supported.")); | ||
} | ||
|
||
return result; | ||
} | ||
|
||
protected override IEnumerable<KeyValuePair<string, object>> GetEqualityComponents() | ||
{ | ||
yield return new KeyValuePair<string, object>(nameof(Value), ToMeters()); | ||
} | ||
|
||
public override string ToString() | ||
=> $"{Value.ToString($"0.{new string('#', QuantityValueDecimalPrecision)}", CultureInfo.InvariantCulture)} {Unit.ToSymbol()}"; | ||
|
||
/// <summary> | ||
/// Returns a string representation of the <see cref="Length"/> object using the specified <see cref="IFormatProvider"/>. | ||
/// </summary> | ||
/// <param name="formatProvider">The format provider for the length value.</param> | ||
/// <returns>A string representation of the <see cref="Length"/> object with the value formatted using the specified <see cref="IFormatProvider"/>.</returns> | ||
public string ToString(IFormatProvider formatProvider) | ||
=> $"{Value.ToString(formatProvider)} {Unit.ToSymbol()}"; | ||
|
||
private QuantityValue? _meters; | ||
|
||
public QuantityValue ToMeters() | ||
=> (_meters ??= GetLengthIn(LengthTypeUnit.Meter)); | ||
|
||
private QuantityValue? _feet; | ||
|
||
public QuantityValue ToFeet() | ||
=> (_feet ??= GetLengthIn(LengthTypeUnit.Foot)); | ||
|
||
private QuantityValue GetLengthIn(LengthTypeUnit targetUnit) | ||
{ | ||
var factor = new MeasurementConversionFactor((MeasurementTypeUnit)Unit, (MeasurementTypeUnit)targetUnit).Value; | ||
return Round(Value * factor); | ||
} | ||
|
||
private static QuantityValue Round(QuantityValue value) | ||
=> Math.Round((double)value, QuantityValueDecimalPrecision); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
using System; | ||
|
||
namespace Nox.Types; | ||
|
||
public enum LengthTypeUnit | ||
{ | ||
Foot = 1, | ||
Meter = 2, | ||
} | ||
|
||
public static class LengthTypeUnitExtensions | ||
{ | ||
public static string ToSymbol(this LengthTypeUnit unit) | ||
{ | ||
return unit switch | ||
{ | ||
LengthTypeUnit.Foot => "ft", | ||
LengthTypeUnit.Meter => "m", | ||
_ => throw new NotImplementedException($"No symbol defined for unit {unit}.") | ||
}; | ||
} | ||
} |
Oops, something went wrong.