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
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
# v1.2.24 (27 November 2022)
- [#621](https://github.com/zzzprojects/System.Linq.Dynamic.Core/pull/621) - Fix Join on inherited class [bug] contributed by [StefH](https://github.com/StefH)
- [#646](https://github.com/zzzprojects/System.Linq.Dynamic.Core/pull/646) - Add more unittests for issue 645 contributed by [StefH](https://github.com/StefH)
- [#647](https://github.com/zzzprojects/System.Linq.Dynamic.Core/pull/647) - Support nullable notation "xxx?" in As expression [feature] contributed by [StefH](https://github.com/StefH)
- [#614](https://github.com/zzzprojects/System.Linq.Dynamic.Core/issues/614) - Join problem with inherited entities [bug]

# v1.2.23 (12 November 2022)
- [#644](https://github.com/zzzprojects/System.Linq.Dynamic.Core/pull/644) - Add support for .NET 7 and EF Core 7 [feature] contributed by [StefH](https://github.com/StefH)

Expand Down
2 changes: 1 addition & 1 deletion Generate-ReleaseNotes.bat
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
rem https://github.com/StefH/GitHubReleaseNotes

SET version=v1.2.23
SET version=v1.2.24

GitHubReleaseNotes --output CHANGELOG.md --exclude-labels invalid question documentation wontfix --language en --version %version% --token %GH_TOKEN%
14 changes: 13 additions & 1 deletion src-console/ConsoleAppEF6_InMemory/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,25 @@ static async Task Main(string[] args)
await using (var context = new TestContextEF6())
{
context.Products.Add(new ProductDynamic { NullableInt = 1, Dict = new Dictionary<string, object> { { "Name", "test" } } });
context.Products.Add(new ProductDynamic { NullableInt = 2, Dict = new Dictionary<string, object> { { "Name1", "test1" } } });
await context.SaveChangesAsync();
}

await using (var context = new TestContextEF6())
{
var intType = typeof(int).FullName;

var a1 = context.Products.Select($"\"{intType}\"(Key)").ToDynamicArray();
Console.WriteLine("a1 {0}", string.Join(",", a1));

var a2 = context.Products.Select($"\"{intType}\"?(Key)").ToDynamicArray();
Console.WriteLine("a2 {0}", string.Join(",", a2));
}

await using (var context = new TestContextEF6())
{
var resultsNormal = context.Products.Where(p => p.Dict["Name"] == "test").ToListAsync();

var results1 = await context.Products.Where("Dict.Name == @0", "test").ToListAsync();
Console.WriteLine("results1:");
foreach (var result in results1)
Expand Down
16 changes: 16 additions & 0 deletions src/System.Linq.Dynamic.Core/AnyOfTypes/AnyOfTypes.g.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by https://github.com/StefH/AnyOf.
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------

namespace AnyOfTypes
{
internal enum AnyOfType
{
Undefined = 0, First, Second, Third, Fourth, Fifth, Sixth, Seventh, Eighth, Ninth, Tenth
}
}
156 changes: 156 additions & 0 deletions src/System.Linq.Dynamic.Core/AnyOfTypes/AnyOf_2.g.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by https://github.com/StefH/AnyOf.
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------

using System;
using System.Diagnostics;
using System.Collections.Generic;

namespace AnyOfTypes
{
[DebuggerDisplay("{_thisType}, AnyOfType = {_currentType}; Type = {_currentValueType?.Name}; Value = '{ToString()}'")]
internal struct AnyOf<TFirst, TSecond>
{
private readonly string _thisType => $"AnyOf<{typeof(TFirst).Name}, {typeof(TSecond).Name}>";
private readonly int _numberOfTypes;
private readonly object _currentValue;
private readonly Type _currentValueType;
private readonly AnyOfType _currentType;

private readonly TFirst _first;
private readonly TSecond _second;

public readonly AnyOfType[] AnyOfTypes => new[] { AnyOfType.First, AnyOfType.Second };
public readonly Type[] Types => new[] { typeof(TFirst), typeof(TSecond) };
public bool IsUndefined => _currentType == AnyOfType.Undefined;
public bool IsFirst => _currentType == AnyOfType.First;
public bool IsSecond => _currentType == AnyOfType.Second;

public static implicit operator AnyOf<TFirst, TSecond>(TFirst value) => new AnyOf<TFirst, TSecond>(value);

public static implicit operator TFirst(AnyOf<TFirst, TSecond> @this) => @this.First;

public AnyOf(TFirst value)
{
_numberOfTypes = 2;
_currentType = AnyOfType.First;
_currentValue = value;
_currentValueType = typeof(TFirst);
_first = value;
_second = default;
}

public TFirst First
{
get
{
Validate(AnyOfType.First);
return _first;
}
}

public static implicit operator AnyOf<TFirst, TSecond>(TSecond value) => new AnyOf<TFirst, TSecond>(value);

public static implicit operator TSecond(AnyOf<TFirst, TSecond> @this) => @this.Second;

public AnyOf(TSecond value)
{
_numberOfTypes = 2;
_currentType = AnyOfType.Second;
_currentValue = value;
_currentValueType = typeof(TSecond);
_second = value;
_first = default;
}

public TSecond Second
{
get
{
Validate(AnyOfType.Second);
return _second;
}
}

private void Validate(AnyOfType desiredType)
{
if (desiredType != _currentType)
{
throw new InvalidOperationException($"Attempting to get {desiredType} when {_currentType} is set");
}
}

public AnyOfType CurrentType
{
get
{
return _currentType;
}
}

public object CurrentValue
{
get
{
return _currentValue;
}
}

public Type CurrentValueType
{
get
{
return _currentValueType;
}
}

public override int GetHashCode()
{
var fields = new object[]
{
_numberOfTypes,
_currentValue,
_currentType,
_first,
_second,
typeof(TFirst),
typeof(TSecond),
};
return HashCodeCalculator.GetHashCode(fields);
}

private bool Equals(AnyOf<TFirst, TSecond> other)
{
return _currentType == other._currentType &&
_numberOfTypes == other._numberOfTypes &&
EqualityComparer<object>.Default.Equals(_currentValue, other._currentValue) &&
EqualityComparer<TFirst>.Default.Equals(_first, other._first) &&
EqualityComparer<TSecond>.Default.Equals(_second, other._second);
}

public static bool operator ==(AnyOf<TFirst, TSecond> obj1, AnyOf<TFirst, TSecond> obj2)
{
return obj1.Equals(obj2);
}

public static bool operator !=(AnyOf<TFirst, TSecond> obj1, AnyOf<TFirst, TSecond> obj2)
{
return !obj1.Equals(obj2);
}

public override bool Equals(object obj)
{
return obj is AnyOf<TFirst, TSecond> o && Equals(o);
}

public override string ToString()
{
return IsUndefined ? null : $"{_currentValue}";
}
}
}
30 changes: 30 additions & 0 deletions src/System.Linq.Dynamic.Core/AnyOfTypes/HashCodeCalculator.g.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by https://github.com/StefH/AnyOf.
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------

using System.Collections.Generic;
using System.Linq;

namespace AnyOfTypes
{
// Code is based on https://github.com/Informatievlaanderen/hashcode-calculator
internal static class HashCodeCalculator
{
public static int GetHashCode(IEnumerable<object> hashFieldValues)
{
const int offset = unchecked((int)2166136261);
const int prime = 16777619;

static int HashCodeAggregator(int hashCode, object value) => value == null
? (hashCode ^ 0) * prime
: (hashCode ^ value.GetHashCode()) * prime;

return hashFieldValues.Aggregate(offset, HashCodeAggregator);
}
}
}
53 changes: 42 additions & 11 deletions src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
using System.Linq.Dynamic.Core.Validation;
using System.Linq.Expressions;
using System.Reflection;
using AnyOfTypes;

namespace System.Linq.Dynamic.Core.Parser
{
Expand Down Expand Up @@ -734,7 +735,7 @@ private Expression ParseUnary()

private Expression ParsePrimary()
{
Expression expr = ParsePrimaryStart();
var expr = ParsePrimaryStart();
_expressionHelper.WrapConstantExpression(ref expr);

while (true)
Expand All @@ -757,6 +758,7 @@ private Expression ParsePrimary()
break;
}
}

return expr;
}

Expand All @@ -766,38 +768,55 @@ private Expression ParsePrimaryStart()
{
case TokenId.Identifier:
return ParseIdentifier();

case TokenId.StringLiteral:
return ParseStringLiteral();
var expressionOrType = ParseStringLiteral(false);
return expressionOrType.IsFirst ? expressionOrType.First : ParseTypeAccess(expressionOrType.Second, false);

case TokenId.IntegerLiteral:
return ParseIntegerLiteral();

case TokenId.RealLiteral:
return ParseRealLiteral();

case TokenId.OpenParen:
return ParseParenExpression();

default:
throw ParseError(Res.ExpressionExpected);
}
}

private Expression ParseStringLiteral()
private AnyOf<Expression, Type> ParseStringLiteral(bool forceParseAsString)
{
_textParser.ValidateToken(TokenId.StringLiteral);

string result = StringParser.ParseString(_textParser.CurrentToken.Text);
var stringValue = StringParser.ParseString(_textParser.CurrentToken.Text);

if (_textParser.CurrentToken.Text[0] == '\'')
{
if (result.Length > 1)
if (stringValue.Length > 1)
{
throw ParseError(Res.InvalidCharacterLiteral);
}

_textParser.NextToken();
return ConstantExpressionHelper.CreateLiteral(result[0], result);
return ConstantExpressionHelper.CreateLiteral(stringValue[0], stringValue);
}

_textParser.NextToken();
return ConstantExpressionHelper.CreateLiteral(result, result);

if (_parsingConfig.SupportFullTypeCastingUsingDoubleQuotes && !forceParseAsString && stringValue.Length > 2 && stringValue.Contains('.'))
{
// Try to resolve this string as a type
var type = _typeFinder.FindTypeByName(stringValue, null, false);
if (type is { })
{
return type;
}
}

return ConstantExpressionHelper.CreateLiteral(stringValue, stringValue);
}

private Expression ParseIntegerLiteral()
Expand Down Expand Up @@ -844,7 +863,7 @@ private Expression ParseIdentifier()
{
if (value is Type typeValue)
{
return ParseTypeAccess(typeValue);
return ParseTypeAccess(typeValue, true);
}

switch (value)
Expand Down Expand Up @@ -1476,10 +1495,13 @@ private Expression ParseLambdaInvocation(LambdaExpression lambda)
return Expression.Invoke(lambda, args);
}

private Expression ParseTypeAccess(Type type)
private Expression ParseTypeAccess(Type type, bool getNext)
{
int errorPos = _textParser.CurrentToken.Pos;
_textParser.NextToken();
if (getNext)
{
_textParser.NextToken();
}

if (_textParser.CurrentToken.Id == TokenId.Question)
{
Expand All @@ -1496,7 +1518,16 @@ private Expression ParseTypeAccess(Type type)
bool shorthand = _textParser.CurrentToken.Id == TokenId.StringLiteral;
if (_textParser.CurrentToken.Id == TokenId.OpenParen || shorthand)
{
Expression[] args = shorthand ? new[] { ParseStringLiteral() } : ParseArgumentList();
Expression[] args;
if (shorthand)
{
var expressionOrType = ParseStringLiteral(true);
args = new[] { expressionOrType.First };
}
else
{
args = ParseArgumentList();
}

// If only 1 argument and
// - the arg is ConstantExpression, return the conversion
Expand Down
Loading