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

Metadata: Configure backing field by attribute on skip navigation #21864

Merged
2 commits merged into from
Jul 31, 2020
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
16 changes: 15 additions & 1 deletion src/EFCore/Infrastructure/ModelValidator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ namespace Microsoft.EntityFrameworkCore.Infrastructure
/// </summary>
public class ModelValidator : IModelValidator
{
private static readonly IEnumerable<string> _dictionaryProperties =
typeof(IDictionary<string, object>).GetRuntimeProperties().Select(e => e.Name);

/// <summary>
/// Creates a new instance of <see cref="ModelValidator" />.
/// </summary>
Expand Down Expand Up @@ -169,13 +172,17 @@ protected virtual void ValidatePropertyMapping(

clrProperties.UnionWith(
runtimeProperties.Values
.Where(pi => pi.IsCandidateProperty())
.Where(pi => pi.IsCandidateProperty(needsWrite: false))
.Select(pi => pi.GetSimpleMemberName()));

clrProperties.ExceptWith(entityType.GetProperties().Select(p => p.Name));
clrProperties.ExceptWith(entityType.GetNavigations().Select(p => p.Name));
clrProperties.ExceptWith(entityType.GetSkipNavigations().Select(p => p.Name));
clrProperties.ExceptWith(entityType.GetServiceProperties().Select(p => p.Name));
if (entityType.IsPropertyBag)
{
clrProperties.ExceptWith(_dictionaryProperties);
}
clrProperties.RemoveWhere(p => entityType.FindIgnoredConfigurationSource(p) != null);

if (clrProperties.Count <= 0)
Expand Down Expand Up @@ -205,6 +212,13 @@ protected virtual void ValidatePropertyMapping(

var targetType = FindCandidateNavigationPropertyType(actualProperty);

if ((targetType == null // Not a navigation
|| targetSequenceType == null) // Not a collection navigation
&& actualProperty.FindSetterProperty() == null) // No setter property
{
continue;
}

var isTargetWeakOrOwned
= targetType != null
&& (conventionModel.HasEntityTypeWithDefiningNavigation(targetType)
Expand Down
35 changes: 4 additions & 31 deletions src/EFCore/Metadata/Builders/IConventionNavigationBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,22 +15,13 @@ namespace Microsoft.EntityFrameworkCore.Metadata.Builders
/// not used in application code.
/// </para>
/// </summary>
public interface IConventionNavigationBuilder : IConventionAnnotatableBuilder
public interface IConventionNavigationBuilder : IConventionPropertyBaseBuilder
{
/// <summary>
/// Gets the navigation being configured.
/// </summary>
new IConventionNavigation Metadata { get; }

/// <summary>
/// Returns a value indicating whether the backing field can be set for this navigation
/// from the given configuration source.
/// </summary>
/// <param name="fieldName"> The field name. </param>
/// <param name="fromDataAnnotation"> Indicates whether the configuration was specified using a data annotation. </param>
/// <returns> <see langword="true" /> if the backing field can be set for this property. </returns>
bool CanSetField([CanBeNull] string fieldName, bool fromDataAnnotation = false);

/// <summary>
/// Sets the backing field to use for this navigation.
/// </summary>
Expand All @@ -40,16 +31,7 @@ public interface IConventionNavigationBuilder : IConventionAnnotatableBuilder
/// The same builder instance if the configuration was applied,
/// <see langword="null" /> otherwise.
/// </returns>
IConventionNavigationBuilder HasField([CanBeNull] string fieldName, bool fromDataAnnotation = false);

/// <summary>
/// Returns a value indicating whether the backing field can be set for this navigation
/// from the given configuration source.
/// </summary>
/// <param name="fieldInfo"> The field. </param>
/// <param name="fromDataAnnotation"> Indicates whether the configuration was specified using a data annotation. </param>
/// <returns> <see langword="true" /> if the backing field can be set for this property. </returns>
bool CanSetField([CanBeNull] FieldInfo fieldInfo, bool fromDataAnnotation = false);
new IConventionNavigationBuilder HasField([CanBeNull] string fieldName, bool fromDataAnnotation = false);

/// <summary>
/// Sets the backing field to use for this navigation.
Expand All @@ -60,16 +42,7 @@ public interface IConventionNavigationBuilder : IConventionAnnotatableBuilder
/// The same builder instance if the configuration was applied,
/// <see langword="null" /> otherwise.
/// </returns>
IConventionNavigationBuilder HasField([CanBeNull] FieldInfo fieldInfo, bool fromDataAnnotation = false);

/// <summary>
/// Returns a value indicating whether the <see cref="PropertyAccessMode" /> can be set for this navigation
/// from the current configuration source.
/// </summary>
/// <param name="propertyAccessMode"> The <see cref="PropertyAccessMode" /> to use for this navigation. </param>
/// <param name="fromDataAnnotation"> Indicates whether the configuration was specified using a data annotation. </param>
/// <returns> <see langword="true" /> if the <see cref="PropertyAccessMode" /> can be set for this navigation. </returns>
bool CanSetPropertyAccessMode(PropertyAccessMode? propertyAccessMode, bool fromDataAnnotation = false);
new IConventionNavigationBuilder HasField([CanBeNull] FieldInfo fieldInfo, bool fromDataAnnotation = false);

/// <summary>
/// Sets the <see cref="PropertyAccessMode" /> to use for this navigation.
Expand All @@ -80,7 +53,7 @@ public interface IConventionNavigationBuilder : IConventionAnnotatableBuilder
/// The same builder instance if the configuration was applied,
/// <see langword="null" /> otherwise.
/// </returns>
IConventionNavigationBuilder UsePropertyAccessMode(PropertyAccessMode? propertyAccessMode, bool fromDataAnnotation = false);
new IConventionNavigationBuilder UsePropertyAccessMode(PropertyAccessMode? propertyAccessMode, bool fromDataAnnotation = false);

/// <summary>
/// Returns a value indicating whether this navigation can be configured to be automatically included in a query
Expand Down
85 changes: 85 additions & 0 deletions src/EFCore/Metadata/Builders/IConventionPropertyBaseBuilder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System.Reflection;
using JetBrains.Annotations;

namespace Microsoft.EntityFrameworkCore.Metadata.Builders
{
/// <summary>
/// <para>
/// Provides a simple API surface for configuring an <see cref="IConventionPropertyBase" /> from conventions.
/// </para>
/// <para>
/// This interface is typically used by database providers (and other extensions). It is generally
/// not used in application code.
/// </para>
/// </summary>
public interface IConventionPropertyBaseBuilder : IConventionAnnotatableBuilder
{
/// <summary>
/// Gets the property-like object being configured.
/// </summary>
new IConventionPropertyBase Metadata { get; }

/// <summary>
/// Sets the backing field to use for this property-like object.
/// </summary>
/// <param name="fieldName"> The field name. </param>
/// <param name="fromDataAnnotation"> Indicates whether the configuration was specified using a data annotation. </param>
/// <returns>
/// The same builder instance if the configuration was applied,
/// <see langword="null" /> otherwise.
/// </returns>
IConventionPropertyBaseBuilder HasField([CanBeNull] string fieldName, bool fromDataAnnotation = false);

/// <summary>
/// Sets the backing field to use for this property-like object.
/// </summary>
/// <param name="fieldInfo"> The field. </param>
/// <param name="fromDataAnnotation"> Indicates whether the configuration was specified using a data annotation. </param>
/// <returns>
/// The same builder instance if the configuration was applied,
/// <see langword="null" /> otherwise.
/// </returns>
IConventionPropertyBaseBuilder HasField([CanBeNull] FieldInfo fieldInfo, bool fromDataAnnotation = false);

/// <summary>
/// Returns a value indicating whether the backing field can be set for this property-like object
/// from the current configuration source.
/// </summary>
/// <param name="fieldName"> The field name. </param>
/// <param name="fromDataAnnotation"> Indicates whether the configuration was specified using a data annotation. </param>
/// <returns> <see langword="true" /> if the backing field can be set for this property-like object. </returns>
bool CanSetField([CanBeNull] string fieldName, bool fromDataAnnotation = false);

/// <summary>
/// Returns a value indicating whether the backing field can be set for this property-like object
/// from the current configuration source.
/// </summary>
/// <param name="fieldInfo"> The field. </param>
/// <param name="fromDataAnnotation"> Indicates whether the configuration was specified using a data annotation. </param>
/// <returns> <see langword="true" /> if the backing field can be set for this property-like object. </returns>
bool CanSetField([CanBeNull] FieldInfo fieldInfo, bool fromDataAnnotation = false);

/// <summary>
/// Sets the <see cref="PropertyAccessMode" /> to use for this property-like object.
/// </summary>
/// <param name="propertyAccessMode"> The <see cref="PropertyAccessMode" /> to use for this property-like object. </param>
/// <param name="fromDataAnnotation"> Indicates whether the configuration was specified using a data annotation. </param>
/// <returns>
/// The same builder instance if the configuration was applied,
/// <see langword="null" /> otherwise.
/// </returns>
IConventionPropertyBaseBuilder UsePropertyAccessMode(PropertyAccessMode? propertyAccessMode, bool fromDataAnnotation = false);

/// <summary>
/// Returns a value indicating whether the <see cref="PropertyAccessMode" /> can be set for this property-like object
/// from the current configuration source.
/// </summary>
/// <param name="propertyAccessMode"> The <see cref="PropertyAccessMode" /> to use for this property-like object. </param>
/// <param name="fromDataAnnotation"> Indicates whether the configuration was specified using a data annotation. </param>
/// <returns> <see langword="true" /> if the <see cref="PropertyAccessMode" /> can be set for this property-like object. </returns>
bool CanSetPropertyAccessMode(PropertyAccessMode? propertyAccessMode, bool fromDataAnnotation = false);
}
}
35 changes: 4 additions & 31 deletions src/EFCore/Metadata/Builders/IConventionPropertyBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ namespace Microsoft.EntityFrameworkCore.Metadata.Builders
/// not used in application code.
/// </para>
/// </summary>
public interface IConventionPropertyBuilder : IConventionAnnotatableBuilder
public interface IConventionPropertyBuilder : IConventionPropertyBaseBuilder
{
/// <summary>
/// Gets the property being configured.
Expand Down Expand Up @@ -117,7 +117,7 @@ public interface IConventionPropertyBuilder : IConventionAnnotatableBuilder
/// The same builder instance if the configuration was applied,
/// <see langword="null" /> otherwise.
/// </returns>
IConventionPropertyBuilder HasField([CanBeNull] string fieldName, bool fromDataAnnotation = false);
new IConventionPropertyBuilder HasField([CanBeNull] string fieldName, bool fromDataAnnotation = false);

/// <summary>
/// Sets the backing field to use for this property.
Expand All @@ -128,25 +128,7 @@ public interface IConventionPropertyBuilder : IConventionAnnotatableBuilder
/// The same builder instance if the configuration was applied,
/// <see langword="null" /> otherwise.
/// </returns>
IConventionPropertyBuilder HasField([CanBeNull] FieldInfo fieldInfo, bool fromDataAnnotation = false);

/// <summary>
/// Returns a value indicating whether the backing field can be set for this property
/// from the current configuration source.
/// </summary>
/// <param name="fieldName"> The field name. </param>
/// <param name="fromDataAnnotation"> Indicates whether the configuration was specified using a data annotation. </param>
/// <returns> <see langword="true" /> if the backing field can be set for this property. </returns>
bool CanSetField([CanBeNull] string fieldName, bool fromDataAnnotation = false);

/// <summary>
/// Returns a value indicating whether the backing field can be set for this property
/// from the current configuration source.
/// </summary>
/// <param name="fieldInfo"> The field. </param>
/// <param name="fromDataAnnotation"> Indicates whether the configuration was specified using a data annotation. </param>
/// <returns> <see langword="true" /> if the backing field can be set for this property. </returns>
bool CanSetField([CanBeNull] FieldInfo fieldInfo, bool fromDataAnnotation = false);
new IConventionPropertyBuilder HasField([CanBeNull] FieldInfo fieldInfo, bool fromDataAnnotation = false);

/// <summary>
/// Sets the <see cref="PropertyAccessMode" /> to use for this property.
Expand All @@ -157,16 +139,7 @@ public interface IConventionPropertyBuilder : IConventionAnnotatableBuilder
/// The same builder instance if the configuration was applied,
/// <see langword="null" /> otherwise.
/// </returns>
IConventionPropertyBuilder UsePropertyAccessMode(PropertyAccessMode? propertyAccessMode, bool fromDataAnnotation = false);

/// <summary>
/// Returns a value indicating whether the <see cref="PropertyAccessMode" /> can be set for this property
/// from the current configuration source.
/// </summary>
/// <param name="propertyAccessMode"> The <see cref="PropertyAccessMode" /> to use for this property. </param>
/// <param name="fromDataAnnotation"> Indicates whether the configuration was specified using a data annotation. </param>
/// <returns> <see langword="true" /> if the <see cref="PropertyAccessMode" /> can be set for this property. </returns>
bool CanSetPropertyAccessMode(PropertyAccessMode? propertyAccessMode, bool fromDataAnnotation = false);
new IConventionPropertyBuilder UsePropertyAccessMode(PropertyAccessMode? propertyAccessMode, bool fromDataAnnotation = false);

/// <summary>
/// Configures the maximum length of data that can be stored in this property.
Expand Down
27 changes: 10 additions & 17 deletions src/EFCore/Metadata/Builders/IConventionServicePropertyBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ namespace Microsoft.EntityFrameworkCore.Metadata.Builders
/// not used in application code.
/// </para>
/// </summary>
public interface IConventionServicePropertyBuilder : IConventionAnnotatableBuilder
public interface IConventionServicePropertyBuilder : IConventionPropertyBaseBuilder
{
/// <summary>
/// Gets the service property being configured.
Expand All @@ -31,7 +31,7 @@ public interface IConventionServicePropertyBuilder : IConventionAnnotatableBuild
/// The same builder instance if the configuration was applied,
/// <see langword="null" /> otherwise.
/// </returns>
IConventionServicePropertyBuilder HasField([CanBeNull] string fieldName, bool fromDataAnnotation = false);
new IConventionServicePropertyBuilder HasField([CanBeNull] string fieldName, bool fromDataAnnotation = false);

/// <summary>
/// Sets the backing field to use for this property.
Expand All @@ -42,25 +42,18 @@ public interface IConventionServicePropertyBuilder : IConventionAnnotatableBuild
/// The same builder instance if the configuration was applied,
/// <see langword="null" /> otherwise.
/// </returns>
IConventionServicePropertyBuilder HasField([CanBeNull] FieldInfo fieldInfo, bool fromDataAnnotation = false);
new IConventionServicePropertyBuilder HasField([CanBeNull] FieldInfo fieldInfo, bool fromDataAnnotation = false);

/// <summary>
/// Returns a value indicating whether the backing field can be set for this property
/// from the current configuration source.
/// </summary>
/// <param name="fieldName"> The field name. </param>
/// <param name="fromDataAnnotation"> Indicates whether the configuration was specified using a data annotation. </param>
/// <returns> <see langword="true" /> if the backing field can be set for this property. </returns>
bool CanSetField([CanBeNull] string fieldName, bool fromDataAnnotation = false);

/// <summary>
/// Returns a value indicating whether the backing field can be set for this property
/// from the current configuration source.
/// Sets the <see cref="PropertyAccessMode" /> to use for this property.
/// </summary>
/// <param name="fieldInfo"> The field. </param>
/// <param name="propertyAccessMode"> The <see cref="PropertyAccessMode" /> to use for this property. </param>
/// <param name="fromDataAnnotation"> Indicates whether the configuration was specified using a data annotation. </param>
/// <returns> <see langword="true" /> if the backing field can be set for this property. </returns>
bool CanSetField([CanBeNull] FieldInfo fieldInfo, bool fromDataAnnotation = false);
/// <returns>
/// The same builder instance if the configuration was applied,
/// <see langword="null" /> otherwise.
/// </returns>
new IConventionServicePropertyBuilder UsePropertyAccessMode(PropertyAccessMode? propertyAccessMode, bool fromDataAnnotation = false);

/// <summary>
/// Sets the <see cref="ServiceParameterBinding" /> for this property.
Expand Down
Loading