Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue215 #228

Merged
merged 5 commits into from
Nov 25, 2018
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;

//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>(() =>
{
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()
}
}
}
}
}