Skip to content

Commit 7281dc9

Browse files
authored
SupportDotInPropertyNames (#719)
1 parent ab20e22 commit 7281dc9

File tree

4 files changed

+73
-4
lines changed

4 files changed

+73
-4
lines changed

src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs

+29-4
Original file line numberDiff line numberDiff line change
@@ -1357,8 +1357,7 @@ private Expression ParseNew()
13571357
if (TokenIdentifierIs("as"))
13581358
{
13591359
_textParser.NextToken();
1360-
propName = GetIdentifier();
1361-
_textParser.NextToken();
1360+
propName = GetIdentifierAs();
13621361
}
13631362
else
13641363
{
@@ -1648,7 +1647,7 @@ private Expression ParseTypeAccess(Type type, bool getNext)
16481647
return ParseMemberAccess(type, null);
16491648
}
16501649

1651-
private bool TryGenerateConversion(Expression sourceExpression, Type destinationType, [NotNullWhen(true) ]out Expression? expression)
1650+
private bool TryGenerateConversion(Expression sourceExpression, Type destinationType, [NotNullWhen(true)] out Expression? expression)
16521651
{
16531652
Type exprType = sourceExpression.Type;
16541653
if (exprType == destinationType)
@@ -2261,7 +2260,33 @@ private bool TokenIdentifierIs(string id)
22612260
private string GetIdentifier()
22622261
{
22632262
_textParser.ValidateToken(TokenId.Identifier, Res.IdentifierExpected);
2264-
string id = _textParser.CurrentToken.Text;
2263+
2264+
return SanitizeId(_textParser.CurrentToken.Text);
2265+
}
2266+
2267+
private string GetIdentifierAs()
2268+
{
2269+
_textParser.ValidateToken(TokenId.Identifier, Res.IdentifierExpected);
2270+
2271+
if (!_parsingConfig.SupportDotInPropertyNames)
2272+
{
2273+
var id = SanitizeId(_textParser.CurrentToken.Text);
2274+
_textParser.NextToken();
2275+
return id;
2276+
}
2277+
2278+
var parts = new List<string>();
2279+
while (_textParser.CurrentToken.Id is TokenId.Dot or TokenId.Identifier)
2280+
{
2281+
parts.Add(_textParser.CurrentToken.Text);
2282+
_textParser.NextToken();
2283+
}
2284+
2285+
return SanitizeId(string.Concat(parts));
2286+
}
2287+
2288+
private static string SanitizeId(string id)
2289+
{
22652290
if (id.Length > 1 && id[0] == '@')
22662291
{
22672292
id = id.Substring(1);

src/System.Linq.Dynamic.Core/ParsingConfig.cs

+7
Original file line numberDiff line numberDiff line change
@@ -220,4 +220,11 @@ public IQueryableAnalyzer QueryableAnalyzer
220220
/// Default value is <c>true</c>.
221221
/// </summary>
222222
public bool PrioritizePropertyOrFieldOverTheType { get; set; } = true;
223+
224+
/// <summary>
225+
/// Support a "." in a property-name. Used in the 'new (a.b as a.b)' syntax.
226+
///
227+
/// Default value is <c>false</c>.
228+
/// </summary>
229+
public bool SupportDotInPropertyNames { get; set; } = false;
223230
}

test/System.Linq.Dynamic.Core.Tests/DynamicClassFactoryTests.cs

+19
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,25 @@ public void CreateGenericComparerTypeForDateTime()
8383
int lessThan = instance.Compare(b, a);
8484
lessThan.Should().Be(-1);
8585
}
86+
87+
[Fact]
88+
public void CreateType_With_PropertyWithDot()
89+
{
90+
// Arrange
91+
var properties = new List<DynamicProperty>
92+
{
93+
new("x.y", typeof(string))
94+
};
95+
96+
// Act
97+
var type = DynamicClassFactory.CreateType(properties);
98+
99+
// Assert
100+
type.GetProperty("x.y").Should().NotBeNull();
101+
102+
var instance = Activator.CreateInstance(type);
103+
instance.Should().NotBeNull();
104+
}
86105
}
87106

88107
public class CustomCaseInsensitiveComparer : IComparer

test/System.Linq.Dynamic.Core.Tests/QueryableTests.Select.cs

+18
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using System.IO;
44
using System.Linq.Dynamic.Core.Exceptions;
55
using System.Linq.Dynamic.Core.Tests.Helpers.Models;
6+
using FluentAssertions;
67
using Linq.PropertyTranslator.Core;
78
using QueryInterceptor.Core;
89
using Xunit;
@@ -86,6 +87,23 @@ public void Select_Dynamic_PropertyInBaseClass()
8687
Assert.Equal(expected.ToArray(), dynamic.ToArray());
8788
}
8889

90+
[Fact]
91+
public void Select_Dynamic_As_WithDot()
92+
{
93+
// Assign
94+
var qry = User.GenerateSampleModels(1).AsQueryable();
95+
var config = new ParsingConfig
96+
{
97+
SupportDotInPropertyNames = true
98+
};
99+
100+
// Act
101+
var result = qry.Select(config, "new (Profile.FirstName as P.FirstName)").ToDynamicArray();
102+
103+
// Assert
104+
result.Should().HaveCount(1);
105+
}
106+
89107
[Fact]
90108
public void Select_Dynamic1()
91109
{

0 commit comments

Comments
 (0)