public EntityMetadataColumnSetupProvider(IEntityType entity, EntityState state, BulkOptions bulkOptions) { _bulkOptions = bulkOptions; SchemaName = entity.GetSchema(); TableName = entity.GetTableName(); var properties = entity.GetProperties(); if (state == EntityState.Deleted) { properties = properties.Where(p => p.IsPrimaryKey()); } if (_bulkOptions.ShadowPropertyAccessor == null) { properties = properties.Where(p => entity.FindDiscriminatorProperty() == p || !p.IsShadowProperty()); } var columns = properties.Select((p, i) => CreateColumnSetup(entity, p, i, state, _bulkOptions)).Where(p => p.ValueDirection != ValueDirection.None); if (!bulkOptions.PropagateValues) { columns = columns.Where(p => p.ValueDirection.HasFlag(ValueDirection.Write)); } _columns = columns.ToImmutableList(); }
/// <summary> /// Validates the discriminator and values for all entity types derived from the given one. /// </summary> /// <param name="rootEntityType">The entity type to validate.</param> protected virtual void ValidateDiscriminatorValues(IEntityType rootEntityType) { var derivedTypes = rootEntityType.GetDerivedTypesInclusive().ToList(); if (derivedTypes.Count == 1) { return; } var discriminatorProperty = rootEntityType.FindDiscriminatorProperty(); if (discriminatorProperty == null) { throw new InvalidOperationException( CoreStrings.NoDiscriminatorProperty(rootEntityType.DisplayName())); } var discriminatorValues = new Dictionary <object, IEntityType>(discriminatorProperty.GetKeyValueComparer()); foreach (var derivedType in derivedTypes) { if (!derivedType.ClrType.IsInstantiable()) { continue; } var discriminatorValue = derivedType.GetDiscriminatorValue(); if (discriminatorValue == null) { throw new InvalidOperationException( CoreStrings.NoDiscriminatorValue(derivedType.DisplayName())); } if (discriminatorValues.TryGetValue(discriminatorValue, out var duplicateEntityType)) { throw new InvalidOperationException( CoreStrings.DuplicateDiscriminatorValue( derivedType.DisplayName(), discriminatorValue, duplicateEntityType.DisplayName())); } discriminatorValues[discriminatorValue] = derivedType; } }
public static IProperty?GetDiscriminatorProperty(this IEntityType entityType) => entityType.FindDiscriminatorProperty();
/// <inheritdoc /> protected override LambdaExpression GenerateMaterializationCondition(IEntityType entityType, bool nullable) { LambdaExpression baseCondition; if (entityType.FindDiscriminatorProperty() == null && entityType.GetDirectlyDerivedTypes().Any()) { // TPT var valueBufferParameter = Parameter(typeof(ValueBuffer)); var discriminatorValueVariable = Variable(typeof(string), "discriminator"); var expressions = new List <Expression> { Assign( discriminatorValueVariable, valueBufferParameter.CreateValueBufferReadValueExpression(typeof(string), 0, null)) }; var derivedConcreteEntityTypes = entityType.GetDerivedTypes().Where(dt => !dt.IsAbstract()).ToArray(); var switchCases = new SwitchCase[derivedConcreteEntityTypes.Length]; for (var i = 0; i < derivedConcreteEntityTypes.Length; i++) { var discriminatorValue = Constant(derivedConcreteEntityTypes[i].ShortName(), typeof(string)); switchCases[i] = SwitchCase(Constant(derivedConcreteEntityTypes[i], typeof(IEntityType)), discriminatorValue); } var defaultBlock = entityType.IsAbstract() ? CreateUnableToDiscriminateExceptionExpression(entityType, discriminatorValueVariable) : Constant(entityType, typeof(IEntityType)); expressions.Add(Switch(discriminatorValueVariable, defaultBlock, switchCases)); baseCondition = Lambda(Block(new[] { discriminatorValueVariable }, expressions), valueBufferParameter); } else { baseCondition = base.GenerateMaterializationCondition(entityType, nullable); } if (entityType.FindPrimaryKey() != null) { var table = entityType.GetViewOrTableMappings().FirstOrDefault()?.Table; if (table != null && table.IsOptional(entityType)) { // Optional dependent var body = baseCondition.Body; var valueBufferParameter = baseCondition.Parameters[0]; Expression?condition = null; var requiredNonPkProperties = entityType.GetProperties().Where(p => !p.IsNullable && !p.IsPrimaryKey()).ToList(); if (requiredNonPkProperties.Count > 0) { condition = requiredNonPkProperties .Select( p => NotEqual( valueBufferParameter.CreateValueBufferReadValueExpression(typeof(object), p.GetIndex(), p), Constant(null))) .Aggregate((a, b) => AndAlso(a, b)); } var allNonPrincipalSharedNonPkProperties = entityType.GetNonPrincipalSharedNonPkProperties(table); // We don't need condition for nullable property if there exist at least one required property which is non shared. if (allNonPrincipalSharedNonPkProperties.Count != 0 && allNonPrincipalSharedNonPkProperties.All(p => p.IsNullable)) { var atLeastOneNonNullValueInNullablePropertyCondition = allNonPrincipalSharedNonPkProperties .Select( p => NotEqual( valueBufferParameter.CreateValueBufferReadValueExpression(typeof(object), p.GetIndex(), p), Constant(null))) .Aggregate((a, b) => OrElse(a, b)); condition = condition == null ? atLeastOneNonNullValueInNullablePropertyCondition : AndAlso(condition, atLeastOneNonNullValueInNullablePropertyCondition); } if (condition != null) { body = Condition(condition, body, Default(typeof(IEntityType))); } return(Lambda(body, valueBufferParameter)); } } return(baseCondition); }
/// <summary> /// Creates an expression of <see cref="Func{ValueBuffer, IEntityType}" /> to determine which entity type to materialize. /// </summary> /// <param name="entityType">The entity type to create materialization condition for.</param> /// <param name="nullable">Whether this entity instance can be null.</param> /// <returns>An expression of <see cref="Func{ValueBuffer, IEntityType}" /> representing materilization condition for the entity type.</returns> protected virtual LambdaExpression GenerateMaterializationCondition(IEntityType entityType, bool nullable) { Check.NotNull(entityType, nameof(EntityType)); var valueBufferParameter = Parameter(typeof(ValueBuffer)); Expression body; var concreteEntityTypes = entityType.GetConcreteDerivedTypesInclusive().ToArray(); var discriminatorProperty = entityType.FindDiscriminatorProperty(); if (discriminatorProperty != null) { var discriminatorValueVariable = Variable(discriminatorProperty.ClrType, "discriminator"); var expressions = new List <Expression> { Assign( discriminatorValueVariable, valueBufferParameter.CreateValueBufferReadValueExpression( discriminatorProperty.ClrType, discriminatorProperty.GetIndex(), discriminatorProperty)) }; var exception = CreateUnableToDiscriminateExceptionExpression(entityType, discriminatorValueVariable); var discriminatorComparer = discriminatorProperty.GetKeyValueComparer(); if (discriminatorComparer.IsDefault()) { var switchCases = new SwitchCase[concreteEntityTypes.Length]; for (var i = 0; i < concreteEntityTypes.Length; i++) { var discriminatorValue = Constant(concreteEntityTypes[i].GetDiscriminatorValue(), discriminatorProperty.ClrType); switchCases[i] = SwitchCase(Constant(concreteEntityTypes[i], typeof(IEntityType)), discriminatorValue); } expressions.Add(Switch(discriminatorValueVariable, exception, switchCases)); } else { Expression conditions = exception; for (var i = concreteEntityTypes.Length - 1; i >= 0; i--) { conditions = Condition( discriminatorComparer.ExtractEqualsBody( discriminatorValueVariable, Constant( concreteEntityTypes[i].GetDiscriminatorValue(), discriminatorProperty.ClrType)), Constant(concreteEntityTypes[i], typeof(IEntityType)), conditions); } expressions.Add(conditions); } body = Block(new[] { discriminatorValueVariable }, expressions); } else { body = Constant(concreteEntityTypes.Length == 1 ? concreteEntityTypes[0] : entityType, typeof(IEntityType)); } if (entityType.FindPrimaryKey() == null && nullable) { body = Condition( entityType.GetProperties() .Select( p => NotEqual( valueBufferParameter.CreateValueBufferReadValueExpression(typeof(object), p.GetIndex(), p), Constant(null))) .Aggregate((a, b) => OrElse(a, b)), body, Default(typeof(IEntityType))); } return(Lambda(body, valueBufferParameter)); }
private IColumnSetup CreateColumnSetup(IEntityType entity, IProperty property, int index, EntityState state, BulkOptions bulkOptions) { var storeIdentifier = StoreObjectIdentifier.Create(entity, StoreObjectType.Table); var direction = GetValueDirection(property, state); if (property.IsPrimaryKey() && _bulkOptions.IdentityInsert) { direction = ValueDirection.Write; } if (!bulkOptions.PropagateValues) { direction = direction & ~ValueDirection.Read; } if (entity.FindDiscriminatorProperty() == property) { var discriminatorValue = entity.GetDiscriminatorValue(); return(new DelegateColumnSetup(index, property.GetColumnName(storeIdentifier.Value), property.ClrType, p => discriminatorValue, (p, q) => { }, ValueDirection.Write)); } Expression <Func <object, object> > getValue = null; Expression <Action <object, object> > setValue = null; var valueConverter = property.GetValueConverter(); if (property.IsShadowProperty()) { var accessorType = typeof(IShadowPropertyAccessor); var param = Expression.Parameter(typeof(object), "p"); var param2 = Expression.Parameter(typeof(object), "q"); Expression getValueBody = Expression.Convert(Expression.Call( Expression.Constant(bulkOptions.ShadowPropertyAccessor, accessorType), accessorType.GetRuntimeMethod("GetValue", new[] { typeof(object), typeof(string) }), param, Expression.Constant(property.Name)), property.ClrType); Expression setValueBody = param2; if (!bulkOptions.IgnoreDefaultValues) { getValueBody = ProcessDefaultValue(getValueBody, property); } if (valueConverter != null) { getValueBody = valueConverter.ConvertToProviderExpression.Body.Replace(valueConverter.ConvertToProviderExpression.Parameters[0], getValueBody); setValueBody = Expression.Convert(valueConverter.ConvertFromProviderExpression.Body.Replace(valueConverter.ConvertFromProviderExpression.Parameters[0], Expression.Convert(setValueBody, valueConverter.ProviderClrType)), typeof(object)); } getValue = Expression.Lambda <Func <object, object> >( Expression.Convert(getValueBody, typeof(object)), param); setValue = Expression.Lambda <Action <object, object> >( Expression.Call( Expression.Constant(bulkOptions.ShadowPropertyAccessor, accessorType), accessorType.GetRuntimeMethod("StoreValue", new[] { typeof(object), typeof(string), typeof(object) }), param, Expression.Constant(property.Name), setValueBody), param, param2); } else { var param = Expression.Parameter(typeof(object), "p"); var param2 = Expression.Parameter(typeof(object), "q"); var cast = Expression.Convert(param, property.DeclaringEntityType.ClrType); Expression getValueBody = Expression.Property(cast, property.PropertyInfo); Expression setValueBody = Expression.Convert(param2, property.ClrType); if (!bulkOptions.IgnoreDefaultValues) { getValueBody = ProcessDefaultValue(getValueBody, property); } if (valueConverter != null) { getValueBody = valueConverter.ConvertToProviderExpression.Body.Replace(valueConverter.ConvertToProviderExpression.Parameters[0], getValueBody); setValueBody = valueConverter.ConvertFromProviderExpression.Body.Replace(valueConverter.ConvertFromProviderExpression.Parameters[0], Expression.Convert(param2, valueConverter.ProviderClrType)); } getValue = Expression.Lambda <Func <object, object> >(Expression.Convert(getValueBody, typeof(object)), param); setValue = Expression.Lambda <Action <object, object> >(Expression.Assign(Expression.Property(cast, property.PropertyInfo), setValueBody), param, param2); } var targetType = property.ClrType; if (valueConverter != null) { targetType = valueConverter.ProviderClrType; } return(new DelegateColumnSetup(index, property.GetColumnName(storeIdentifier.Value), targetType, getValue.Compile(), setValue.Compile(), direction)); }