Skip to content

Commit

Permalink
cache a part of OfX-EFCore because the SelectorIds is dynamic!
Browse files Browse the repository at this point in the history
  • Loading branch information
quyvu01 committed Dec 30, 2024
1 parent c9fe065 commit 3cadaf4
Show file tree
Hide file tree
Showing 7 changed files with 40 additions and 21 deletions.
41 changes: 24 additions & 17 deletions src/OfX.EntityFrameworkCore/EfQueryOfHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
using Microsoft.Extensions.DependencyInjection;
using Newtonsoft.Json;
using OfX.Abstractions;
using OfX.ApplicationModels;
using OfX.Attributes;
using OfX.Cached;
using OfX.EntityFrameworkCore.Delegates;
using OfX.Responses;

Expand Down Expand Up @@ -37,28 +39,25 @@ public async Task<ItemsResponse<OfXDataResponse>> GetDataAsync(RequestContext<TA
return new ItemsResponse<OfXDataResponse>(data);
}

// Todo: This is very first version, which is support for string. I need to define for specific type. This will be update later one!
// Currently, the Id type is supported for primitive type, in the next version. The strongly-type should be supported!
// I cannot find the way to optimize this one on this moment because seem this cannot be cached.
// Tried to use FromRawSql, but I need to write all Query like Select * from...
// Mark this one as the issue. I'll back later one!
// May I should cache the containsMethod, idAsString first!
private Expression<Func<TModel, bool>> BuildFilter(RequestOf<TAttribute> query)
{
var parameter = Expression.Parameter(typeof(TModel), "x");
var property = Expression.Property(parameter, idPropertyName);
var modelIdData = GetModelData();
var selectorsConstant = Expression.Constant(query.SelectorIds);
var containsMethod = typeof(List<string>).GetMethod("Contains", [typeof(string)]);
var containsCall = Expression.Call(selectorsConstant, containsMethod!, property);
return Expression.Lambda<Func<TModel, bool>>(containsCall, parameter);
var containsCall = Expression.Call(selectorsConstant, OfXCached.IdsContainsMethodLazy.Value,
modelIdData.MethodCallExpression);
return Expression.Lambda<Func<TModel, bool>>(containsCall, modelIdData.ParameterExpression);
}

private Expression<Func<TModel, OfXDataResponse>> BuildResponse(RequestOf<TAttribute> request)
{
return ExpressionMapModelStorage.Value.GetOrAdd(request.Expression, expression =>
{
var parameter = Expression.Parameter(typeof(TModel), "x");

// Access the Id property on the model
var idProperty = Expression.Property(parameter, idPropertyName);
var toStringMethod = typeof(object).GetMethod(nameof(ToString), Type.EmptyTypes);
var idAsString = Expression.Call(idProperty, toStringMethod!);

var (parameter, idAsString) = GetModelData();
var expressionParts = expression.Split('.');
Expression currentExpression = parameter;
var currentType = typeof(TModel);
Expand Down Expand Up @@ -113,10 +112,8 @@ private Expression<Func<TModel, OfXDataResponse>> BuildResponse(RequestOf<TAttri
// Create member bindings for Id and serialized Value
var bindings = new List<MemberBinding>
{
Expression.Bind(typeof(OfXDataResponse).GetProperty(nameof(OfXDataResponse.Id))!,
idAsString),
Expression.Bind(typeof(OfXDataResponse).GetProperty(nameof(OfXDataResponse.Value))!,
serializeCall)
Expression.Bind(typeof(OfXDataResponse).GetProperty(nameof(OfXDataResponse.Id))!, idAsString),
Expression.Bind(typeof(OfXDataResponse).GetProperty(nameof(OfXDataResponse.Value))!, serializeCall)
};

// Create a new OfXDataResponse object
Expand All @@ -126,4 +123,14 @@ private Expression<Func<TModel, OfXDataResponse>> BuildResponse(RequestOf<TAttri
return Expression.Lambda<Func<TModel, OfXDataResponse>>(newExpression, parameter);
});
}

private ModelIdData GetModelData() => OfXCached.ModelIdDataCachedLazy
.Value.GetOrAdd(typeof(TModel), modelType =>
{
var parameter = Expression.Parameter(modelType, "x");
var idProperty = Expression.Property(parameter, idPropertyName);
var toStringMethod = typeof(object).GetMethod(nameof(ToString), Type.EmptyTypes);
var idAsString = Expression.Call(idProperty, toStringMethod!);
return new ModelIdData(parameter, idAsString);
});
}
2 changes: 1 addition & 1 deletion src/OfX.EntityFrameworkCore/OfX.EntityFrameworkCore.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<ImplicitUsings>enable</ImplicitUsings>
<TargetFrameworks>net9.0;net8.0</TargetFrameworks>
<LangVersion>default</LangVersion>
<Version>3.0.2</Version>
<Version>3.0.3</Version>
<Authors>Quy Vu</Authors>
<PackageId>OfX-EFCore</PackageId>
<Description>OfX extension. Use EntityFramework as Data Querying</Description>
Expand Down
2 changes: 1 addition & 1 deletion src/OfX.EntityFrameworkCore/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ public class User
```
That all! Let go to the moon!

Note: In this release, Id is exclusively supported as a string. But hold tight—I'm gearing up to blow your mind with the next update! Stay tuned!
Note: Currently, the Id type is supported for primitive type, in the next version. The strongly-type should be supported!

| Package Name | Description | .NET Version | Document |
|----------------------------------------------------------|-------------------------------------------------------------------------------------------------|--------------|---------------------------------------------------------------------------|
Expand Down
2 changes: 1 addition & 1 deletion src/OfX.Grpc/OfX.Grpc.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<ImplicitUsings>enable</ImplicitUsings>
<TargetFrameworks>net9.0;net8.0</TargetFrameworks>
<LangVersion>default</LangVersion>
<Version>3.0.2</Version>
<Version>3.0.3</Version>
<Authors>Quy Vu</Authors>
<PackageId>OfX-gRPC</PackageId>
<Description>OfX extension. Use gRPC as Data transporting</Description>
Expand Down
5 changes: 5 additions & 0 deletions src/OfX/ApplicationModels/ModelIdData.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
using System.Linq.Expressions;

namespace OfX.ApplicationModels;

public sealed record ModelIdData(ParameterExpression ParameterExpression, MethodCallExpression MethodCallExpression);
7 changes: 7 additions & 0 deletions src/OfX/Cached/OfXCached.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using System.Collections.Concurrent;
using System.Linq.Expressions;
using System.Reflection;
using OfX.ApplicationModels;

namespace OfX.Cached;

Expand All @@ -10,6 +12,11 @@ public static class OfXCached

private static readonly Lazy<ConcurrentDictionary<Type, Func<object[], object>>> ConstructorCache = new(() => []);

public static readonly Lazy<MethodInfo> IdsContainsMethodLazy =
new(() => typeof(List<string>).GetMethod("Contains", [typeof(string)]));

public static readonly Lazy<ConcurrentDictionary<Type, ModelIdData>> ModelIdDataCachedLazy = new(() => []);

public static object CreateInstanceWithCache(Type type, params object[] args)
{
if (ConstructorCache.Value.TryGetValue(type, out var factory)) return factory(args);
Expand Down
2 changes: 1 addition & 1 deletion src/OfX/OfX.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<ImplicitUsings>enable</ImplicitUsings>
<TargetFrameworks>net9.0;net8.0</TargetFrameworks>
<LangVersion>default</LangVersion>
<Version>3.0.2</Version>
<Version>3.0.3</Version>
<Authors>Quy Vu</Authors>
<PackageId>OfX</PackageId>
<Description>The high performance and easiest way to play with microservices for .NET</Description>
Expand Down

0 comments on commit 3cadaf4

Please sign in to comment.