Skip to content

Commit

Permalink
Fix for 20281. Adding HasField() method and updating tests. (#20323)
Browse files Browse the repository at this point in the history
Adding HasField() method on NavigationBuilder
  • Loading branch information
lajones committed Mar 18, 2020
1 parent eeb9cf9 commit 0f6196f
Show file tree
Hide file tree
Showing 8 changed files with 265 additions and 118 deletions.
22 changes: 22 additions & 0 deletions src/EFCore/Metadata/Builders/IConventionNavigationBuilder.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// 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 JetBrains.Annotations;

namespace Microsoft.EntityFrameworkCore.Metadata.Builders
{
public interface IConventionNavigationBuilder : IConventionAnnotatableBuilder
Expand All @@ -10,6 +12,26 @@ public interface IConventionNavigationBuilder : IConventionAnnotatableBuilder
/// </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> <c>true</c> 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>
/// <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,
/// <c>null</c> otherwise.
/// </returns>
IConventionNavigationBuilder HasField([CanBeNull] string fieldName, bool fromDataAnnotation = false);

/// <summary>
/// Returns a value indicating whether the <see cref="PropertyAccessMode" /> can be set for this navigation
/// from the current configuration source.
Expand Down
53 changes: 40 additions & 13 deletions src/EFCore/Metadata/Builders/NavigationBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,16 +31,16 @@ public NavigationBuilder([NotNull] IMutableNavigationBase navigationOrSkipNaviga
{
Check.NotNull(navigationOrSkipNavigation, nameof(navigationOrSkipNavigation));

NavBuilder = (navigationOrSkipNavigation as Navigation)?.Builder;
SkipNavBuilder = (navigationOrSkipNavigation as SkipNavigation)?.Builder;
InternalNavigationBuilder = (navigationOrSkipNavigation as Navigation)?.Builder;
InternalSkipNavigationBuilder = (navigationOrSkipNavigation as SkipNavigation)?.Builder;
Metadata = navigationOrSkipNavigation;

Check.DebugAssert(NavBuilder != null || SkipNavBuilder != null, "Expected either a Navigation or SkipNavigation");
Check.DebugAssert(InternalNavigationBuilder != null || InternalSkipNavigationBuilder != null, "Expected either a Navigation or SkipNavigation");
}

private InternalNavigationBuilder NavBuilder { get; }
private InternalNavigationBuilder InternalNavigationBuilder { get; }

private InternalSkipNavigationBuilder SkipNavBuilder { get; }
private InternalSkipNavigationBuilder InternalSkipNavigationBuilder { get; }

/// <summary>
/// The navigation being configured.
Expand All @@ -60,13 +60,13 @@ public virtual NavigationBuilder HasAnnotation([NotNull] string annotation, [Not
Check.NotEmpty(annotation, nameof(annotation));
Check.NotNull(value, nameof(value));

if (NavBuilder != null)
if (InternalNavigationBuilder != null)
{
NavBuilder.HasAnnotation(annotation, value, ConfigurationSource.Explicit);
InternalNavigationBuilder.HasAnnotation(annotation, value, ConfigurationSource.Explicit);
}
else
{
SkipNavBuilder.HasAnnotation(annotation, value, ConfigurationSource.Explicit);
InternalSkipNavigationBuilder.HasAnnotation(annotation, value, ConfigurationSource.Explicit);
}

return this;
Expand All @@ -91,21 +91,48 @@ public virtual NavigationBuilder HasAnnotation([NotNull] string annotation, [Not
/// <returns> The same builder instance so that multiple configuration calls can be chained. </returns>
public virtual NavigationBuilder UsePropertyAccessMode(PropertyAccessMode propertyAccessMode)
{
if (NavBuilder != null)
if (InternalNavigationBuilder != null)
{
NavBuilder.UsePropertyAccessMode(propertyAccessMode, ConfigurationSource.Explicit);
InternalNavigationBuilder.UsePropertyAccessMode(propertyAccessMode, ConfigurationSource.Explicit);
}
else
{
SkipNavBuilder.UsePropertyAccessMode(propertyAccessMode, ConfigurationSource.Explicit);
InternalSkipNavigationBuilder.UsePropertyAccessMode(propertyAccessMode, ConfigurationSource.Explicit);
}

return this;
}

IConventionSkipNavigationBuilder IInfrastructure<IConventionSkipNavigationBuilder>.Instance => SkipNavBuilder;
/// <summary>
/// <para>
/// Sets a backing field to use for this navigation property.
/// </para>
/// </summary>
/// <param name="fieldName"> The name of the field to use for this navigation property. </param>
/// <returns> The same builder instance so that multiple configuration calls can be chained. </returns>
public virtual NavigationBuilder HasField([CanBeNull] string fieldName)
{
if (InternalNavigationBuilder != null)
{
InternalNavigationBuilder.HasField(fieldName, ConfigurationSource.Explicit);
}
else
{
InternalSkipNavigationBuilder.HasField(fieldName, ConfigurationSource.Explicit);
}

IConventionNavigationBuilder IInfrastructure<IConventionNavigationBuilder>.Instance => NavBuilder;
return this;
}

/// <summary>
/// The internal builder being used to configure the skip navigation.
/// </summary>
IConventionSkipNavigationBuilder IInfrastructure<IConventionSkipNavigationBuilder>.Instance => InternalSkipNavigationBuilder;

/// <summary>
/// The internal builder being used to configure the navigation.
/// </summary>
IConventionNavigationBuilder IInfrastructure<IConventionNavigationBuilder>.Instance => InternalNavigationBuilder;


#region Hidden System.Object members
Expand Down
31 changes: 31 additions & 0 deletions src/EFCore/Metadata/Internal/InternalNavigationBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,15 @@ public InternalNavigationBuilder([NotNull] Navigation metadata, [NotNull] Intern
{
}

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public virtual new InternalNavigationBuilder HasField([CanBeNull] string fieldName, ConfigurationSource configurationSource)
=> (InternalNavigationBuilder)base.HasField(fieldName, configurationSource);

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
Expand Down Expand Up @@ -68,5 +77,27 @@ IConventionNavigationBuilder IConventionNavigationBuilder.UsePropertyAccessMode(
PropertyAccessMode? propertyAccessMode, bool fromDataAnnotation)
=> UsePropertyAccessMode(
propertyAccessMode, fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention);

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
bool IConventionNavigationBuilder.CanSetField(string fieldName, bool fromDataAnnotation)
=> CanSetField(
fieldName,
fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention);

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
IConventionNavigationBuilder IConventionNavigationBuilder.HasField(string fieldName, bool fromDataAnnotation)
=> HasField(
fieldName,
fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention);
}
}
47 changes: 47 additions & 0 deletions src/EFCore/Metadata/Internal/InternalPropertyBaseBuilder`.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// 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.Internal
Expand All @@ -25,6 +26,52 @@ public InternalPropertyBaseBuilder([NotNull] TPropertyBase metadata, [NotNull] I
{
}

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public virtual InternalPropertyBaseBuilder<TPropertyBase> HasField([CanBeNull] string fieldName, ConfigurationSource configurationSource)
{
if (Metadata.FieldInfo?.GetSimpleMemberName() == fieldName
|| configurationSource.Overrides(Metadata.GetFieldInfoConfigurationSource()))
{
Metadata.SetFieldInfo(fieldName, configurationSource);

return this;
}

return null;
}

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public virtual bool CanSetField([CanBeNull] string fieldName, ConfigurationSource? configurationSource)
{
if (configurationSource.Overrides(Metadata.GetFieldInfoConfigurationSource()))
{
if (fieldName == null)
{
return true;
}

var fieldInfo = PropertyBase.GetFieldInfo(
fieldName, Metadata.DeclaringType, Metadata.Name,
shouldThrow: false);
return fieldInfo != null
&& PropertyBase.IsCompatible(
fieldInfo, Metadata.ClrType, Metadata.DeclaringType.ClrType, Metadata.Name,
shouldThrow: false);
}

return Metadata.FieldInfo?.GetSimpleMemberName() == fieldName;
}

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
Expand Down
41 changes: 2 additions & 39 deletions src/EFCore/Metadata/Internal/InternalPropertyBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -152,18 +152,8 @@ public virtual bool CanSetIsConcurrencyToken(bool? concurrencyToken, Configurati
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public virtual InternalPropertyBuilder HasField([CanBeNull] string fieldName, ConfigurationSource configurationSource)
{
if (Metadata.FieldInfo?.GetSimpleMemberName() == fieldName
|| configurationSource.Overrides(Metadata.GetFieldInfoConfigurationSource()))
{
Metadata.SetFieldInfo(fieldName, configurationSource);

return this;
}

return null;
}
public virtual new InternalPropertyBuilder HasField([CanBeNull] string fieldName, ConfigurationSource configurationSource)
=> (InternalPropertyBuilder)base.HasField(fieldName, configurationSource);

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
Expand All @@ -183,33 +173,6 @@ public virtual InternalPropertyBuilder HasField([CanBeNull] FieldInfo fieldInfo,
return null;
}

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public virtual bool CanSetField([CanBeNull] string fieldName, ConfigurationSource? configurationSource)
{
if (configurationSource.Overrides(Metadata.GetFieldInfoConfigurationSource()))
{
if (fieldName == null)
{
return true;
}

var fieldInfo = PropertyBase.GetFieldInfo(
fieldName, Metadata.DeclaringType, Metadata.Name,
shouldThrow: false);
return fieldInfo != null
&& PropertyBase.IsCompatible(
fieldInfo, Metadata.ClrType, Metadata.DeclaringType.ClrType, Metadata.Name,
shouldThrow: false);
}

return Metadata.FieldInfo?.GetSimpleMemberName() == fieldName;
}

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
Expand Down
Loading

0 comments on commit 0f6196f

Please sign in to comment.