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
5 changes: 4 additions & 1 deletion src/System.Linq.Dynamic.Core/DynamicClassFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,10 @@ public static Type CreateType([NotNull] IList<DynamicProperty> properties, bool
ilgeneratorToString.Emit(OpCodes.Pop);
}

if (createParameterCtor)
// Only create the default and with params constructor when there are any params.
// Otherwise default constructor is not needed because it matches the default
// one provided by the runtime when no constructor is present
if (createParameterCtor && names.Any())
{
// .ctor default
ConstructorBuilder constructorDef = tb.DefineConstructor(MethodAttributes.Public | MethodAttributes.HideBySig, CallingConventions.HasThis, EmptyTypes);
Expand Down
10 changes: 6 additions & 4 deletions src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1531,11 +1531,13 @@ Expression ParseMemberAccess(Type type, Expression instance)
return Expression.Dynamic(new DynamicGetMemberBinder(id), type, instance);
}
#endif

MethodInfo indexerMethod = instance.Type.GetMethod("get_Item", new[] { typeof(string) });
if (indexerMethod != null)
if (!_parsingConfig.DisableMemberAccessToIndexAccessorFallback)
{
return Expression.Call(instance, indexerMethod, Expression.Constant(id));
MethodInfo indexerMethod = instance.Type.GetMethod("get_Item", new[] { typeof(string) });
if (indexerMethod != null)
{
return Expression.Call(instance, indexerMethod, Expression.Constant(id));
}
}

if (_textParser.CurrentToken.Id == TokenId.Lambda && _it.Type == type)
Expand Down
7 changes: 7 additions & 0 deletions src/System.Linq.Dynamic.Core/ParsingConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -103,5 +103,12 @@ public IExpressionPromoter ExpressionPromoter
/// 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.
/// </summary>
public bool DisableMemberAccessToIndexAccessorFallback { get; set; } = false;
}
}
40 changes: 39 additions & 1 deletion test/System.Linq.Dynamic.Core.Tests/EntitiesTests.Select.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using System.Collections;
using System.Linq.Dynamic.Core.Exceptions;
using System.Linq.Dynamic.Core.Tests.Helpers.Entities;
using MongoDB.Bson;
#if EFCORE
using Microsoft.EntityFrameworkCore;
#else
Expand Down Expand Up @@ -48,6 +50,42 @@ public void Entities_Select_SingleColumn()
Assert.Equal<ICollection>(expected, test);
}

[Fact]
public void Entities_Select_EmptyObject()
{
ParsingConfig config = ParsingConfig.Default;
config.EvaluateGroupByAtDatabase = true;
Comment thread
StefH marked this conversation as resolved.

//Arrange
PopulateTestData(5, 0);

var expected = _context.Blogs.Select(x => new {}).ToList();

//Act
var test = _context.Blogs.GroupBy(config, "BlogId", "new()").Select<object>("new()").ToList();

//Assert
Assert.Equal(expected.ToJson(), test.ToJson());
}

[Fact]
public void Entities_Select_BrokenObject()
{
ParsingConfig config = ParsingConfig.Default;
config.DisableMemberAccessToIndexAccessorFallback = false;

// Silently creates something that will later fail on materialization
var test = _context.Blogs.Select(config, "new(~.BlogId)");
test = test.Select(config, "new(nonexistentproperty as howcanthiswork)");

// Will fail when creating the expression
config.DisableMemberAccessToIndexAccessorFallback = true;
Assert.ThrowsAny<ParseException>(() =>
Comment thread
StefH marked this conversation as resolved.
{
test = test.Select(config, "new(nonexistentproperty as howcanthiswork)");
});
}

[Fact]
public void Entities_Select_MultipleColumn()
{
Expand Down Expand Up @@ -108,4 +146,4 @@ public void Entities_Select_BlogAndPosts()
}
}
}
}
}