Skip to content

Commit

Permalink
Imported Tingle.Extensions.EntityFrameworkCore
Browse files Browse the repository at this point in the history
  • Loading branch information
mburumaxwell committed Mar 7, 2024
1 parent f565a36 commit d5c65da
Show file tree
Hide file tree
Showing 22 changed files with 828 additions and 0 deletions.
1 change: 1 addition & 0 deletions .github/workflows/cleanup.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ jobs:
- { name: 'Tingle.AspNetCore.Tokens' }
- { name: 'Tingle.Extensions.Caching.MongoDB' }
- { name: 'Tingle.Extensions.DataAnnotations' }
- { name: 'Tingle.Extensions.EntityFrameworkCore' }
- { name: 'Tingle.Extensions.Http' }
- { name: 'Tingle.Extensions.Http.Authentication' }
- { name: 'Tingle.Extensions.JsonPatch' }
Expand Down
1 change: 1 addition & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"cSpell.words": [
"Bson",
"EFCORE",
"etag",
"Ksuid",
"libphonenumber",
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ This repository contains projects/libraries for adding useful functionality to .
|[`Tingle.AspNetCore.Tokens`](https://www.nuget.org/packages/Tingle.AspNetCore.Tokens/)|Support for generation of continuation tokens in ASP.NET Core with optional expiry. Useful for pagination, user invite tokens, expiring operation tokens, etc. This is availed through the `ContinuationToken<T>` and `TimedContinuationToken<T>` types. See [docs](./src/Tingle.AspNetCore.Tokens/README.md) and [sample](./samples/TokensSample).|
|[`Tingle.Extensions.Caching.MongoDB`](https://www.nuget.org/packages/Tingle.Extensions.Caching.MongoDB/)|Distributed caching implemented with [MongoDB](https://mongodb.com) on top of `IDistributedCache`, inspired by [CosmosCache](https://github.com/Azure/Microsoft.Extensions.Caching.Cosmos). See [docs](./src/Tingle.Extensions.Caching.MongoDB/README.md) and [sample](./samples/AspNetCoreSessionState)|
|[`Tingle.Extensions.DataAnnotations`](https://www.nuget.org/packages/Tingle.Extensions.DataAnnotations/)|Additional data validation attributes in the `System.ComponentModel.DataAnnotations` namespace. Some of this should have been present in the framework but are very specific to some use cases. For example `FiveStarRatingAttribute`. See [docs](./src/Tingle.Extensions.DataAnnotations/README.md).|
|[`Tingle.Extensions.EntityFrameworkCore`](https://www.nuget.org/packages/Tingle.Extensions.EntityFrameworkCore/)|Convenience functionality and extensions for working with EntityFrameworkCore. See [docs](./src/Tingle.Extensions.EntityFrameworkCore/README.md).|
|[`Tingle.Extensions.Http`](https://www.nuget.org/packages/Tingle.Extensions.Http/)|Lightweight abstraction around `HttpClient` which can be used to build custom client with response wrapping semantics. See [docs](./src/Tingle.Extensions.Http/README.md).|
|[`Tingle.Extensions.Http.Authentication`](https://www.nuget.org/packages/Tingle.Extensions.Http.Authentication/)|Authentication providers for use with `HttpClient` and includes support for DI via `Microsoft.Extensions.Http`. See [docs](./src/Tingle.Extensions.Http.Authentication/README.md) and [sample](./samples/HttpAuthenticationSample).|
|[`Tingle.Extensions.JsonPatch`](https://www.nuget.org/packages/Tingle.Extensions.JsonPatch/)|JSON Patch (RFC 6902) support for .NET to easily generate JSON Patch documents using `System.Text.Json` for client applications. See [docs](./src/Tingle.Extensions.JsonPatch/README.md).|
Expand Down
14 changes: 14 additions & 0 deletions Tingle.Extensions.sln
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tingle.Extensions.Caching.M
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tingle.Extensions.DataAnnotations", "src\Tingle.Extensions.DataAnnotations\Tingle.Extensions.DataAnnotations.csproj", "{51FA6572-8EB6-4291-8D02-BB736057A50E}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tingle.Extensions.EntityFrameworkCore", "src\Tingle.Extensions.EntityFrameworkCore\Tingle.Extensions.EntityFrameworkCore.csproj", "{32D8B776-DF01-4726-B76B-9D0D1A4B8EE3}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tingle.Extensions.Http", "src\Tingle.Extensions.Http\Tingle.Extensions.Http.csproj", "{5BFAD4DB-D6A6-44F4-ACAB-B7B04E5A052E}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tingle.Extensions.Http.Authentication", "src\Tingle.Extensions.Http.Authentication\Tingle.Extensions.Http.Authentication.csproj", "{47F95938-964A-47FE-A0D6-1EDD0893455B}"
Expand Down Expand Up @@ -59,6 +61,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tingle.Extensions.Caching.M
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tingle.Extensions.DataAnnotations.Tests", "tests\Tingle.Extensions.DataAnnotations.Tests\Tingle.Extensions.DataAnnotations.Tests.csproj", "{8E3530BB-ED60-4A2C-9BD2-C6879D67B4BD}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tingle.Extensions.EntityFrameworkCore.Tests", "tests\Tingle.Extensions.EntityFrameworkCore.Tests\Tingle.Extensions.EntityFrameworkCore.Tests.csproj", "{AE6F9F63-4EB0-4BD8-B407-4AE91184FB07}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tingle.Extensions.Http.Tests", "tests\Tingle.Extensions.Http.Tests\Tingle.Extensions.Http.Tests.csproj", "{41980843-7F99-4AD2-B9E9-B13FC349A150}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tingle.Extensions.Http.Authentication.Tests", "tests\Tingle.Extensions.Http.Authentication.Tests\Tingle.Extensions.Http.Authentication.Tests.csproj", "{D0C66D3A-ED1F-486E-AA19-BDBB19025368}"
Expand Down Expand Up @@ -272,6 +276,14 @@ Global
{009C5985-9DD4-45A8-A31E-4E6B7FE5EE78}.Debug|Any CPU.Build.0 = Debug|Any CPU
{009C5985-9DD4-45A8-A31E-4E6B7FE5EE78}.Release|Any CPU.ActiveCfg = Release|Any CPU
{009C5985-9DD4-45A8-A31E-4E6B7FE5EE78}.Release|Any CPU.Build.0 = Release|Any CPU
{32D8B776-DF01-4726-B76B-9D0D1A4B8EE3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{32D8B776-DF01-4726-B76B-9D0D1A4B8EE3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{32D8B776-DF01-4726-B76B-9D0D1A4B8EE3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{32D8B776-DF01-4726-B76B-9D0D1A4B8EE3}.Release|Any CPU.Build.0 = Release|Any CPU
{AE6F9F63-4EB0-4BD8-B407-4AE91184FB07}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AE6F9F63-4EB0-4BD8-B407-4AE91184FB07}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AE6F9F63-4EB0-4BD8-B407-4AE91184FB07}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AE6F9F63-4EB0-4BD8-B407-4AE91184FB07}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -318,6 +330,8 @@ Global
{CFEE0754-9DAF-4AA6-98F3-477F065D7DC4} = {CE7124E9-01B3-4AD6-8B8F-FB302E60FB7F}
{AC04C113-8F75-43BA-8FEE-987475A87C58} = {CE7124E9-01B3-4AD6-8B8F-FB302E60FB7F}
{009C5985-9DD4-45A8-A31E-4E6B7FE5EE78} = {9071B0C9-DE4D-411D-A9D3-CB7326CBBD80}
{32D8B776-DF01-4726-B76B-9D0D1A4B8EE3} = {9546186D-D4E1-4EDB-956E-1F81C7F4DB72}
{AE6F9F63-4EB0-4BD8-B407-4AE91184FB07} = {815F0941-3B70-4705-A583-AF627559595C}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {B9323FCA-8E8B-4176-A463-87D202EC4552}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
#if NET8_0_OR_GREATER
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using Microsoft.EntityFrameworkCore.Metadata.Conventions;
using Microsoft.EntityFrameworkCore.Metadata.Conventions.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata.Internal;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Reflection;

namespace Tingle.Extensions.EntityFrameworkCore.Conventions;

/// <summary>
/// A convention that configures the maximum length based on the <see cref="LengthAttribute" /> applied on the property.
/// </summary>
/// <remarks>
/// See <see href="https://aka.ms/efcore-docs-conventions">Model building conventions</see> for more information and examples.
/// </remarks>
/// <param name="dependencies">Parameter object containing dependencies for this convention.</param>
public class LengthAttributeConvention(ProviderConventionSetBuilderDependencies dependencies) : PropertyAttributeConventionBase<LengthAttribute>(dependencies), IComplexPropertyAddedConvention
{
/// <inheritdoc />
protected override void ProcessPropertyAdded(
IConventionPropertyBuilder propertyBuilder,
LengthAttribute attribute,
MemberInfo clrMember,
IConventionContext context)
{
if (attribute.MaximumLength > 0)
{
propertyBuilder.HasMaxLength(attribute.MaximumLength, fromDataAnnotation: true);
}
}

/// <inheritdoc />
protected override void ProcessPropertyAdded(
IConventionComplexPropertyBuilder propertyBuilder,
LengthAttribute attribute,
MemberInfo clrMember,
IConventionContext context)
{
var property = propertyBuilder.Metadata;
#pragma warning disable EF1001
var member = property.GetIdentifyingMemberInfo();
#pragma warning restore EF1001
if (member != null
&& Attribute.IsDefined(member, typeof(ForeignKeyAttribute), inherit: true))
{
throw new InvalidOperationException(
CoreStrings.AttributeNotOnEntityTypeProperty(
"MaxLength", property.DeclaringType.DisplayName(), property.Name));
}
}
}
#endif
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using Microsoft.EntityFrameworkCore.ChangeTracking;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Tingle.Extensions.Primitives;

namespace Tingle.Extensions.EntityFrameworkCore.Converters;

///
public class ByteSizeConverter : ValueConverter<ByteSize, long>
{
///
public ByteSizeConverter() : base(convertToProviderExpression: v => v.Bytes,
convertFromProviderExpression: v => v == default ? default : new ByteSize(v))
{ }
}

///
public class ByteSizeComparer : ValueComparer<ByteSize>
{
///
public ByteSizeComparer() : base(equalsExpression: (l, r) => l == r,
hashCodeExpression: v => v.GetHashCode(),
snapshotExpression: v => new ByteSize(v.Bytes))
{ }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using Microsoft.EntityFrameworkCore.ChangeTracking;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Tingle.Extensions.Primitives;

namespace Tingle.Extensions.EntityFrameworkCore.Converters;

///
public class DurationConverter : ValueConverter<Duration, string>
{
///
public DurationConverter() : base(convertToProviderExpression: v => v.ToString(),
convertFromProviderExpression: v => v == null ? default : Duration.Parse(v))
{ }
}

///
public class DurationComparer : ValueComparer<Duration>
{
///
public DurationComparer() : base(equalsExpression: (l, r) => l == r,
hashCodeExpression: v => v.GetHashCode(),
snapshotExpression: v => Duration.Parse(v.ToString()))
{ }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using Microsoft.EntityFrameworkCore.ChangeTracking;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Tingle.Extensions.Primitives;

namespace Tingle.Extensions.EntityFrameworkCore.Converters;

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

///
public class EtagComparer : ValueComparer<Etag>
{
///
public EtagComparer() : base(equalsExpression: (l, r) => l == r,
hashCodeExpression: v => v.GetHashCode(),
snapshotExpression: v => new Etag(v.ToByteArray()))
{ }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#if NET8_0_OR_GREATER
using Microsoft.EntityFrameworkCore.ChangeTracking;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using System.Net;

namespace Tingle.Extensions.EntityFrameworkCore.Converters;

///
public class IPNetworkConverter : ValueConverter<IPNetwork, string>
{
///
public IPNetworkConverter() : base(convertToProviderExpression: v => v.ToString(),
convertFromProviderExpression: v => v == null ? default : IPNetwork.Parse(v))
{ }
}

///
public class IPNetworkComparer : ValueComparer<IPNetwork>
{
///
public IPNetworkComparer() : base(equalsExpression: (l, r) => l == r,
hashCodeExpression: v => v.GetHashCode(),
snapshotExpression: v => IPNetwork.Parse(v.ToString()))
{ }
}
#endif
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
using Microsoft.EntityFrameworkCore.ChangeTracking;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using System.Text.Json;
using SC = Tingle.Extensions.EntityFrameworkCore.EfCoreJsonSerializerContext;

namespace Tingle.Extensions.EntityFrameworkCore.Converters;

///
public class JsonElementConverter : ValueConverter<JsonElement, string>
{
///
public JsonElementConverter() : base(convertToProviderExpression: v => v.ToString(),
convertFromProviderExpression: v => v == null ? default : JsonDocument.Parse(v, default).RootElement)
{ }
}

///
public class JsonElementComparer : ValueComparer<JsonElement>
{
///
public JsonElementComparer() : base(
equalsExpression: (l, r) => JsonSerializer.Serialize(l, SC.Default.JsonElement) == JsonSerializer.Serialize(r, SC.Default.JsonElement),
hashCodeExpression: v => v.GetHashCode(),
snapshotExpression: v => JsonDocument.Parse(JsonSerializer.Serialize(v, SC.Default.JsonElement), default).RootElement)
{ }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using Microsoft.EntityFrameworkCore.ChangeTracking;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using System.Text.Json.Nodes;

#pragma warning disable CS8603 // Possible null reference return.
#pragma warning disable IL2026 // Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code

namespace Tingle.Extensions.EntityFrameworkCore.Converters;

///
public class JsonNodeConverter : ValueConverter<JsonNode, string>
{
///
public JsonNodeConverter() : base(convertToProviderExpression: v => v.ToJsonString(default),
convertFromProviderExpression: v => v == null ? default : JsonNode.Parse(v, default, default))
{ }
}

///
public class JsonNodeComparer : ValueComparer<JsonNode>
{
///
public JsonNodeComparer() : base(equalsExpression: (l, r) => (l == null ? null : l.ToJsonString(default)) == (r == null ? null : r.ToJsonString(default)),
hashCodeExpression: v => v == null ? 0 : v.ToJsonString(default).GetHashCode(),
snapshotExpression: v => JsonNode.Parse(v.ToJsonString(default), default, default))
{ }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using Microsoft.EntityFrameworkCore.ChangeTracking;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using System.Text.Json.Nodes;

#pragma warning disable CS8603 // Possible null reference return.
#pragma warning disable IL2026 // Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code

namespace Tingle.Extensions.EntityFrameworkCore.Converters;

///
public class JsonObjectConverter : ValueConverter<JsonObject, string>
{
///
public JsonObjectConverter() : base(convertToProviderExpression: v => v.ToJsonString(default),
convertFromProviderExpression: v => v == null ? default : JsonNode.Parse(v, default, default)!.AsObject())
{ }
}

///
public class JsonObjectComparer : ValueComparer<JsonObject>
{
///
public JsonObjectComparer() : base(equalsExpression: (l, r) => (l == null ? null : l.ToJsonString(default)) == (r == null ? null : r.ToJsonString(default)),
hashCodeExpression: v => v == null ? 0 : v.ToJsonString(default).GetHashCode(),
snapshotExpression: v => v == null ? null : JsonNode.Parse(v.ToJsonString(default), default, default)!.AsObject())
{ }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using Microsoft.EntityFrameworkCore.ChangeTracking;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Tingle.Extensions.Primitives;

namespace Tingle.Extensions.EntityFrameworkCore.Converters;

///
public class SequenceNumberConverter : ValueConverter<SequenceNumber, long>
{
///
public SequenceNumberConverter() : base(convertToProviderExpression: v => v.Value,
convertFromProviderExpression: v => v == SequenceNumber.Empty ? default : new SequenceNumber(v))
{ }
}

///
public class SequenceNumberComparer : ValueComparer<SequenceNumber>
{
///
public SequenceNumberComparer() : base(equalsExpression: (l, r) => l == r,
hashCodeExpression: v => v.GetHashCode(),
snapshotExpression: v => new SequenceNumber(v.Value))
{ }
}
Loading

0 comments on commit d5c65da

Please sign in to comment.