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
2 changes: 1 addition & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
</PropertyGroup>

<PropertyGroup>
<VersionPrefix>1.0.18</VersionPrefix>
<VersionPrefix>1.0.19</VersionPrefix>
</PropertyGroup>

<Choose>
Expand Down
3 changes: 3 additions & 0 deletions System.Linq.Dynamic.Core.sln.DotSettings
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=EF/@EntryIndexedValue">EF</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=UTC/@EntryIndexedValue">UTC</s:String></wpf:ResourceDictionary>
15 changes: 15 additions & 0 deletions src-console/ConsoleAppEF2.1.1_InMemory/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Linq.Dynamic.Core;
using System.Linq.Dynamic.Core.CustomTypeProviders;
Expand Down Expand Up @@ -74,6 +75,20 @@ private static void StringEscapeTest()

static void Main(string[] args)
{
var d = new[] { DateTime.UtcNow }.AsQueryable();

//DateTime.TryParse("Fri, 10 May 2019 11:03:17 GMT", CultureInfo.InvariantCulture, DateTimeStyles.None, out var dateTime);
//dateTime.ToUniversalTime().Dump();

var r = d.Where(dd => dd > DateTime.Parse("Fri, 10 May 2019 11:03:17 GMT")).ToArray();

var r2 = d.Where("it > \"Fri, 10 May 2019 11:03:17 GMT\"").ToArray();

// DateTime.Now.ToString("R").Dump();

return;


StringEscapeTest();
//var q = new[] { new NestedDto(), new NestedDto { NestedDto2 = new NestedDto2 { NestedDto3 = new NestedDto3 { Id = 42 } } } }.AsQueryable();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
<PropertyGroup>
<Description>Dynamic Linq extensions for EntityFramework which adds Async support</Description>
<AssemblyTitle>EntityFramework.DynamicLinq</AssemblyTitle>
<!--<VersionPrefix>1.0.9.1</VersionPrefix>-->
<Authors>Stef Heyenrath</Authors>
<TargetFrameworks>net45;net46</TargetFrameworks>
<DefineConstants>EF</DefineConstants>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
<PropertyGroup>
<Description>Dynamic Linq extensions for Microsoft.EntityFrameworkCore which adds Async support</Description>
<AssemblyTitle>Microsoft.EntityFrameworkCore.DynamicLinq</AssemblyTitle>
<!--<VersionPrefix>1.0.9.1</VersionPrefix>-->
<Authors>Stef Heyenrath</Authors>
<TargetFrameworks>net451;net46;netstandard1.3;netstandard2.0;uap10.0;netcoreapp2.1</TargetFrameworks>
<DefineConstants>$(DefineConstants);EFCORE</DefineConstants>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System.Linq;

// ReSharper disable once CheckNamespace
namespace System.Reflection
{
/// <summary>
Expand Down Expand Up @@ -36,4 +37,4 @@ public static Type[] GetGenericTypeArguments(this TypeInfo typeInfo)
}
#endif
}
}
}
31 changes: 12 additions & 19 deletions src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using System.Linq.Dynamic.Core.Parser.SupportedMethods;
using System.Linq.Dynamic.Core.Parser.SupportedOperands;
using System.Linq.Dynamic.Core.Tokenizer;
using System.Linq.Dynamic.Core.TypeConverters;
using System.Linq.Dynamic.Core.Validation;
using System.Linq.Expressions;
using System.Reflection;
Expand All @@ -29,6 +30,7 @@ public class ExpressionParser
private readonly TextParser _textParser;
private readonly IExpressionHelper _expressionHelper;
private readonly ITypeFinder _typeFinder;
private readonly ITypeConverterFactory _typeConverterFactory;
private readonly Dictionary<string, object> _internals;
private readonly Dictionary<string, object> _symbols;

Expand Down Expand Up @@ -75,6 +77,7 @@ public ExpressionParser([CanBeNull] ParameterExpression[] parameters, [NotNull]
_methodFinder = new MethodFinder(_parsingConfig);
_expressionHelper = new ExpressionHelper(_parsingConfig);
_typeFinder = new TypeFinder(_parsingConfig, _keywordsHelper);
_typeConverterFactory = new TypeConverterFactory(_parsingConfig);
}

void ProcessParameters(ParameterExpression[] parameters)
Expand Down Expand Up @@ -477,13 +480,13 @@ Expression ParseComparisonOperator()
}
}
}
else if ((constantExpr = right as ConstantExpression) != null && constantExpr.Value is string && (typeConverter = TypeConverterFactory.GetConverter(left.Type)) != null)
else if ((constantExpr = right as ConstantExpression) != null && constantExpr.Value is string stringValueR && (typeConverter = _typeConverterFactory.GetConverter(left.Type)) != null)
{
right = Expression.Constant(typeConverter.ConvertFromInvariantString((string)constantExpr.Value), left.Type);
right = Expression.Constant(typeConverter.ConvertFromInvariantString(stringValueR), left.Type);
}
else if ((constantExpr = left as ConstantExpression) != null && constantExpr.Value is string && (typeConverter = TypeConverterFactory.GetConverter(right.Type)) != null)
else if ((constantExpr = left as ConstantExpression) != null && constantExpr.Value is string stringValueL && (typeConverter = _typeConverterFactory.GetConverter(right.Type)) != null)
{
left = Expression.Constant(typeConverter.ConvertFromInvariantString((string)constantExpr.Value), right.Type);
left = Expression.Constant(typeConverter.ConvertFromInvariantString(stringValueL), right.Type);
}
else
{
Expand Down Expand Up @@ -1512,7 +1515,7 @@ Expression ParseTypeAccess(Type type)
return ParseMemberAccess(type, null);
}

static Expression GenerateConversion(Expression expr, Type type, int errorPos)
private Expression GenerateConversion(Expression expr, Type type, int errorPos)
{
Type exprType = expr.Type;
if (exprType == type)
Expand Down Expand Up @@ -1543,21 +1546,11 @@ static Expression GenerateConversion(Expression expr, Type type, int errorPos)
{
string text = (string)((ConstantExpression)expr).Value;

// DateTime is parsed as UTC time.
if (type == typeof(DateTime) && DateTime.TryParse(text, CultureInfo.InvariantCulture, DateTimeStyles.None, out var dateTime))
var typeConvertor = _typeConverterFactory.GetConverter(type);
if (typeConvertor != null)
{
return Expression.Constant(dateTime, type);
}

object[] arguments = { text, null };
#if NETFX_CORE || WINDOWS_APP || DOTNET5_1 || UAP10_0 || NETSTANDARD
MethodInfo method = type.GetMethod("TryParse", new[] { typeof(string), type.MakeByRefType() });
#else
MethodInfo method = type.GetMethod("TryParse", BindingFlags.Public | BindingFlags.Static, null, new Type[] { typeof(string), type.MakeByRefType() }, null);
#endif
if (method != null && (bool)method.Invoke(null, arguments))
{
return Expression.Constant(arguments[1], type);
var value = typeConvertor.ConvertFromInvariantString(text);
return Expression.Constant(value, type);
}
}

Expand Down
39 changes: 31 additions & 8 deletions src/System.Linq.Dynamic.Core/ParsingConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -92,58 +92,81 @@ public IQueryableAnalyzer QueryableAnalyzer
/// <summary>
/// Determines if the context keywords (it, parent, and root) are valid and usable inside a Dynamic Linq string expression.
/// Does not affect the usability of the equivalent context symbols ($, ^ and ~).
///
/// Default value is true.
/// </summary>
public bool AreContextKeywordsEnabled { get; set; } = true;

/// <summary>
/// Gets or sets a value indicating whether to use dynamic object class for anonymous types. Default value is false.
/// Gets or sets a value indicating whether to use dynamic object class for anonymous types.
///
/// Default value is false.
/// </summary>
public bool UseDynamicObjectClassForAnonymousTypes { get; set; } = false;

/// <summary>
/// Gets or sets a value indicating whether the EntityFramework version supports evaluating GroupBy at database level. Default value is false.
/// Gets or sets a value indicating whether the EntityFramework version supports evaluating GroupBy at database level.
/// See https://docs.microsoft.com/en-us/ef/core/what-is-new/ef-core-2.1#linq-groupby-translation
///
/// Remark: when this setting is set to 'true', make sure to supply this ParsingConfig as first parameter on the extension methods.
///
/// Default value is false.
/// </summary>
public bool EvaluateGroupByAtDatabase { get; set; } = false;

/// <summary>
/// Use Parameterized Names in generated dynamic SQL query. Default set to false.
/// Use Parameterized Names in generated dynamic SQL query.
/// See https://github.com/graeme-hill/gblog/blob/master/source_content/articles/2014.139_entity-framework-dynamic-queries-and-parameterization.mkd
///
/// Default value is false.
/// </summary>
public bool UseParameterizedNamesInDynamicQuery { get; set; } = false;

/// <summary>
/// Allows the New() keyword to evaluate any available Type. Default value is false.
/// Allows the New() keyword to evaluate any available Type.
///
/// Default value is false.
/// </summary>
public bool AllowNewToEvaluateAnyType { get; set; } = false;

/// <summary>
/// Renames the (Typed)ParameterExpression empty Name to a the correct supplied name from `it`. Default value is false.
/// Renames the (Typed)ParameterExpression empty Name to a the correct supplied name from `it`.
///
/// Default value is false.
/// </summary>
public bool RenameParameterExpression { get; set; } = false;

/// <summary>
/// By default when a member is not found in a type and the type has a string based index accessor it will be parsed as an index accessor. Use
/// this flag to disable this behaviour and have parsing fail when parsing an expression
/// where a member access on a non existing member happens. Default value is false.
/// where a member access on a non existing member happens.
///
/// Default value is false.
/// </summary>
public bool DisableMemberAccessToIndexAccessorFallback { get; set; } = false;

/// <summary>
/// By default finding types by a simple name is not suported.
/// By default finding types by a simple name is not supported.
/// Use this flag to use the CustomTypeProvider to resolve types by a simple name like "Employee" instead of "MyDatabase.Entities.Employee".
/// Note that a first matching type is returned and this functionality needs to scan all types from all assemblies, so use with caution.
///
/// Default value is false.
/// </summary>
public bool ResolveTypesBySimpleName { get; set; } = false;

/// <summary>
/// Support enumeration-types from the System namespace in mscorelib. An example could be "StringComparison".
/// Support enumeration-types from the System namespace in mscorlib. An example could be "StringComparison".
///
/// Default value is true.
/// </summary>
public bool SupportEnumerationsFromSystemNamespace { get; set; } = true;

/// <summary>
/// By default DateTime (like 'Fri, 10 May 2019 11:03:17 GMT') is parsed as local time.
/// Use this flag to parse all DateTime strings as UTC.
///
/// Default value is false.
/// </summary>
public bool DateTimeIsParsedAsUTC { get; set; } = false;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using System.ComponentModel;
using System.Globalization;

namespace System.Linq.Dynamic.Core.TypeConverters
{
internal class CustomDateTimeConverter : DateTimeOffsetConverter
{
/// <summary>
/// Converts the specified object to a <see cref="DateTime"></see>.
/// </summary>
/// <param name="context">The date format context.</param>
/// <param name="culture">The date culture.</param>
/// <param name="value">The object to be converted.</param>
/// <returns>A <see cref="Nullable{DateTime}"></see> that represents the specified object.</returns>
/// <exception cref="NotSupportedException">The conversion cannot be performed.</exception>
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
var dateTimeOffset = base.ConvertFrom(context, culture, value) as DateTimeOffset?;

return dateTimeOffset?.UtcDateTime;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using JetBrains.Annotations;
using System.ComponentModel;

namespace System.Linq.Dynamic.Core.TypeConverters
{
interface ITypeConverterFactory
{
/// <summary>
/// Returns a type converter for the specified type.
/// </summary>
/// <param name="type">The System.Type of the target component.</param>
/// <returns>A System.ComponentModel.TypeConverter for the specified type.</returns>
TypeConverter GetConverter([NotNull] Type type);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,29 @@
using System.ComponentModel;
using System.Linq.Dynamic.Core.Validation;

namespace System.Linq.Dynamic.Core
namespace System.Linq.Dynamic.Core.TypeConverters
{
internal static class TypeConverterFactory
internal class TypeConverterFactory : ITypeConverterFactory
{
/// <summary>
/// Returns a type converter for the specified type.
/// </summary>
/// <param name="type">The System.Type of the target component.</param>
/// <returns>A System.ComponentModel.TypeConverter for the specified type.</returns>
public static TypeConverter GetConverter([NotNull] Type type)
private readonly ParsingConfig _config;

public TypeConverterFactory([NotNull] ParsingConfig config)
{
Check.NotNull(config, nameof(config));

_config = config;
}

/// <see cref="ITypeConverterFactory.GetConverter"/>
public TypeConverter GetConverter(Type type)
{
Check.NotNull(type, nameof(type));

if (_config.DateTimeIsParsedAsUTC && (type == typeof(DateTime) || type == typeof(DateTime?)))
{
return new CustomDateTimeConverter();
}

#if !SILVERLIGHT
return TypeDescriptor.GetConverter(type);
#else
Expand All @@ -33,4 +43,4 @@ public static TypeConverter GetConverter([NotNull] Type type)
#endif
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ public partial class QueryableTests
public class Example
{
public DateTime Time { get; set; }
public DateTime? TimeNull { get; set; }
public DayOfWeek? DOWNull { get; set; }
public DayOfWeek DOW { get; set; }
public int Sec { get; set; }
Expand Down
Loading