Skip to content

Commit

Permalink
Removed GuidNotEmptyAttribute.
Browse files Browse the repository at this point in the history
Added GuidNotEmpty convention to Required annotation.
Fixed exception when updating directly in db using a query and properties to set.
  • Loading branch information
vivet committed Nov 6, 2024
1 parent f10710c commit df235bd
Show file tree
Hide file tree
Showing 7 changed files with 232 additions and 63 deletions.
12 changes: 7 additions & 5 deletions Nano.App/Api/BaseApi.cs
Original file line number Diff line number Diff line change
Expand Up @@ -603,6 +603,7 @@ private async Task GetResponseAsync(HttpResponseMessage httpResponse, Cancellati
switch (httpResponse.StatusCode)
{
case HttpStatusCode.NotFound:
case HttpStatusCode.NoContent:
return;

case HttpStatusCode.Unauthorized:
Expand Down Expand Up @@ -647,7 +648,8 @@ private async Task<TResponse> GetResponseAsync<TResponse>(HttpResponseMessage ht
switch (httpResponse.StatusCode)
{
case HttpStatusCode.NotFound:
return default;
case HttpStatusCode.NoContent:
return null;

case HttpStatusCode.Unauthorized:
throw new UnauthorizedException();
Expand Down Expand Up @@ -1059,13 +1061,13 @@ public virtual Task<TEntity> CreateAndGetAsync<TEntity>(CreateAndGetRequest requ
/// <param name="request">The <see cref="CreateManyRequest"/>.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/>.</param>
/// <returns>Nothing.</returns>
public virtual Task CreateManyAsync<TEntity>(CreateManyRequest request, CancellationToken cancellationToken = default)
public virtual async Task CreateManyAsync<TEntity>(CreateManyRequest request, CancellationToken cancellationToken = default)
where TEntity : class, IEntityCreatable
{
if (request == null)
throw new ArgumentNullException(nameof(request));

return this.InvokeAsync<CreateManyRequest, IEnumerable<TEntity>>(request, cancellationToken);
await this.InvokeAsync<CreateManyRequest, IEnumerable<TEntity>>(request, cancellationToken);
}

/// <summary>
Expand Down Expand Up @@ -1127,13 +1129,13 @@ public virtual Task<TEntity> EditAndGetAsync<TEntity>(EditAndGetRequest request,
/// <param name="request">The <see cref="EditManyRequest"/>.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/>.</param>
/// <returns>Nothing.</returns>
public virtual Task EditManyAsync<TEntity>(EditManyRequest request, CancellationToken cancellationToken = default)
public virtual async Task EditManyAsync<TEntity>(EditManyRequest request, CancellationToken cancellationToken = default)
where TEntity : class, IEntityUpdatable
{
if (request == null)
throw new ArgumentNullException(nameof(request));

return this.InvokeAsync<EditManyRequest, IEnumerable<TEntity>>(request, cancellationToken);
await this.InvokeAsync<EditManyRequest, IEnumerable<TEntity>>(request, cancellationToken);
}

/// <summary>
Expand Down
21 changes: 0 additions & 21 deletions Nano.Models/Attributes/GuidNotEmptyAttribute.cs

This file was deleted.

88 changes: 60 additions & 28 deletions Nano.Repository/BaseRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -661,18 +661,13 @@ public virtual async Task AddManyAsync<TEntity>(IEnumerable<TEntity> entities, C
if (entities == null)
throw new ArgumentNullException(nameof(entities));

foreach (var entity in entities)
{
this.Context
.Add(entity);
}
await this.Context
.AddRangeAsync(entities, cancellationToken);

if (this.Context.AutoSave)
{
await this.SaveChangesAsync(cancellationToken);
}

await Task.CompletedTask;
}

/// <inheritdoc />
Expand Down Expand Up @@ -721,24 +716,21 @@ public virtual async Task<TEntity> UpdateAndGetAsync<TEntity, TKey>(TEntity enti
}

/// <inheritdoc />
public virtual async Task UpdateManyAsync<TEntity>(IEnumerable<TEntity> entities, CancellationToken cancellationToken = default)
public virtual Task UpdateManyAsync<TEntity>(IEnumerable<TEntity> entities, CancellationToken cancellationToken = default)
where TEntity : class, IEntityUpdatable
{
if (entities == null)
throw new ArgumentNullException(nameof(entities));

foreach (var entity in entities)
{
this.Context
.Update(entity);
}
this.Context
.UpdateRange(entities);

if (this.Context.AutoSave)
{
await this.SaveChangesAsync(cancellationToken);
return this.SaveChangesAsync(cancellationToken);
}

await Task.CompletedTask;
return Task.CompletedTask;
}

/// <inheritdoc />
Expand All @@ -761,18 +753,34 @@ public virtual Task UpdateManyBulkAsync<TEntity, TCriteria>(TCriteria criteria,
where TEntity : class, IEntityUpdatable
where TCriteria : class, IQueryCriteria, new()
{
if (criteria == null)
throw new ArgumentNullException(nameof(criteria));

if (propertyUpdates == null)
throw new ArgumentNullException(nameof(propertyUpdates));

var updateExpression = this.BuildUpdateExpression<TEntity>(propertyUpdates);

return this.GetEntitySet<TEntity>()
.Where(criteria)
.ExecuteUpdateAsync(x => this.GetSetPropertyCalls(x, propertyUpdates), cancellationToken);
.ExecuteUpdateAsync(updateExpression, cancellationToken);
}

/// <inheritdoc />
public virtual Task UpdateManyBulkAsync<TEntity>(Expression<Func<TEntity, bool>> where, Dictionary<string, object> propertyUpdates, CancellationToken cancellationToken = default)
where TEntity : class, IEntityUpdatable
{
if (where == null)
throw new ArgumentNullException(nameof(where));

if (propertyUpdates == null)
throw new ArgumentNullException(nameof(propertyUpdates));

var updateExpression = this.BuildUpdateExpression<TEntity>(propertyUpdates);

return this.GetEntitySet<TEntity>()
.Where(where)
.ExecuteUpdateAsync(x => this.GetSetPropertyCalls(x, propertyUpdates), cancellationToken);
.ExecuteUpdateAsync(updateExpression, cancellationToken);
}

/// <inheritdoc />
Expand Down Expand Up @@ -1113,21 +1121,45 @@ protected virtual void Dispose(bool disposing)
this.Context?.Dispose();
}

private SetPropertyCalls<TEntity> GetSetPropertyCalls<TEntity>(SetPropertyCalls<TEntity> setPropertyCalls, Dictionary<string, object> propertyUpdates)
where TEntity : class, IEntityUpdatable
private Expression<Func<SetPropertyCalls<TEntity>, SetPropertyCalls<TEntity>>> BuildUpdateExpression<TEntity>(Dictionary<string, object> updates)
where TEntity : class
{
if (setPropertyCalls == null)
throw new ArgumentNullException(nameof(setPropertyCalls));

if (propertyUpdates == null)
throw new ArgumentNullException(nameof(propertyUpdates));
if (updates == null)
throw new ArgumentNullException(nameof(updates));

var parameter = Expression.Parameter(typeof(SetPropertyCalls<TEntity>), "instance");

foreach (var keyValuePair in propertyUpdates)
Expression expression = parameter;
foreach (var (propertyName, value) in updates)
{
setPropertyCalls = setPropertyCalls
.SetProperty(x => EF.Property<object>(x, keyValuePair.Key), keyValuePair.Value);
var entityParameter = Expression.Parameter(typeof(TEntity), "x");
var property = Expression.Property(entityParameter, propertyName);

var propertyType = property.Type;
var genericType = typeof(Func<,>).MakeGenericType(typeof(TEntity), propertyType);
var propertyLambda = Expression.Lambda(genericType, property, entityParameter);
var constantValue = Expression.Constant(value, propertyType);
var valueLambda = Expression.Lambda(genericType, constantValue, entityParameter);

var setPropertyMethod = typeof(SetPropertyCalls<TEntity>)
.GetMethods()
.FirstOrDefault(x =>
x.Name == "SetProperty" &&
x.IsGenericMethod &&
x.GetParameters().Length == 2 &&
x.GetParameters()[0].ParameterType.GetGenericArguments()[0] == typeof(TEntity));

if (setPropertyMethod == null)
{
throw new NullReferenceException(nameof(setPropertyMethod));
}

var genericSetPropertyMethod = setPropertyMethod
.MakeGenericMethod(propertyType);

expression = Expression.Call(expression, genericSetPropertyMethod, propertyLambda, valueLambda);
}

return setPropertyCalls;
return Expression.Lambda<Func<SetPropertyCalls<TEntity>, SetPropertyCalls<TEntity>>>(expression, parameter);
}
}
3 changes: 2 additions & 1 deletion Nano.Web/Controllers/BaseControllerWritable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,7 @@ await this.Repository
[ProducesResponseType((int)HttpStatusCode.NotFound)]
[ProducesResponseType(typeof(Error), (int)HttpStatusCode.BadRequest)]
[ProducesResponseType(typeof(Error), (int)HttpStatusCode.InternalServerError)]
public virtual async Task<IActionResult> EditManyBulkAsync([FromBody][Required] IEnumerable<TEntity> entities, CancellationToken cancellationToken = default)
public virtual async Task<IActionResult> EditManyBulkAsync([FromBody][Required]IEnumerable<TEntity> entities, CancellationToken cancellationToken = default)
{
await this.Repository
.UpdateManyBulkAsync(entities, cancellationToken);
Expand All @@ -296,6 +296,7 @@ await this.Repository
/// <response code="401">Unauthorized.</response>
/// <response code="500">Error occured.</response>
[HttpPut]
[HttpPost]
[Route("edit/query")]
[Consumes(HttpContentType.JSON)]
[ProducesResponseType((int)HttpStatusCode.OK)]
Expand Down
4 changes: 4 additions & 0 deletions Nano.Web/Extensions/ServiceCollectionExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
using Nano.Config;
using Nano.Models;
using Nano.Models.Const;
using Nano.Web.Hosting.ActionFilters;
using Nano.Web.Hosting.Serialization.Json.Const;
using Vivet.AspNetCore.RequestVirusScan.Extensions;

Expand Down Expand Up @@ -192,6 +193,9 @@ internal static IServiceCollection AddWeb(this IServiceCollection services, ICon
.Add(new AuditActionHidingConvention());
}

x.Filters
.Add<ValidateGuidNotEmptyFilter>();

if (webOptions.Hosting.UseHttpsRequired)
{
x.Filters
Expand Down
Loading

0 comments on commit df235bd

Please sign in to comment.