Skip to content

Commit

Permalink
Allow conversion of Etag in EFCore to byte[], int, uint, `lon…
Browse files Browse the repository at this point in the history
…g`, or `ulong` (#306)

Depending on the database type, the row version may have different types.
SQL Server uses `byte[]` for row version, Postgres uses `uint` for `xid`/`xmin`, while the new Mongo driver for EFCore can use `int`, `uint`, `long`, or `ulong` since it is developer driven.

This also allows a concurrency setups that are not not entirely database driven.
  • Loading branch information
mburumaxwell authored Aug 17, 2024
1 parent 23ae5bd commit f17116c
Show file tree
Hide file tree
Showing 3 changed files with 148 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,47 @@
namespace Tingle.Extensions.EntityFrameworkCore.Converters;

///
public class EtagConverter : ValueConverter<Etag, byte[]>
public class EtagToBytesConverter : ValueConverter<Etag, byte[]>
{
///
public EtagConverter() : base(convertToProviderExpression: v => v.ToByteArray(),
convertFromProviderExpression: v => v == null ? default : new Etag(v))
public EtagToBytesConverter() : base(convertToProviderExpression: v => v.ToByteArray(),
convertFromProviderExpression: v => v == null ? default : new Etag(v))
{ }
}

///
public class EtagToInt32Converter : ValueConverter<Etag, int>
{
///
public EtagToInt32Converter() : base(convertToProviderExpression: v => Convert.ToInt32((ulong)v),
convertFromProviderExpression: v => new Etag(Convert.ToUInt64(v)))
{ }
}

///
public class EtagToUInt32Converter : ValueConverter<Etag, uint>
{
///
public EtagToUInt32Converter() : base(convertToProviderExpression: v => Convert.ToUInt32((ulong)v),
convertFromProviderExpression: v => new Etag(v))
{ }
}

///
public class EtagToInt64Converter : ValueConverter<Etag, long>
{
///
public EtagToInt64Converter() : base(convertToProviderExpression: v => Convert.ToInt64((ulong)v),
convertFromProviderExpression: v => new Etag(Convert.ToUInt64(v)))
{ }
}

///
public class EtagToUInt64Converter : ValueConverter<Etag, ulong>
{
///
public EtagToUInt64Converter() : base(convertToProviderExpression: v => (ulong)v,
convertFromProviderExpression: v => new Etag(v))
{ }
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,58 @@ namespace Microsoft.EntityFrameworkCore;
public static class ModelConfigurationBuilderExtensions
{
/// <summary>
/// Add fields of type <see cref="Etag"/> to be converted using <see cref="EtagConverter"/>.
/// Add fields of type <see cref="Etag"/> to be converted to a <see cref="T:byte[]"/>.
/// </summary>
/// <param name="configurationBuilder">The <see cref="ModelConfigurationBuilder"/> to use.</param>
public static void AddEtagConventions(this ModelConfigurationBuilder configurationBuilder)
public static void AddEtagToBytesConventions(this ModelConfigurationBuilder configurationBuilder)
{
ArgumentNullException.ThrowIfNull(configurationBuilder);

configurationBuilder.Properties<Etag>().HaveConversion<EtagConverter, EtagComparer>();
configurationBuilder.Properties<Etag>().HaveConversion<EtagToBytesConverter, EtagComparer>();
}

/// <summary>
/// Add fields of type <see cref="Etag"/> to be converted to a <see cref="uint"/>.
/// </summary>
/// <param name="configurationBuilder">The <see cref="ModelConfigurationBuilder"/> to use.</param>
public static void AddEtagToInt32Conventions(this ModelConfigurationBuilder configurationBuilder)
{
ArgumentNullException.ThrowIfNull(configurationBuilder);

configurationBuilder.Properties<Etag>().HaveConversion<EtagToInt32Converter, EtagComparer>();
}

/// <summary>
/// Add fields of type <see cref="Etag"/> to be converted to a <see cref="int"/>.
/// </summary>
/// <param name="configurationBuilder">The <see cref="ModelConfigurationBuilder"/> to use.</param>
public static void AddEtagToUInt32Conventions(this ModelConfigurationBuilder configurationBuilder)
{
ArgumentNullException.ThrowIfNull(configurationBuilder);

configurationBuilder.Properties<Etag>().HaveConversion<EtagToUInt32Converter, EtagComparer>();
}

/// <summary>
/// Add fields of type <see cref="Etag"/> to be converted to a <see cref="long"/>.
/// </summary>
/// <param name="configurationBuilder">The <see cref="ModelConfigurationBuilder"/> to use.</param>
public static void AddEtagToInt64Conventions(this ModelConfigurationBuilder configurationBuilder)
{
ArgumentNullException.ThrowIfNull(configurationBuilder);

configurationBuilder.Properties<Etag>().HaveConversion<EtagToInt64Converter, EtagComparer>();
}

/// <summary>
/// Add fields of type <see cref="Etag"/> to be converted to a <see cref="ulong"/>.
/// </summary>
/// <param name="configurationBuilder">The <see cref="ModelConfigurationBuilder"/> to use.</param>
public static void AddEtagToUInt64Conventions(this ModelConfigurationBuilder configurationBuilder)
{
ArgumentNullException.ThrowIfNull(configurationBuilder);

configurationBuilder.Properties<Etag>().HaveConversion<EtagToUInt64Converter, EtagComparer>();
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,71 @@ public static class PropertyBuilderExtensions
/// </summary>
/// <param name="propertyBuilder">The <see cref="PropertyBuilder{TProperty}"/> to extend.</param>
/// <returns></returns>
public static PropertyBuilder<Etag> HasEtagConversion(this PropertyBuilder<Etag> propertyBuilder)
public static PropertyBuilder<Etag> HasEtagToBytesConversion(this PropertyBuilder<Etag> propertyBuilder)
{
ArgumentNullException.ThrowIfNull(propertyBuilder);

propertyBuilder.HasConversion(new EtagConverter());
propertyBuilder.HasConversion(new EtagToBytesConverter());
propertyBuilder.Metadata.SetValueComparer(new EtagComparer());

return propertyBuilder;
}

/// <summary>
/// Attach conversion of property to/from <see cref="Etag"/> stored in the database as a <see cref="int"/>.
/// </summary>
/// <param name="propertyBuilder">The <see cref="PropertyBuilder{TProperty}"/> to extend.</param>
/// <returns></returns>
public static PropertyBuilder<Etag> HasEtagToInt32Conversion(this PropertyBuilder<Etag> propertyBuilder)
{
ArgumentNullException.ThrowIfNull(propertyBuilder);

propertyBuilder.HasConversion(new EtagToInt32Converter());
propertyBuilder.Metadata.SetValueComparer(new EtagComparer());

return propertyBuilder;
}

/// <summary>
/// Attach conversion of property to/from <see cref="Etag"/> stored in the database as a <see cref="uint"/>.
/// </summary>
/// <param name="propertyBuilder">The <see cref="PropertyBuilder{TProperty}"/> to extend.</param>
/// <returns></returns>
public static PropertyBuilder<Etag> HasEtagToUInt32Conversion(this PropertyBuilder<Etag> propertyBuilder)
{
ArgumentNullException.ThrowIfNull(propertyBuilder);

propertyBuilder.HasConversion(new EtagToUInt32Converter());
propertyBuilder.Metadata.SetValueComparer(new EtagComparer());

return propertyBuilder;
}

/// <summary>
/// Attach conversion of property to/from <see cref="Etag"/> stored in the database as a <see cref="long"/>.
/// </summary>
/// <param name="propertyBuilder">The <see cref="PropertyBuilder{TProperty}"/> to extend.</param>
/// <returns></returns>
public static PropertyBuilder<Etag> HasEtagToInt64Conversion(this PropertyBuilder<Etag> propertyBuilder)
{
ArgumentNullException.ThrowIfNull(propertyBuilder);

propertyBuilder.HasConversion(new EtagToInt64Converter());
propertyBuilder.Metadata.SetValueComparer(new EtagComparer());

return propertyBuilder;
}

/// <summary>
/// Attach conversion of property to/from <see cref="Etag"/> stored in the database as a <see cref="ulong"/>.
/// </summary>
/// <param name="propertyBuilder">The <see cref="PropertyBuilder{TProperty}"/> to extend.</param>
/// <returns></returns>
public static PropertyBuilder<Etag> HasEtagToUInt64Conversion(this PropertyBuilder<Etag> propertyBuilder)
{
ArgumentNullException.ThrowIfNull(propertyBuilder);

propertyBuilder.HasConversion(new EtagToUInt64Converter());
propertyBuilder.Metadata.SetValueComparer(new EtagComparer());

return propertyBuilder;
Expand Down

0 comments on commit f17116c

Please sign in to comment.