Skip to content

Commit

Permalink
minor improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
VahidFarahmandian committed Feb 24, 2025
1 parent 72c8bda commit 1530ec0
Show file tree
Hide file tree
Showing 66 changed files with 880 additions and 583 deletions.
6 changes: 3 additions & 3 deletions 01-Core/Jinget.Core.DiScanner/Jinget.Core.DiScanner.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="JetBrains.Annotations" Version="2024.2.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.2" />
<PackageReference Include="Microsoft.Extensions.DependencyModel" Version="8.0.2" />
<PackageReference Include="JetBrains.Annotations" Version="2024.3.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="9.0.2" />
<PackageReference Include="Microsoft.Extensions.DependencyModel" Version="9.0.2" />
</ItemGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ public class DateGreaterThanAttribute(string otherPropertyName) : ValidationAttr
/// <summary>
/// check whether <paramref name="value"/> is greater than <paramref name="otherPropertyName"/>
/// </summary>
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
protected override ValidationResult? IsValid(object? value, ValidationContext validationContext)
{
if (value == null) return ValidationResult.Success; // Allow nulls (optional, adjust as needed)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ public class TimeGreaterThanAttribute(string otherPropertyName) : ValidationAttr
/// <summary>
/// check whether <paramref name="value"/> is greater than <paramref name="otherPropertyName"/>
/// </summary>
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
protected override ValidationResult? IsValid(object? value, ValidationContext validationContext)
{
if (value == null)
return ValidationResult.Success; // Allow nulls (optional, adjust as needed)
Expand Down
11 changes: 7 additions & 4 deletions 01-Core/Jinget.Core/CodeDom/JingetDynamicCode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,10 @@ public class MethodOptions
/// What should be return type of the dynamically injected Invoke method?
/// If not set, void will be used
/// </summary>
public Type ReturnType { get; set; }
/// <remarks>
/// Default value: typeof(void)
/// </remarks>
public Type ReturnType { get; set; } = typeof(void);

/// <summary>
/// What are the input parameters for dynamically injected Invoke method?
Expand All @@ -188,9 +191,9 @@ public class MethodOptions

public class ParameterOptions
{
public Type Type { get; set; }
public string Name { get; set; }
public object Value { get; set; }
public Type? Type { get; set; }
public string? Name { get; set; }
public object? Value { get; set; }
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
/// <summary>
/// add support for 'required' keyword using latest lang version and .net standard 2.1
/// </summary>
#pragma warning disable CS9113 // Parameter is unread.
public class CompilerFeatureRequiredAttribute(string name) : Attribute
#pragma warning restore CS9113 // Parameter is unread.
{
}
10 changes: 6 additions & 4 deletions 01-Core/Jinget.Core/ExpressionToSql/Internal/OrderBy.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ public class OrderBy<T>
/// <summary>
/// The order by expression
/// </summary>
public virtual Expression<Func<T, object>> Name { get; set; }
public virtual Expression<Func<T, object>>? Name { get; set; }

public OrderBy() { }
public OrderBy(string name) => Name = ExpressionUtility.ToExpression<T, object>(name, "x");
Expand All @@ -29,12 +29,14 @@ public OrderBy() { }
/// </summary>
public override string ToString()
{
if (Name == null)
return "";
StringBuilder orderByClause = new();
orderByClause.Append('[');

if (Name.Body is MemberExpression expression &&
expression.Expression.NodeType != ExpressionType.Convert &&
expression.Expression.NodeType != ExpressionType.Parameter)
expression.Expression?.NodeType != ExpressionType.Convert &&
expression.Expression?.NodeType != ExpressionType.Parameter)
orderByClause.Append(Expression.Lambda(expression).Compile().DynamicInvoke());
else
orderByClause.Append(Name.Stringfy());
Expand Down
5 changes: 4 additions & 1 deletion 01-Core/Jinget.Core/ExpressionToSql/Internal/Where.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ private static WherePart Recurse(ref int i, Expression expression, bool isUnary
return WherePart.Concat(Where<T, R>.Recurse(ref i, member), "=", WherePart.IsParameter(i++, true));
}

if (((MemberExpression)expression).Expression.NodeType == ExpressionType.Constant)
if (((MemberExpression)expression).Expression?.NodeType == ExpressionType.Constant)
{
goto case_ConstantExpression;
}
Expand Down Expand Up @@ -260,6 +260,9 @@ public static WherePart IsCollection(ref int countStart, IEnumerable values)

public static WherePart Concat(WherePart left, string @operator, WherePart right)
{
if (right.Sql == null)
throw new Exception("Jinget Says: Right-side expression can not be null here");

//these operators does not need to append @
List<string> excludedList = ["IN", "AND", "OR"];
var rightExpr = !excludedList.Contains(@operator) && !right.Sql.StartsWith("@")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,23 +14,39 @@ public static class DynamicParametersExtensions
/// <returns></returns>
public static List<dynamic> GetSQLValues(this DynamicParameters parameters)
{
List<object> lstValues = [];
var t = parameters.GetType().GetField("parameters", BindingFlags.NonPublic | BindingFlags.Instance);
List<dynamic> lstValues = [];
var parametersField = parameters.GetType().GetField("parameters", BindingFlags.NonPublic | BindingFlags.Instance);

if (t == null)
if (parametersField == null)
return lstValues;

foreach (DictionaryEntry dictionaryEntry in (IDictionary)t.GetValue(parameters))
if (parametersField.GetValue(parameters) is IDictionary parameterDictionary)
{
var dbType = (DbType)dictionaryEntry.Value.GetValue("DbType");
if (dbType.IsBooleanDbType())
lstValues.Add(parameters.Get<dynamic>(dictionaryEntry.Key.ToString()) == true ? 1 : 0);
else if (dbType.IsNumericDbType())
lstValues.Add(parameters.Get<dynamic>(dictionaryEntry.Key.ToString()));
else if (dbType.IsUnicodeDbType())
lstValues.Add("N'" + parameters.Get<dynamic>(dictionaryEntry.Key.ToString()) + "'");
else
lstValues.Add("'" + parameters.Get<dynamic>(dictionaryEntry.Key.ToString()) + "'");
foreach (DictionaryEntry dictionaryEntry in parameterDictionary)
{
var parameterValue = dictionaryEntry.Value;
if (parameterValue != null)
{
var dbTypeValue = parameterValue.GetType().GetProperty("DbType")?.GetValue(parameterValue);
if (dbTypeValue is DbType dbType)
{
var parameterKeyValue = dictionaryEntry.Key.ToString();
if (parameterKeyValue != null)
{
var parameterValueFromParameters = parameters.Get<dynamic>(parameterKeyValue);

if (dbType.IsBooleanDbType())
lstValues.Add(parameterValueFromParameters == true ? 1 : 0);
else if (dbType.IsNumericDbType())
lstValues.Add(parameterValueFromParameters);
else if (dbType.IsUnicodeDbType())
lstValues.Add("N'" + parameterValueFromParameters + "'");
else
lstValues.Add("'" + parameterValueFromParameters + "'");
}
}
}
}
}
return lstValues;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,18 +34,27 @@ internal static (string queryText, Dictionary<string, object?>? queryParameters)
countQueryText = queryText[queryText.IndexOf(" FROM ", StringComparison.CurrentCultureIgnoreCase)..];
countQueryText = "Select Count(*) " + countQueryText;

string pagingStatement = "";
string? pagingStatement = "";
if (orderBy == null)
{
var iOrderByInterface = param.GetType().GetInterface(typeof(IOrderBy<>).Name);
var orderByProperty = iOrderByInterface.GetProperty("OrderBy");
var orderByValue = orderByProperty.GetValue(param);
pagingStatement = typeof(PagingExtensions).Call(
"GetPaging",
[typeof(Paging), orderByValue.GetType()],
[paging, orderByValue],
param.GetType()
).ToString();
if (iOrderByInterface != null)
{
var orderByProperty = iOrderByInterface.GetProperty("OrderBy");
if (orderByProperty != null)
{
var orderByValue = orderByProperty.GetValue(param);
if (orderByValue != null)
{
pagingStatement = typeof(PagingExtensions).Call(
"GetPaging",
[typeof(Paging), orderByValue.GetType()],
[paging, orderByValue],
param.GetType()
)?.ToString();
}
}
}
}
else
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,12 @@ static Expression<Func<T, bool>> CreateBinaryExpression<T>(
if (expr2 is null) return expr1;
var parameter = Expression.Parameter(typeof(T), parameterName);
(Expression? LeftExpression, Expression? RightExpression) = Visit(expr1, expr2, parameter);
if (LeftExpression == null || RightExpression == null)
{
throw new ArgumentNullException("Visit method returned null expression");
}
BinaryExpression binaryExpr;

if (expressionType == ExpressionType.AndAlso)
binaryExpr = Expression.AndAlso(LeftExpression, RightExpression);
else if (expressionType == ExpressionType.OrElse)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@ public static bool IsSubclassOfRawGeneric(this Type derivedType, Type parentType
{
return true;
}
#pragma warning disable CS8600 // Converting null literal or possible null value to non-nullable type.
derivedType = derivedType.BaseType;
#pragma warning restore CS8600 // Converting null literal or possible null value to non-nullable type.
}
return false;
}
Expand Down
94 changes: 83 additions & 11 deletions 01-Core/Jinget.Core/ExtensionMethods/HttpContextExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,22 +1,94 @@
namespace Jinget.Core.ExtensionMethods;
using System.Net;

namespace Jinget.Core.ExtensionMethods;

public static class HttpContextExtensions
{
/// <summary>
/// Check if given request is a multipart request or not
/// Determines whether the current HTTP request has a multipart content type (specifically "multipart/form-data").
/// </summary>
public static bool IsMultipartContentType(this HttpContext context) =>
context.Request.GetTypedHeaders().ContentType != null &&
context.Request.GetTypedHeaders().ContentType.MediaType.Value.ToLower().StartsWith("multipart/form-data");
/// <param name="context">The HttpContext representing the current HTTP request.</param>
/// <returns>True if the content type is multipart/form-data; otherwise, false.</returns>
/// <remarks>
/// This method checks the Content-Type header of the request to determine if it starts with "multipart/form-data".
/// It uses the GetTypedHeaders() extension method for robust header parsing and performs a case-insensitive comparison.
/// </remarks>
public static bool IsMultipartContentType(this HttpContext context)
{
// Retrieve the parsed Content-Type header from the request.
var contentTypeHeader = context.Request.GetTypedHeaders().ContentType;

// Check if the Content-Type header exists.
if (contentTypeHeader != null)
{
// Retrieve the media type value from the parsed header.
var mediaType = contentTypeHeader.MediaType.Value;

// Check if the media type value exists and starts with "multipart/form-data" (case-insensitive).
if (mediaType != null && mediaType.StartsWith("multipart/form-data", StringComparison.CurrentCultureIgnoreCase))
{
return true; // It's a multipart/form-data request.
}
}

return false; // It's not a multipart/form-data request.
}

/// <summary>
/// Get request connecton ip address
/// Retrieves the client's IP address from the HttpContext.
/// </summary>
public static string GetIpAddress(this HttpContext context) =>
context.Connection.RemoteIpAddress == null
? "Unknown"
: context.Connection.RemoteIpAddress.ToString();
/// <param name="context">The HttpContext from which to retrieve the IP address.</param>
/// <param name="customClientIpHeader">
/// The header name containing the client's IP address (e.g., "X-Forwarded-For").
/// Defaults to "X-Forwarded-For".
/// </param>
/// <returns>
/// The client's IP address as a string, or "Unknown" if the IP address cannot be determined.
/// </returns>
/// <remarks>
/// This method first attempts to retrieve the IP address from the specified custom header.
/// If the header is not found or the IP address in the header is invalid, it falls back to the
/// RemoteIpAddress property of the HttpContext.Connection.
/// The RemoteIpAddress property represents the IP address of the immediate connection to the server.
/// In environments with reverse proxies or load balancers, the RemoteIpAddress may not represent
/// the actual client's IP address.
/// The method also validates the IP address retrieved from the custom header using IPAddress.TryParse.
/// </remarks>
public static string GetClientIpAddress(this HttpContext context, string customClientIpHeader = "X-Forwarded-For")
{
// Get the IP address of the immediate connection to the server.
// This may be the proxy or load balancer's IP, not the client's.
var ipAddress = context.Connection.RemoteIpAddress?.ToString();

// Check if the specified custom header contains the client's IP address.
if (context.Request.Headers.TryGetValue(customClientIpHeader, out var clientIpAddress))
{
// Get the first IP address from the header (in case of multiple IPs).
var firstIp = clientIpAddress.ToString().Split(',')[0].Trim();

public static bool EndpointIsAuthorized(this HttpContext httpContext)
// Validate the IP address format.
if (IPAddress.TryParse(firstIp, out _))
{
// If valid, use the IP address from the header.
ipAddress = firstIp;
}
}

// Return the retrieved IP address, or "Unknown" if not found.
return ipAddress ?? "Unknown";
}

/// <summary>
/// Checks if the endpoint associated with the current HTTP request has the [Authorize] attribute.
/// </summary>
/// <param name="httpContext">The HttpContext for the current request.</param>
/// <returns>True if the endpoint has the [Authorize] attribute; otherwise, false.</returns>
/// <remarks>
/// This method retrieves the endpoint from the HttpContext and checks for the presence of the AuthorizeAttribute.
/// It returns true if the attribute is found; otherwise, it returns false.
/// This method does not evaluate authorization policies. It only checks for the existence of the attribute.
/// </remarks>
public static bool EndpointIsDecoratedWithAuthorizeAttribute(this HttpContext httpContext)
=> httpContext.GetEndpoint()?.Metadata.GetMetadata<AuthorizeAttribute>() != null;

}
2 changes: 1 addition & 1 deletion 01-Core/Jinget.Core/ExtensionMethods/ObjectExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@ public static bool HasDefaultValue(this object value)
if (Nullable.GetUnderlyingType(type) != null)
return false;

object defaultValue = Activator.CreateInstance(type);
object? defaultValue = Activator.CreateInstance(type);
return value.Equals(defaultValue);
}
}
Loading

0 comments on commit 1530ec0

Please sign in to comment.