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
8 changes: 6 additions & 2 deletions src/System.Linq.Dynamic.Core/DynamicClass.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Dynamic;
using System.Reflection;
using System.Runtime.CompilerServices;

namespace System.Linq.Dynamic.Core;

Expand All @@ -18,6 +19,8 @@ namespace System.Linq.Dynamic.Core;
/// </summary>
public abstract class DynamicClass : DynamicObject
{
internal const string IndexerName = "System_Linq_Dynamic_Core_DynamicClass_Indexer";

private Dictionary<string, object?>? _propertiesDictionary;

private Dictionary<string, object?> Properties
Expand Down Expand Up @@ -99,11 +102,12 @@ public void SetDynamicPropertyValue(string propertyName, object value)
/// <value>The <see cref="object"/>.</value>
/// <param name="name">The name.</param>
/// <returns>Value from the property.</returns>
[IndexerName(IndexerName)]
public object? this[string name]
{
get
{
return Properties.TryGetValue(name, out object? result) ? result : null;
return Properties.TryGetValue(name, out var result) ? result : null;
}

set
Expand Down Expand Up @@ -153,7 +157,7 @@ public override bool TryGetMember(GetMemberBinder binder, out object? result)
/// </returns>
public override bool TrySetMember(SetMemberBinder binder, object? value)
{
string name = binder.Name;
var name = binder.Name;
if (Properties.ContainsKey(name))
{
Properties[name] = value;
Expand Down
2 changes: 2 additions & 0 deletions src/System.Linq.Dynamic.Core/DynamicClass.net35.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ namespace System.Linq.Dynamic.Core;
/// </summary>
public abstract class DynamicClass
{
internal const string IndexerName = "System_Linq_Dynamic_Core_DynamicClass_Indexer";

/// <summary>
/// Gets the dynamic property by name.
/// </summary>
Expand Down
4 changes: 4 additions & 0 deletions src/System.Linq.Dynamic.Core/DynamicClass.uap.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#if UAP10_0
using System.Collections.Generic;
using System.Dynamic;
using System.Runtime.CompilerServices;

namespace System.Linq.Dynamic.Core;

Expand All @@ -9,6 +10,8 @@ namespace System.Linq.Dynamic.Core;
/// </summary>
public class DynamicClass : DynamicObject
{
internal const string IndexerName = "System_Linq_Dynamic_Core_DynamicClass_Indexer";

private readonly Dictionary<string, object> _properties = new();

/// <summary>
Expand All @@ -31,6 +34,7 @@ public DynamicClass(params KeyValuePair<string, object>[] propertylist)
/// </value>
/// <param name="name">The name.</param>
/// <returns>Value from the property.</returns>
[IndexerName(IndexerName)]
public object this[string name]
{
get
Expand Down
2 changes: 1 addition & 1 deletion src/System.Linq.Dynamic.Core/DynamicQueryableExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@
ParameterExpression parameter = ParameterExpressionHelper.CreateParameterExpression(source.ElementType, "s");
Expression selector = Expression.Lambda(Expression.MakeMemberAccess(parameter, property), parameter);
// We've tried to find an expression of the type Expression<Func<TSource, TAcc>>,
// which is expressed as ( (TSource s) => s.Price );

Check warning on line 69 in src/System.Linq.Dynamic.Core/DynamicQueryableExtensions.cs

View workflow job for this annotation

GitHub Actions / Linux: Build and Tests

Remove this commented out code. (https://rules.sonarsource.com/csharp/RSPEC-125)

var methods = typeof(Queryable).GetMethods().Where(x => x.Name == function && x.IsGenericMethod).ToArray();

Expand Down Expand Up @@ -2135,7 +2135,7 @@
/// <inheritdoc cref="SelectMany(IQueryable, ParsingConfig, string, string, string, string, object[], object[])"/>
public static IQueryable SelectMany(this IQueryable source, string collectionSelector, string resultSelector, string collectionParameterName, string resultParameterName, object?[]? collectionSelectorArgs = null, params object?[]? resultSelectorArgs)
{
return SelectMany(source, ParsingConfig.Default, collectionSelector, resultSelector, collectionParameterName, resultParameterName, collectionSelectorArgs, resultSelectorArgs);

Check warning on line 2138 in src/System.Linq.Dynamic.Core/DynamicQueryableExtensions.cs

View workflow job for this annotation

GitHub Actions / Linux: Build and Tests

Argument of type 'object?[]' cannot be used for parameter 'resultSelectorArgs' of type 'object[]' in 'IQueryable DynamicQueryableExtensions.SelectMany(IQueryable source, ParsingConfig config, string collectionSelector, string resultSelector, string collectionParameterName, string resultParameterName, object?[]? collectionSelectorArgs = null, params object[]? resultSelectorArgs)' due to differences in the nullability of reference types.

Check warning on line 2138 in src/System.Linq.Dynamic.Core/DynamicQueryableExtensions.cs

View workflow job for this annotation

GitHub Actions / Linux: Build and Tests

Argument of type 'object?[]' cannot be used for parameter 'resultSelectorArgs' of type 'object[]' in 'IQueryable DynamicQueryableExtensions.SelectMany(IQueryable source, ParsingConfig config, string collectionSelector, string resultSelector, string collectionParameterName, string resultParameterName, object?[]? collectionSelectorArgs = null, params object[]? resultSelectorArgs)' due to differences in the nullability of reference types.

Check warning on line 2138 in src/System.Linq.Dynamic.Core/DynamicQueryableExtensions.cs

View workflow job for this annotation

GitHub Actions / Windows: Build and Tests

Argument of type 'object?[]' cannot be used for parameter 'resultSelectorArgs' of type 'object[]' in 'IQueryable DynamicQueryableExtensions.SelectMany(IQueryable source, ParsingConfig config, string collectionSelector, string resultSelector, string collectionParameterName, string resultParameterName, object?[]? collectionSelectorArgs = null, params object[]? resultSelectorArgs)' due to differences in the nullability of reference types.

Check warning on line 2138 in src/System.Linq.Dynamic.Core/DynamicQueryableExtensions.cs

View workflow job for this annotation

GitHub Actions / Windows: Build and Tests

Argument of type 'object?[]' cannot be used for parameter 'resultSelectorArgs' of type 'object[]' in 'IQueryable DynamicQueryableExtensions.SelectMany(IQueryable source, ParsingConfig config, string collectionSelector, string resultSelector, string collectionParameterName, string resultParameterName, object?[]? collectionSelectorArgs = null, params object[]? resultSelectorArgs)' due to differences in the nullability of reference types.

Check warning on line 2138 in src/System.Linq.Dynamic.Core/DynamicQueryableExtensions.cs

View workflow job for this annotation

GitHub Actions / Linux: Build and Tests

Argument of type 'object?[]' cannot be used for parameter 'resultSelectorArgs' of type 'object[]' in 'IQueryable DynamicQueryableExtensions.SelectMany(IQueryable source, ParsingConfig config, string collectionSelector, string resultSelector, string collectionParameterName, string resultParameterName, object?[]? collectionSelectorArgs = null, params object[]? resultSelectorArgs)' due to differences in the nullability of reference types.

Check warning on line 2138 in src/System.Linq.Dynamic.Core/DynamicQueryableExtensions.cs

View workflow job for this annotation

GitHub Actions / Linux: Build and Tests

Argument of type 'object?[]' cannot be used for parameter 'resultSelectorArgs' of type 'object[]' in 'IQueryable DynamicQueryableExtensions.SelectMany(IQueryable source, ParsingConfig config, string collectionSelector, string resultSelector, string collectionParameterName, string resultParameterName, object?[]? collectionSelectorArgs = null, params object[]? resultSelectorArgs)' due to differences in the nullability of reference types.

Check warning on line 2138 in src/System.Linq.Dynamic.Core/DynamicQueryableExtensions.cs

View workflow job for this annotation

GitHub Actions / Linux: Build and Tests

Argument of type 'object?[]' cannot be used for parameter 'resultSelectorArgs' of type 'object[]' in 'IQueryable DynamicQueryableExtensions.SelectMany(IQueryable source, ParsingConfig config, string collectionSelector, string resultSelector, string collectionParameterName, string resultParameterName, object?[]? collectionSelectorArgs = null, params object[]? resultSelectorArgs)' due to differences in the nullability of reference types.
}

#endregion SelectMany
Expand Down Expand Up @@ -2849,7 +2849,7 @@

private static LambdaExpression EnsureLambdaExpressionReturnsObject(LambdaExpression lambdaExpression)
{
if (!lambdaExpression.GetReturnType().GetTypeInfo().IsSubclassOf(typeof(DynamicClass)))
if (!TypeHelper.IsDynamicClass(lambdaExpression.GetReturnType()))
{
return Expression.Lambda(Expression.Convert(lambdaExpression.Body, typeof(object)), lambdaExpression.Parameters.ToArray());
}
Expand Down
7 changes: 4 additions & 3 deletions src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1580,7 +1580,7 @@
var propertyInfos = type.GetProperties();
if (type.GetTypeInfo().BaseType == typeof(DynamicClass))
{
propertyInfos = propertyInfos.Where(x => x.Name != "Item").ToArray();
propertyInfos = propertyInfos.Where(x => x.Name != DynamicClass.IndexerName).ToArray();
}

var propertyTypes = propertyInfos.Select(p => p.PropertyType).ToArray();
Expand Down Expand Up @@ -1906,7 +1906,7 @@
#if UAP10_0 || NETSTANDARD1_3
if (type == typeof(DynamicClass))
{
return Expression.MakeIndex(expression, typeof(DynamicClass).GetProperty("Item"), new[] { Expression.Constant(id) });
return Expression.MakeIndex(expression!, typeof(DynamicClass).GetProperty(DynamicClass.IndexerName), [Expression.Constant(id)]);
}
#endif
if (TryFindPropertyOrField(type!, id, expression, out var propertyOrFieldExpression))
Expand All @@ -1920,7 +1920,8 @@

if (!_parsingConfig.DisableMemberAccessToIndexAccessorFallback && extraCheck)
{
var indexerMethod = expression?.Type.GetMethod("get_Item", new[] { typeof(string) });
var indexerName = TypeHelper.IsDynamicClass(type!) ? DynamicClass.IndexerName : "Item";
var indexerMethod = expression?.Type.GetMethod($"get_{indexerName}", [typeof(string)]);
if (indexerMethod != null)
{
return Expression.Call(expression, indexerMethod, Expression.Constant(id));
Expand Down Expand Up @@ -1954,8 +1955,8 @@
switch (member)
{
case PropertyInfo property:
var propertyIsStatic = property?.GetGetMethod().IsStatic ?? property?.GetSetMethod().IsStatic ?? false;

Check warning on line 1958 in src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs

View workflow job for this annotation

GitHub Actions / Linux: Build and Tests

Dereference of a possibly null reference.

Check warning on line 1958 in src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs

View workflow job for this annotation

GitHub Actions / Linux: Build and Tests

Dereference of a possibly null reference.

Check warning on line 1958 in src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs

View workflow job for this annotation

GitHub Actions / Linux: Build and Tests

Dereference of a possibly null reference.

Check warning on line 1958 in src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs

View workflow job for this annotation

GitHub Actions / Linux: Build and Tests

Dereference of a possibly null reference.

Check warning on line 1958 in src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs

View workflow job for this annotation

GitHub Actions / Linux: Build and Tests

Dereference of a possibly null reference.
propertyOrFieldExpression = propertyIsStatic ? Expression.Property(null, property) : Expression.Property(expression, property);

Check warning on line 1959 in src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs

View workflow job for this annotation

GitHub Actions / Linux: Build and Tests

Possible null reference argument for parameter 'property' in 'MemberExpression Expression.Property(Expression? expression, PropertyInfo property)'.

Check warning on line 1959 in src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs

View workflow job for this annotation

GitHub Actions / Linux: Build and Tests

Possible null reference argument for parameter 'property' in 'MemberExpression Expression.Property(Expression? expression, PropertyInfo property)'.
return true;

case FieldInfo field:
Expand Down
5 changes: 5 additions & 0 deletions src/System.Linq.Dynamic.Core/Parser/TypeHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ namespace System.Linq.Dynamic.Core.Parser;

internal static class TypeHelper
{
internal static bool IsDynamicClass(Type type)
{
return type == typeof(DynamicClass) || type.GetTypeInfo().IsSubclassOf(typeof(DynamicClass));
}

internal static bool TryGetAsEnumerable(Type type, [NotNullWhen(true)] out Type? enumerableType)
{
if (type.IsArray)
Expand Down
11 changes: 11 additions & 0 deletions test/System.Linq.Dynamic.Core.Tests/EntitiesTests.Select.cs
Original file line number Diff line number Diff line change
Expand Up @@ -164,4 +164,15 @@ public void Entities_Select_DynamicClass_And_Select_DynamicClass()

dynamicResult.Should().BeEquivalentTo([1000, 1001]);
}

[Fact]
public void Entities_Select_ClassWithItemProperty()
{
// Act
var result = _context.Posts.Select(x => new { x.Item, x.BlogId }).ToArray();
var resultDynamic = _context.Posts.Select("new (Item, BlogId)").ToDynamicArray();

// Assert
resultDynamic.Should().BeEquivalentTo(result);
}
}
5 changes: 3 additions & 2 deletions test/System.Linq.Dynamic.Core.Tests/EntitiesTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ namespace System.Linq.Dynamic.Core.Tests;

public partial class EntitiesTests : IClassFixture<EntitiesTestsDatabaseFixture>
{
private static readonly Random Rnd = new Random(1);
private static readonly Random Rnd = new(1);

private readonly BlogContext _context;

Expand Down Expand Up @@ -66,7 +66,8 @@ private void InternalPopulateTestData()
Content = "My Content",
PostDate = postDate,
CloseDate = Rnd.Next(0, 10) < 5 ? postDate.AddDays(1) : null,
NumberOfReads = Rnd.Next(0, 5000)
NumberOfReads = Rnd.Next(0, 5000),
Item = "Item " + Rnd.Next(0, 1000)
};

_context.Posts.Add(post);
Expand Down
2 changes: 2 additions & 0 deletions test/System.Linq.Dynamic.Core.Tests/Helpers/Entities/Post.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,6 @@ public class Post
public DateTime PostDate { get; set; }

public DateTime? CloseDate { get; set; }

public string? Item { get; set; }
}
27 changes: 27 additions & 0 deletions test/System.Linq.Dynamic.Core.Tests/QueryableTests.Select.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,14 @@ namespace System.Linq.Dynamic.Core.Tests
{
public partial class QueryableTests
{
[DynamicLinqType]
public class ClassWithItem
{
public string? Item { get; set; }

public int Value { get; set; }
}

[DynamicLinqType]
public class Example
{
Expand Down Expand Up @@ -536,5 +544,24 @@ public void Select_Dynamic_StringConcatDifferentTypes(string expression, string
// Act
queryable.Select(config, expression).ToDynamicArray<string>()[0].Should().Be(expectedResult);
}

[Fact]
public void Select_Dynamic_ClassWithItemProperty()
{
// Arrange
var data = new []
{
new ClassWithItem { Item = "Value1", Value = 1 },
new ClassWithItem { Item = "Value2", Value = 2 }
};
var queryable = data.AsQueryable();

// Act
var result = queryable.Select(x => new {x.Item, x.Value }).ToArray();
var resultDynamic = queryable.Select("new (Item, Value)").ToDynamicArray();

// Assert
resultDynamic.Should().BeEquivalentTo(result);
}
}
}
Loading