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
Original file line number Diff line number Diff line change
Expand Up @@ -1521,6 +1521,39 @@ protected override Expression VisitSwitch(SwitchExpression switchExpression)
//sometimes we have shadow value buffer and sometimes not, but type initializer always comes last
switch (body.Expressions[^1])
{
case UnaryExpression { Operand: BlockExpression innerBlock } jsonEntityTypeInitializerUnary
when jsonEntityTypeInitializerUnary.NodeType is ExpressionType.Convert or ExpressionType.ConvertChecked:
{
// in case of proxies, the entity initializer block is wrapped around Convert node
// that converts from the proxy type to the actual entity type.
// We normalize that into a block by pushing the convert inside the inner block. Rather than:
//
// return (MyEntity)
// {
// ProxyEntity instance;
// (...)
// return instance;
// }
//
// we produce:
// return
// {
// ProxyEntity instance;
// MyEntity actualInstance;
// (...)
// actualInstance = (MyEntity)instance;
// return actualInstance;
// }
var newVariables = innerBlock.Variables.ToList();
var proxyConversionVariable = Variable(jsonEntityTypeInitializerUnary.Type);
newVariables.Add(proxyConversionVariable);
var newExpressions = innerBlock.Expressions.ToList()[..^1];
newExpressions.Add(Assign(proxyConversionVariable, jsonEntityTypeInitializerUnary.Update(innerBlock.Expressions[^1])));
newExpressions.Add(proxyConversionVariable);
jsonEntityTypeInitializerBlock = Block(newVariables, newExpressions);
break;
}

case BlockExpression b:
jsonEntityTypeInitializerBlock = b;
break;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -704,6 +704,112 @@ public MyJsonEntityShadowPropertiesWithCtor(string name)

#endregion

#region LazyLoadingProxies

[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual async Task Project_proxies_entity_with_json(bool async)
{
var contextFactory = await InitializeAsync<MyContextLazyLoadingProxies>(
seed: SeedLazyLoadingProxies,
onConfiguring: OnConfiguringLazyLoadingProxies,
addServices: AddServicesLazyLoadingProxies);

using (var context = contextFactory.CreateContext())
{
var query = context.Entities;

var result = async
? await query.ToListAsync()
: query.ToList();

Assert.Equal(2, result.Count);
}
}

protected void OnConfiguringLazyLoadingProxies(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder.UseLazyLoadingProxies();

protected void AddServicesLazyLoadingProxies(IServiceCollection addServices)
=> addServices.AddEntityFrameworkProxies();

private void SeedLazyLoadingProxies(MyContextLazyLoadingProxies ctx)
{
var r1 = new MyJsonEntityLazyLoadingProxiesWithCtor("r1", 1);
var c11 = new MyJsonEntityLazyLoadingProxies { Name = "c11", Number = 11 };
var c12 = new MyJsonEntityLazyLoadingProxies { Name = "c12", Number = 12 };
var c13 = new MyJsonEntityLazyLoadingProxies { Name = "c13", Number = 13 };

var r2 = new MyJsonEntityLazyLoadingProxiesWithCtor("r2", 2);
var c21 = new MyJsonEntityLazyLoadingProxies { Name = "c21", Number = 21 };
var c22 = new MyJsonEntityLazyLoadingProxies { Name = "c22", Number = 22 };

var e1 = new MyEntityLazyLoadingProxies
{
Id = 1,
Name = "e1",
Reference = r1,
Collection = new List<MyJsonEntityLazyLoadingProxies> { c11, c12, c13 }
};

var e2 = new MyEntityLazyLoadingProxies
{
Id = 2,
Name = "e2",
Reference = r2,
Collection = new List<MyJsonEntityLazyLoadingProxies> { c21, c22 }
};

ctx.Entities.AddRange(e1, e2);
ctx.SaveChanges();
}

protected class MyContextLazyLoadingProxies : DbContext
{
public MyContextLazyLoadingProxies(DbContextOptions options)
: base(options)
{
}

public DbSet<MyEntityLazyLoadingProxies> Entities { get; set; }

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<MyEntityLazyLoadingProxies>().Property(x => x.Id).ValueGeneratedNever();
modelBuilder.Entity<MyEntityLazyLoadingProxies>().OwnsOne(x => x.Reference, b => b.ToJson());
modelBuilder.Entity<MyEntityLazyLoadingProxies>().OwnsMany(x => x.Collection, b => b.ToJson());
}
}

public class MyEntityLazyLoadingProxies
{
public int Id { get; set; }
public string Name { get; set; }

public virtual MyJsonEntityLazyLoadingProxiesWithCtor Reference { get; set; }
public virtual List<MyJsonEntityLazyLoadingProxies> Collection { get; set; }
}

public class MyJsonEntityLazyLoadingProxiesWithCtor
{
public MyJsonEntityLazyLoadingProxiesWithCtor(string name, int number)
{
Name = name;
Number = number;
}

public string Name { get; set; }
public int Number { get; set; }
}

public class MyJsonEntityLazyLoadingProxies
{
public string Name { get; set; }
public int Number { get; set; }
}

#endregion

protected TestSqlLoggerFactory TestSqlLoggerFactory
=> (TestSqlLoggerFactory)ListLoggerFactory;
}