Skip to content
177 changes: 107 additions & 70 deletions Orm/Xtensive.Orm/Orm/Linq/Expressions/EntityExpression.cs
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
// Copyright (C) 2003-2010 Xtensive LLC.
// All rights reserved.
// For conditions of distribution and use, see license.
// Copyright (C) 2009-2020 Xtensive LLC.
// This code is distributed under MIT license terms.
// See the License.txt file in the project root for more information.
// Created by: Alexis Kochetov
// Created: 2009.05.05

using System;
using System.Diagnostics;
using System.Linq;
using System.Collections.Generic;
using System.Linq.Expressions;
using Xtensive.Core;
using Xtensive.Orm.Model;

namespace Xtensive.Orm.Linq.Expressions
Expand All @@ -19,94 +17,115 @@ internal class EntityExpression : ParameterizedExpression,
{
private List<PersistentFieldExpression> fields;

public TypeInfo PersistentType { get; private set; }
public TypeInfo PersistentType { get; }

public KeyExpression Key { get; private set; }
public KeyExpression Key { get; }

public List<PersistentFieldExpression> Fields
{
get { return fields; }
private set
{
get => fields;
private set {
fields = value;
var fieldExpressions = fields.OfType<FieldExpression>();
foreach (var fieldExpression in fieldExpressions)
foreach (var fieldExpression in fields.OfType<FieldExpression>()) {
fieldExpression.Owner = this;
}
}
}

public bool IsNullable { get; set; }

public Expression Remap(int offset, Dictionary<Expression, Expression> processedExpressions)
{
if (!CanRemap)
if (!CanRemap) {
return this;
Expression value;
if (processedExpressions.TryGetValue(this, out value))
}

if (processedExpressions.TryGetValue(this, out var value)) {
return value;
}

var keyExpression = (KeyExpression) Key.Remap(offset, processedExpressions);
var result = new EntityExpression(PersistentType, keyExpression, OuterParameter, DefaultIfEmpty);
processedExpressions.Add(this, result);
result.IsNullable = IsNullable;
result.Fields = Fields
.Select(f => f.Remap(offset, processedExpressions))
.Cast<PersistentFieldExpression>()
.ToList();
var processedFields = new List<PersistentFieldExpression>(fields.Count);
foreach (var field in fields) {
// Do not convert to LINQ. We want to avoid a closure creation here.
processedFields.Add((PersistentFieldExpression) field.Remap(offset, processedExpressions));
}

result.Fields = processedFields;
return result;
}

public Expression Remap(int[] map, Dictionary<Expression, Expression> processedExpressions)
{
if (!CanRemap)
if (!CanRemap) {
return this;
Expression value;
if (processedExpressions.TryGetValue(this, out value))
}

if (processedExpressions.TryGetValue(this, out var value)) {
return value;
}

var keyExpression = (KeyExpression) Key.Remap(map, processedExpressions);
if (keyExpression==null)
if (keyExpression == null) {
return null;
}

var result = new EntityExpression(PersistentType, keyExpression, OuterParameter, DefaultIfEmpty);
processedExpressions.Add(this, result);
result.IsNullable = IsNullable;
result.Fields = Fields
.Select(f => f.Remap(map, processedExpressions))
.Where(f => f!=null)
.Cast<PersistentFieldExpression>()
.ToList();
var processedFields = new List<PersistentFieldExpression>(fields.Count);
foreach (var field in fields) {
// Do not convert to LINQ. We want to avoid a closure creation here.
var mappedField = (PersistentFieldExpression) field.Remap(map, processedExpressions);
if (mappedField == null) {
continue;
}

processedFields.Add(mappedField);
}

result.Fields = processedFields;
return result;
}

public Expression BindParameter(ParameterExpression parameter, Dictionary<Expression, Expression> processedExpressions)
{
Expression value;
if (processedExpressions.TryGetValue(this, out value))
if (processedExpressions.TryGetValue(this, out var value)) {
return value;
}

var keyExpression = (KeyExpression) Key.BindParameter(parameter, processedExpressions);
var result = new EntityExpression(PersistentType, keyExpression, parameter, DefaultIfEmpty);
processedExpressions.Add(this, result);
result.Fields = Fields
.Select(f => f.BindParameter(parameter, processedExpressions))
.Cast<PersistentFieldExpression>()
.ToList();
var processedFields = new List<PersistentFieldExpression>(fields.Count);
foreach (var field in fields) {
// Do not convert to LINQ. We want to avoid a closure creation here.
processedFields.Add((PersistentFieldExpression) field.BindParameter(parameter, processedExpressions));
}

result.Fields = processedFields;
return result;
}

public Expression RemoveOuterParameter(Dictionary<Expression, Expression> processedExpressions)
{
Expression value;
if (processedExpressions.TryGetValue(this, out value))
if (processedExpressions.TryGetValue(this, out var value)) {
return value;
}

var keyExpression = (KeyExpression) Key.RemoveOuterParameter(processedExpressions);
var result = new EntityExpression(PersistentType, keyExpression, null, DefaultIfEmpty);
processedExpressions.Add(this, result);
result.Fields = Fields
.Select(f => f.RemoveOuterParameter(processedExpressions))
.Cast<PersistentFieldExpression>()
.ToList();
var processedFields = new List<PersistentFieldExpression>(fields.Count);
foreach (var field in fields) {
// Do not convert to LINQ. We want to avoid a closure creation here.
processedFields.Add((PersistentFieldExpression) field.RemoveOuterParameter(processedExpressions));
}

result.Fields = processedFields;
return result;
}

Expand All @@ -118,70 +137,88 @@ public static void Fill(EntityExpression entityExpression, int offset)
var typeInfo = entityExpression.PersistentType;
foreach (var nestedField in typeInfo.Fields.Except(entityExpression.Fields.OfType<FieldExpression>().Select(field=>field.Field))) {
var nestedFieldExpression = BuildNestedFieldExpression(nestedField, offset);
var fieldExpression = nestedFieldExpression as FieldExpression;
if (fieldExpression!=null)
if (nestedFieldExpression is FieldExpression fieldExpression) {
fieldExpression.Owner = entityExpression;
}

entityExpression.fields.Add(nestedFieldExpression);
}
}

public static EntityExpression Create(TypeInfo typeInfo, int offset, bool keyFieldsOnly)
{
if (!typeInfo.IsEntity && !typeInfo.IsInterface)
throw new ArgumentException(string.Format(Strings.ExPersistentTypeXIsNotEntityOrPersistentInterface, typeInfo.Name), "typeInfo");
var fields = new List<PersistentFieldExpression>();
if (!typeInfo.IsEntity && !typeInfo.IsInterface) {
throw new ArgumentException(
string.Format(Strings.ExPersistentTypeXIsNotEntityOrPersistentInterface, typeInfo.Name), nameof(typeInfo));
}

var keyExpression = KeyExpression.Create(typeInfo, offset);
fields.Add(keyExpression);

List<PersistentFieldExpression> fields;
var result = new EntityExpression(typeInfo, keyExpression, null, false);
if (keyFieldsOnly) {
fields = new List<PersistentFieldExpression>(keyExpression.KeyFields.Count + 1) {keyExpression};
// Add key fields to field collection
var keyFieldClones = keyExpression
.KeyFields
.Select(kf=>FieldExpression.CreateField(kf.Field, offset))
.Cast<PersistentFieldExpression>();
fields.AddRange(keyFieldClones);
}
else
foreach (var nestedField in typeInfo.Fields)
foreach (var keyField in keyExpression.KeyFields) {
// Do not convert to LINQ. We want to avoid a closure creation here.
fields.Add(FieldExpression.CreateField(keyField.Field, offset));
}
}
else {
fields = new List<PersistentFieldExpression>(typeInfo.Fields.Count + 1) {keyExpression};
foreach (var nestedField in typeInfo.Fields) {
// Do not convert to LINQ. We want to avoid a closure creation here.
fields.Add(BuildNestedFieldExpression(nestedField, offset));
}
}

result.Fields = fields;
return result;
}

public static EntityExpression Create(EntityFieldExpression entityFieldExpression, int offset)
{
var typeInfo = entityFieldExpression.PersistentType;
var fields = new List<PersistentFieldExpression>();
var keyExpression = KeyExpression.Create(typeInfo, offset);
fields.Add(keyExpression);
foreach (var nestedField in typeInfo.Fields)
fields.Add(BuildNestedFieldExpression(nestedField, offset));
var fields = new List<PersistentFieldExpression>(typeInfo.Fields.Count + 1) {keyExpression};
foreach (var nestedField in typeInfo.Fields) {
// Do not convert to LINQ. We want to avoid a closure creation here.
fields.Add(BuildNestedFieldExpression(nestedField, offset));
}

var result = new EntityExpression(typeInfo, keyExpression, null, entityFieldExpression.DefaultIfEmpty) {
Fields = fields
};
if (entityFieldExpression.OuterParameter==null)
if (entityFieldExpression.OuterParameter == null) {
return result;
return (EntityExpression) result.BindParameter(entityFieldExpression.OuterParameter, new Dictionary<Expression, Expression>());
}

return (EntityExpression) result.BindParameter(
entityFieldExpression.OuterParameter, new Dictionary<Expression, Expression>());
}

private static PersistentFieldExpression BuildNestedFieldExpression(FieldInfo nestedField, int offset)
{
if (nestedField.IsPrimitive)
if (nestedField.IsPrimitive) {
return FieldExpression.CreateField(nestedField, offset);
if (nestedField.IsStructure)
}

if (nestedField.IsStructure) {
return StructureFieldExpression.CreateStructure(nestedField, offset);
if (nestedField.IsEntity)
}

if (nestedField.IsEntity) {
return EntityFieldExpression.CreateEntityField(nestedField, offset);
if (nestedField.IsEntitySet)
return EntitySetExpression.CreateEntitySet(nestedField);
throw new NotSupportedException(string.Format(Strings.ExNestedFieldXIsNotSupported, nestedField.Attributes));
}
}

public override string ToString()
{
return string.Format("{0} {1}", base.ToString(), PersistentType.Name);
if (nestedField.IsEntitySet) {
return EntitySetExpression.CreateEntitySet(nestedField);
}

throw new NotSupportedException(string.Format(Strings.ExNestedFieldXIsNotSupported, nestedField.Attributes));
}

public override string ToString() => $"{base.ToString()} {PersistentType.Name}";

// Constructors

Expand Down
Loading