Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .github/workflows/CI.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ jobs:
uses: actions/setup-dotnet@v1
with:
dotnet-version: |
3.1.x
6.0.x
8.0.x
9.0.x
Expand Down
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,17 @@

Represents the **NuGet** versions.

## v3.31.0
- *Enhancement:* Moved existing reflection-based `JsonMergePatch` to `Extended.JsonMergePatchEx`; this remains the `AddJsonMergePatch` default implementation.
- *Enhancement:* Added new `JsonMergePatch` that leverages `JsonElement` and `Utf8JsonWriter` without underlying reflection; useful in scenarios where the value type is not known. This is also not as performant as the reflection-based `JsonMergePatchEx` version and the primary reason why it is not the new default.
- *Enhancement:* Refactored the `CosmosDb` capabilities such that the `CosmosDbContainer` and `CosmosDbModelContainer` are model type independent, with underlying type support implemented at the method level for greater flexibility and control. The typed `CosmosDbContainer<T, TModel>` etc. remain and are accessed from the type independent containers as required.
- The existing `IMultiSetArgs` operations have been moved (and renamed) from `CosmosDb` to `CosmosDbContainer` and `CosmosDbModelContainer` as these are single container-specific.
- The existing `CosmosDb.UseAuthorizeFilter` operations have been moved to `CosmosDbContainer` as these are single container-specific.
- *Enhancement:* Added `Cleaner.PrepareCreate` and `Cleaner.PrepareUpdate` to encapsulate `ChangeLog.PrepareCreated` and `ChangeLog.PrepareUpdated`, and `Cleaner.ResetTenantId` to ensure consistent handling throughout _CoreEx_.
- *Enhancement:* Added `SystemTime.Timestamp` as the standard means to access the current timestamp (uses `Cleaner.Clean`) to ensure consistency throughout _CoreEx_. Therefore, the likes of `DateTime.UtcNow` should be replaced with `SystemTime.Timestamp`. The previous `ExecutionContent.SystemTime` has been removed as it was not consistent with the `ExecutionContext` pattern.
- *Enhancement:* Updated the `IExtendedException.ShouldBeLogged` implementations to check `SettingsBase` configuration to enable/disable.
- *Enhancement:* Updated dependencies to latest; including transitive where applicable.

## v3.30.2
- *Fixed:* Missing `QueryArgs.IncludeText` added to set the `$text=true` equivalent.
- *Fixed:* Simplification of creating and setting the `QueryArgs.Filter` using an implict string operator.
Expand Down
2 changes: 1 addition & 1 deletion Common.targets
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<Project>
<PropertyGroup>
<Version>3.30.2</Version>
<Version>3.31.0</Version>
<LangVersion>preview</LangVersion>
<Authors>Avanade</Authors>
<Company>Avanade</Company>
Expand Down
3 changes: 2 additions & 1 deletion samples/My.Hr/My.Hr.Api/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using CoreEx.Azure.ServiceBus.HealthChecks;
using Azure.Messaging.ServiceBus;
using Microsoft.Extensions.DependencyInjection;
using CoreEx.Json.Merge;

namespace My.Hr.Api;

Expand All @@ -34,7 +35,7 @@ public void ConfigureServices(IServiceCollection services)
.AddAzureServiceBusSender()
.AddAzureServiceBusPurger()
.AddJsonMergePatch()
.AddWebApi((_, webapi) => webapi.UnhandledExceptionAsync = (ex, _, _) => Task.FromResult(ex is DbUpdateConcurrencyException efex ? webapi.CreateActionResultFromExtendedException(new ConcurrencyException()) : null))
.AddWebApi((_, webapi) => webapi.UnhandledExceptionAsync = (ex, logger, _) => Task.FromResult(ex is DbUpdateConcurrencyException efex ? webapi.CreateActionResultFromExtendedException(new ConcurrencyException(null, ex), logger) : null))
.AddReferenceDataContentWebApi()
.AddRequestCache();

Expand Down
3 changes: 2 additions & 1 deletion samples/My.Hr/My.Hr.Api/appsettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@
"AbsoluteExpirationRelativeToNow": "03:00:00",
"SlidingExpiration": "00:45:00"
}
}
},
"LogConcurrencyException": true
},
"ServiceBusConnection": {
"fullyQualifiedNamespace": "Endpoint=sb://top-secret.servicebus.windows.net/;SharedAccessKeyName=top-secret;SharedAccessKey=top-encrypted-secret"
Expand Down
22 changes: 22 additions & 0 deletions samples/My.Hr/My.Hr.Business/Data/Employee2Configuration.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
namespace My.Hr.Business.Data;

public class Employee2Configuration : IEntityTypeConfiguration<Employee2>
{
public void Configure(EntityTypeBuilder<Employee2> builder)
{
builder.ToTable("Employee2", "Hr");
builder.Property(p => p.Id).HasColumnName("EmployeeId").HasColumnType("UNIQUEIDENTIFIER");
builder.Property(p => p.Email).HasColumnType("NVARCHAR(250)");
builder.Property(p => p.FirstName).HasColumnType("NVARCHAR(100)");
builder.Property(p => p.LastName).HasColumnType("NVARCHAR(100)");
builder.Property(p => p.Gender).HasColumnName("GenderCode").HasColumnType("NVARCHAR(50)").HasConversion(v => v!.Code, v => (Gender?)v);
builder.Property(p => p.Birthday).HasColumnType("DATE");
builder.Property(p => p.StartDate).HasColumnType("DATE");
builder.Property(p => p.TerminationDate).HasColumnType("DATE");
builder.Property(p => p.TerminationReasonCode).HasColumnType("NVARCHAR(50)");
builder.Property(p => p.PhoneNo).HasColumnType("NVARCHAR(50)");
builder.Property(p => p.ETag).HasColumnName("RowVersion").IsRowVersion().HasConversion(s => s == null ? Array.Empty<byte>() : Convert.FromBase64String(s), d => Convert.ToBase64String(d));
builder.Property(p => p.IsDeleted).HasColumnType("BIT");
builder.HasKey("Id");
}
}
3 changes: 2 additions & 1 deletion samples/My.Hr/My.Hr.Business/Data/HrDbContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder
.ApplyConfiguration(new UsStateConfiguration())
.ApplyConfiguration(new EmployeeConfiguration());
.ApplyConfiguration(new EmployeeConfiguration())
.ApplyConfiguration(new Employee2Configuration());

base.OnModelCreating(modelBuilder);
}
Expand Down
10 changes: 10 additions & 0 deletions samples/My.Hr/My.Hr.Business/Data/HrEfDb.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ public interface IHrEfDb : IEfDb
/// Gets the <see cref="Employee"/> entity.
/// </summary>
EfDbEntity<Employee, Employee> Employees { get; }

/// <summary>
/// Gets the <see cref="Employee2"/> entity.
/// </summary>
EfDbEntity<Employee2, Employee2> Employees2 { get; }
}

/// <summary>
Expand All @@ -27,5 +32,10 @@ public HrEfDb(HrDbContext dbContext, IMapper mapper) : base(dbContext, mapper) {
/// Gets the <see cref="Employee"/> encapsulated entity.
/// </summary>
public EfDbEntity<Employee, Employee> Employees => new(this);

/// <summary>
/// Gets the <see cref="Employee"/> encapsulated entity.
/// </summary>
public EfDbEntity<Employee2, Employee2> Employees2 => new(this);
}
}
74 changes: 74 additions & 0 deletions samples/My.Hr/My.Hr.Business/Models/Employee2.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
using System.Diagnostics;

namespace My.Hr.Business.Models;

/// <summary>
/// Represents the Entity Framework (EF) model for database object 'Hr.Employee2'.
/// </summary>
public class Employee2 : IIdentifier<Guid>, IETag, ILogicallyDeleted
{
/// <summary>
/// Gets or sets the 'EmployeeId' column value.
/// </summary>
public Guid Id { get; set; }

/// <summary>
/// Gets or sets the 'Email' column value.
/// </summary>
public string? Email { get; set; }

/// <summary>
/// Gets or sets the 'FirstName' column value.
/// </summary>
public string? FirstName { get; set; }

/// <summary>
/// Gets or sets the 'LastName' column value.
/// </summary>
public string? LastName { get; set; }

/// <summary>
/// Gets or sets the 'GenderCode' column value.
/// </summary>
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
public Gender? Gender { get; set; }

/// <summary>
/// Gets or sets the 'Birthday' column value.
/// </summary>
public DateTime? Birthday { get; set; }

/// <summary>
/// Gets or sets the 'StartDate' column value.
/// </summary>
public DateTime? StartDate { get; set; }

/// <summary>
/// Gets or sets the 'TerminationDate' column value.
/// </summary>
public DateTime? TerminationDate { get; set; }

/// <summary>
/// Gets or sets the 'TerminationReasonCode' column value.
/// </summary>
public string? TerminationReasonCode { get; set; }

/// <summary>
/// Gets or sets the 'PhoneNo' column value.
/// </summary>
public string? PhoneNo { get; set; }

/// <summary>
/// Gets or sets the 'RowVersion' column value.
/// </summary>
public string? ETag { get; set; }

/// <summary>
/// Gets or sets the 'IsDeleted' column value.
/// </summary>
public bool? IsDeleted { get; set; }
}

public class Employee2Collection : List<Employee2> { }

public class Employee2CollectionResult : CollectionResult<Employee2Collection, Employee2> { }
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ public Task<Result> VerifyEmployeeAsync(Guid id) => Result
var verification = new EmployeeVerificationRequest
{
Name = employee!.FirstName,
Age = DateTime.UtcNow.Subtract(employee.Birthday.GetValueOrDefault()).Days / 365,
Age = SystemTime.Timestamp.Subtract(employee.Birthday.GetValueOrDefault()).Days / 365,
Gender = employee.Gender?.Code
};

Expand Down
4 changes: 3 additions & 1 deletion samples/My.Hr/My.Hr.Business/Services/EmployeeService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ public async Task<Employee> UpdateEmployeeAsync(Employee employee, Guid id)
throw new NotFoundException();

employee.Id = id;

_dbContext.ChangeTracker.Clear(); // Different employee instance (result of using CoreEx.Json.JsonMergePatch vs CoreEx.Json.Extended.JsonMergePatchEx); therefore, clear the change tracker prior to update attempt.
_dbContext.Employees.Update(employee);
await _dbContext.SaveChangesAsync().ConfigureAwait(false);
return employee;
Expand All @@ -80,7 +82,7 @@ public async Task VerifyEmployeeAsync(Guid id)
var verification = new EmployeeVerificationRequest
{
Name = employee.FirstName,
Age = DateTime.UtcNow.Subtract(employee.Birthday.GetValueOrDefault()).Days / 365,
Age = SystemTime.Timestamp.Subtract(employee.Birthday.GetValueOrDefault()).Days / 365,
Gender = employee.Gender?.Code
};

Expand Down
2 changes: 1 addition & 1 deletion samples/My.Hr/My.Hr.Business/Services/EmployeeService2.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ public async Task VerifyEmployeeAsync(Guid id)
var verification = new EmployeeVerificationRequest
{
Name = employee.FirstName,
Age = DateTime.UtcNow.Subtract(employee.Birthday.GetValueOrDefault()).Days / 365,
Age = SystemTime.Timestamp.Subtract(employee.Birthday.GetValueOrDefault()).Days / 365,
Gender = employee.Gender?.Code
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ public EmployeeValidator()
RuleFor(x => x.FirstName).NotNull().MaximumLength(100);
RuleFor(x => x.LastName).NotNull().MaximumLength(100);
RuleFor(x => x.Gender).NotNull().IsValid();
RuleFor(x => x.Birthday).NotNull().LessThanOrEqualTo(DateTime.UtcNow.AddYears(-18)).WithMessage("Birthday is invalid as the Employee must be at least 18 years of age.");
RuleFor(x => x.Birthday).NotNull().LessThanOrEqualTo(SystemTime.Timestamp.AddYears(-18)).WithMessage("Birthday is invalid as the Employee must be at least 18 years of age.");
RuleFor(x => x.StartDate).NotNull().GreaterThanOrEqualTo(new DateTime(1999, 01, 01, 0, 0, 0, DateTimeKind.Utc)).WithMessage("January 1, 1999");
RuleFor(x => x.PhoneNo).NotNull().MaximumLength(50);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
-- Migration Script

BEGIN TRANSACTION

CREATE TABLE [Hr].[Employee2] (
[EmployeeId] UNIQUEIDENTIFIER NOT NULL DEFAULT (NEWSEQUENTIALID()) PRIMARY KEY, -- This is the primary key
[Email] NVARCHAR(250) NULL UNIQUE, -- This is the employee's unique email address
[FirstName] NVARCHAR(100) NULL,
[LastName] NVARCHAR(100) NULL,
[GenderCode] NVARCHAR(50) NULL, -- This is the related Gender code; see Ref.Gender table
[Birthday] DATE NULL,
[StartDate] DATE NULL,
[TerminationDate] DATE NULL,
[TerminationReasonCode] NVARCHAR(50) NULL, -- This is the related Termination Reason code; see Ref.TerminationReason table
[PhoneNo] NVARCHAR(50) NULL,
[AddressJson] NVARCHAR(500) NULL, -- This is the full address persisted as JSON.
[IsDeleted] BIT NULL, -- Logical delete
[RowVersion] TIMESTAMP NOT NULL, -- This is used for concurrency version checking.
[CreatedBy] NVARCHAR(250) NULL, -- The following are standard audit columns.
[CreatedDate] DATETIME2 NULL,
[UpdatedBy] NVARCHAR(250) NULL,
[UpdatedDate] DATETIME2 NULL
);

COMMIT TRANSACTION
5 changes: 3 additions & 2 deletions samples/My.Hr/My.Hr.Functions/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using CoreEx.Database;
using CoreEx.Database.HealthChecks;
using CoreEx.Http.HealthChecks;
using CoreEx.Json.Merge;
using Microsoft.Azure.Functions.Extensions.DependencyInjection;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
Expand Down Expand Up @@ -40,8 +41,8 @@ public override void Configure(IFunctionsHostBuilder builder)
.AddEventPublisher()
.AddSingleton(sp => new Az.ServiceBusClient(sp.GetRequiredService<HrSettings>().ServiceBusConnection__fullyQualifiedNamespace))
.AddAzureServiceBusSender()
.AddWebApi((_, webapi) => webapi.UnhandledExceptionAsync = (ex, _, _) => Task.FromResult(ex is DbUpdateConcurrencyException efex ? webapi.CreateActionResultFromExtendedException(new ConcurrencyException()) : null))
.AddJsonMergePatch()
.AddWebApi((_, webapi) => webapi.UnhandledExceptionAsync = (ex, logger, _) => Task.FromResult(ex is DbUpdateConcurrencyException efex ? webapi.CreateActionResultFromExtendedException(new ConcurrencyException(), logger) : null))
.AddJsonMergePatch(sp => new JsonMergePatch())
.AddWebApiPublisher()
.AddAzureServiceBusSubscriber();

Expand Down
7 changes: 6 additions & 1 deletion samples/My.Hr/My.Hr.UnitTest/Data/Data.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,9 @@
- EmergencyContact:
- { EmergencyContactId: 201, EmployeeId: 2, FirstName: Garth, LastName: Smith, PhoneNo: (443) 678 1827, RelationshipTypeCode: PAR }
- { EmergencyContactId: 202, EmployeeId: 2, FirstName: Sarah, LastName: Smith, PhoneNo: (443) 234 3837, RelationshipTypeCode: PAR }
- { EmergencyContactId: 401, EmployeeId: 4, FirstName: Michael, LastName: Manners, PhoneNo: (234) 297 9834, RelationshipTypeCode: FRD }
- { EmergencyContactId: 401, EmployeeId: 4, FirstName: Michael, LastName: Manners, PhoneNo: (234) 297 9834, RelationshipTypeCode: FRD }
- Employee2:
- { EmployeeId: 1, Email: w.jones@org.com, FirstName: Wendy, LastName: Jones, GenderCode: F, Birthday: 1985-03-18, StartDate: 2000-12-11, PhoneNo: (425) 612 8113 }
- { EmployeeId: 2, Email: b.smith@org.com, FirstName: Brian, LastName: Smith, GenderCode: M, Birthday: 1994-11-07, StartDate: 2013-08-06, TerminationDate: 2015-04-08, TerminationReasonCode: RE, PhoneNo: (429) 120 0098, IsDeleted: false }
- { EmployeeId: 3, Email: r.Browne@org.com, FirstName: Rachael, LastName: Browne, GenderCode: F, Birthday: 1972-06-28, StartDate: 2019-11-06, PhoneNo: (421) 783 2343, IsDeleted: true }
- { EmployeeId: 4, Email: w.smither@org.com, FirstName: Waylon, LastName: Smithers, GenderCode: M, Birthday: 1952-02-21, StartDate: 2001-01-22, PhoneNo: (428) 893 2793, AddressJson: '{ "street1": "8365 851 PL NE", "city": "Redmond", "state": "WA", "postCode": "98052" }' }
Loading
Loading