Skip to content

Commit

Permalink
issue koenbeuk#71
Browse files Browse the repository at this point in the history
  • Loading branch information
Алексей Пыжов committed Apr 3, 2024
1 parent f0c2359 commit 22e3ec0
Show file tree
Hide file tree
Showing 7 changed files with 159 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ public ProjectableAttribute(string useMemberBody, object memberBodyParameterValu
{
UseMemberBody = useMemberBody;
MemberBodyParameterValues = new[] { memberBodyParameterValue };

}
public ProjectableAttribute(string useMemberBody, string memberBodyParameterValue) : this(useMemberBody, (object)memberBodyParameterValue) { }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -209,19 +209,30 @@ PropertyInfo property when nodeExpression is not null
if (nodeExpression is not null)
{
_expressionArgumentReplacer.ParameterArgumentMapping.Add(reflectedExpression.Parameters[0], nodeExpression);
if (reflectedExpression.Parameters.Count > 1)
{
var projectableAttribute = nodeMember.GetCustomAttribute<ProjectableAttribute>(false)!;
foreach (var prm in reflectedExpression.Parameters.Skip(1).Select((Parameter, Index) => new { Parameter, Index }))
{
var value = projectableAttribute!.MemberBodyParameterValues![prm.Index];
_expressionArgumentReplacer.ParameterArgumentMapping.Add(prm.Parameter, Expression.Constant(value));
}
}

var updatedBody = _expressionArgumentReplacer.Visit(reflectedExpression.Body);
_expressionArgumentReplacer.ParameterArgumentMapping.Clear();

return base.Visit(
return Visit(
updatedBody
);
}
else
{
return base.Visit(
return Visit(
reflectedExpression.Body
);
}

}

return base.VisitMember(node);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,10 @@ public LambdaExpression FindGeneratedExpression(MemberInfo projectableMemberInfo
{
expression = GetExpressionFromGeneratedType(projectableMemberInfo, true, projectableAttribute.UseMemberBody);
}

if (expression is null && projectableAttribute.UseMemberBody is not null)
{
expression = GetExpressionFromMemberBody(projectableMemberInfo, projectableAttribute.UseMemberBody, projectableAttribute.MemberBodyParameterValues);
}

if (expression is null)
{
var declaringType = projectableMemberInfo.DeclaringType ?? throw new InvalidOperationException("Expected a valid type here");
Expand All @@ -41,13 +39,14 @@ public LambdaExpression FindGeneratedExpression(MemberInfo projectableMemberInfo
static LambdaExpression? GetExpressionFromMemberBody(MemberInfo projectableMemberInfo, string memberName, object[]? memberParameters)
{
var declaringType = projectableMemberInfo.DeclaringType ?? throw new InvalidOperationException("Expected a valid type here");

var exprProperty = declaringType.GetProperty(memberName, BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
?? declaringType.BaseType?.GetProperty(memberName, BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
var lambda = exprProperty?.GetValue(null) as LambdaExpression;

if (lambda is not null)
{

if (projectableMemberInfo is PropertyInfo property && (lambda.Parameters.Count ==
(1 + (memberParameters?.Length ?? 0)))
&& (lambda.Parameters[0].Type == declaringType ||
Expand All @@ -74,15 +73,13 @@ public LambdaExpression FindGeneratedExpression(MemberInfo projectableMemberInfo
return lambda;
}
}

return null;
}

static LambdaExpression? GetExpressionFromGeneratedType(MemberInfo projectableMemberInfo, bool useLocalType = false, string methodName = "Expression")
{
var declaringType = projectableMemberInfo.DeclaringType ?? throw new InvalidOperationException("Expected a valid type here");
var generatedContainingTypeName = ProjectionExpressionClassNameGenerator.GenerateFullName(declaringType.Namespace, declaringType.GetNestedTypePath().Select(x => x.Name), projectableMemberInfo.Name);

var expressionFactoryType = !useLocalType
? declaringType.Assembly.GetType(generatedContainingTypeName)
: declaringType;
Expand Down Expand Up @@ -111,7 +108,6 @@ public LambdaExpression FindGeneratedExpression(MemberInfo projectableMemberInfo
return expressionFactoryMethod.Invoke(null, null) as LambdaExpression ?? throw new InvalidOperationException("Expected lambda");
}
}

return null;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="7.0.17" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="7.0.12" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
<PackageReference Include="ScenarioTests.XUnit" Version="1.0.0" />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq;
using System.Threading.Tasks;
using EntityFrameworkCore.Projectables.FunctionalTests.Helpers;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Scaffolding.Metadata;
using ScenarioTests;
using VerifyXunit;
using Xunit;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
using System.Collections.Generic;
using EntityFrameworkCore.Projectables.Infrastructure;
using Microsoft.EntityFrameworkCore;
using Order = EntityFrameworkCore.Projectables.FunctionalTests.MemberBodyParameterValueTests.Order;
using OrderItem = EntityFrameworkCore.Projectables.FunctionalTests.MemberBodyParameterValueTests.OrderItem;

namespace EntityFrameworkCore.Projectables.FunctionalTests.Helpers
{
public class SampleBodyParamDbContext : DbContext
{
readonly CompatibilityMode _compatibilityMode;

public SampleBodyParamDbContext(CompatibilityMode compatibilityMode = CompatibilityMode.Full)
{
_compatibilityMode = compatibilityMode;

var _orders = new List<Order>() {
new() {
Id = 1,

},
new() {
Id = 2,

}
};

var _orders_items = new List<OrderItem>() {
new() {
Id = 1,
OrderId = 1,
Name = "Order_1"
},
new() {
Id = 2,
OrderId = 1,
Name = "Order_2"
},
new() {
Id = 3,
OrderId = 2,
Name = "Order_3"
},
new() {
Id = 4,
OrderId = 2,
Name = "Order_4"
},
};

Order!.AddRange(_orders);
OrderItem!.AddRange(_orders_items);
SaveChanges();
}

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseInMemoryDatabase("TestDb");
optionsBuilder.UseProjectables(options => {
options.CompatibilityMode(_compatibilityMode); // Needed by our ComplexModelTests
});
}

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Order>()
.HasKey(x => x.Id);
modelBuilder.Entity<OrderItem>().HasKey(x => x.Id);

}

public DbSet<Order>? Order { get; set; }
public DbSet<OrderItem>? OrderItem { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Linq.Expressions;
using EntityFrameworkCore.Projectables.FunctionalTests.Helpers;
using Microsoft.EntityFrameworkCore;
using Xunit;

namespace EntityFrameworkCore.Projectables.FunctionalTests
{
public class MemberBodyParameterValueTests
{
public class Order
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.None)]
public int Id { get; set; }
public List<OrderItem> OrderItem { get; set; } = new List<OrderItem>();

[Projectable(nameof(GetOrderItemNameExpression), 1)]
public string FirstOrderPropName => GetOrderItemName(1);


[Projectable(UseMemberBody = nameof(GetOrderItemNameInnerExpression))]
public string GetOrderItemName(int id)
=> OrderItem.Where(av => av.Id == id)
.Select(av => av.Name)
.FirstOrDefault() ?? throw new ArgumentException("Argument matching identifier not found");

private static Expression<Func<Order, int, string>> GetOrderItemNameInnerExpression()
=> (@this, id) => @this.OrderItem
.Where(av => av.Id == id)
.Select(av => av.Name)
.FirstOrDefault() ?? string.Empty;

public static Expression<Func<Order, int, string>> GetOrderItemNameExpression
=> (order, id) => order.GetOrderItemName(id);
}

public class OrderItem
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.None)]
public int Id { get; set; }
public int OrderId { get; set; }
public string Name { get; set; } = string.Empty;
}

[Fact]
public void UseBodyParameterValue()
{
//Arrange
using var dbContext = new SampleBodyParamDbContext();

// Act
var query = dbContext
.Set<Order>()
.Include(a => a.OrderItem)
.FirstOrDefault(d => d.FirstOrderPropName == "Order_1");

// Assert
Assert.NotNull(query);
Assert.True(query!.FirstOrderPropName == "Order_1");
}
}
}

0 comments on commit 22e3ec0

Please sign in to comment.