Skip to content

Commit

Permalink
Use FindEntityType(Type) as appropriate
Browse files Browse the repository at this point in the history
- Make FindEntityType(Type) return null for shared type entity type
- Add IModel.IsShared(Type) API
- Flow IEntityType info in places when availble rather than doing Type based loopup

Part of #9914
  • Loading branch information
smitpatel committed Jul 15, 2020
1 parent 45fe7cd commit f99b5f7
Show file tree
Hide file tree
Showing 19 changed files with 132 additions and 46 deletions.
5 changes: 5 additions & 0 deletions src/EFCore/DbContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,11 @@ private IEntityFinder Finder(Type type)
throw new InvalidOperationException(CoreStrings.InvalidSetTypeWeak(type.ShortDisplayName()));
}

if (Model.IsShared(type))
{
throw new InvalidOperationException(CoreStrings.InvalidSetSharedType(type.ShortDisplayName()));
}

throw new InvalidOperationException(CoreStrings.InvalidSetType(type.ShortDisplayName()));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ public static IServiceCollection AddDbContext<TContextService, TContextImplement
serviceCollection,
optionsAction == null
? (Action<IServiceProvider, DbContextOptionsBuilder>)null
: (p, b) => optionsAction.Invoke(b), contextLifetime, optionsLifetime);
: (p, b) => optionsAction(b), contextLifetime, optionsLifetime);

/// <summary>
/// <para>
Expand Down Expand Up @@ -672,7 +672,7 @@ public static IServiceCollection AddDbContextFactory<TContext, TFactory>(
serviceCollection,
optionsAction == null
? (Action<IServiceProvider, DbContextOptionsBuilder>)null
: (p, b) => optionsAction.Invoke(b),
: (p, b) => optionsAction(b),
lifetime);

/// <summary>
Expand Down
14 changes: 13 additions & 1 deletion src/EFCore/Extensions/ModelExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ public static class ModelExtensions
{
/// <summary>
/// Gets the entity that maps the given entity class. Returns <see langword="null" /> if no entity type with
/// the given CLR type is found or the entity type has a defining navigation.
/// the given CLR type is found or the given CLR type is being used by shared type entity type
/// or the entity type has a defining navigation.
/// </summary>
/// <param name="model"> The model to find the entity type in. </param>
/// <param name="type"> The type to find the corresponding entity type for. </param>
Expand All @@ -34,6 +35,7 @@ public static IEntityType FindEntityType([NotNull] this IModel model, [NotNull]
/// <summary>
/// Gets the entity that maps the given entity class, where the class may be a proxy derived from the
/// actual entity type. Returns <see langword="null" /> if no entity type with the given CLR type is found
/// or the given CLR type is being used by shared type entity type
/// or the entity type has a defining navigation.
/// </summary>
/// <param name="model"> The model to find the entity type in. </param>
Expand Down Expand Up @@ -119,6 +121,16 @@ public static bool HasEntityTypeWithDefiningNavigation([NotNull] this IModel mod
=> Check.NotNull(model, nameof(model)).AsModel()
.HasEntityTypeWithDefiningNavigation(Check.NotNull(name, nameof(name)));

/// <summary>
/// Gets whether the CLR type is used by shared type entities in the model.
/// </summary>
/// <param name="model"> The model to find the entity type in. </param>
/// <param name="clrType"> The CLR type. </param>
/// <returns> Whether the CLR type is used by shared type entities in the model. </returns>
[DebuggerStepThrough]
public static bool IsShared([NotNull] this IModel model, [NotNull] Type clrType)
=> Check.NotNull(model, nameof(model)).AsModel().IsShared(Check.NotNull(clrType, nameof(clrType)));

/// <summary>
/// Gets the default change tracking strategy being used for entities in the model. This strategy indicates how the
/// context detects changes to properties for an instance of an entity type.
Expand Down
4 changes: 3 additions & 1 deletion src/EFCore/Extensions/MutableModelExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ namespace Microsoft.EntityFrameworkCore
public static class MutableModelExtensions
{
/// <summary>
/// Gets the entity that maps the given entity class. Returns <see langword="null" /> if no entity type with the given name is found.
/// Gets the entity that maps the given entity class. Returns <see langword="null" /> if no entity type with
/// the given CLR type is found or the given CLR type is being used by shared type entity type
/// or the entity type has a defining navigation.
/// </summary>
/// <param name="model"> The model to find the entity type in. </param>
/// <param name="type"> The type to find the corresponding entity type for. </param>
Expand Down
1 change: 1 addition & 0 deletions src/EFCore/Infrastructure/ModelValidator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,7 @@ var isTargetWeakOrOwned

if (targetType?.IsValidEntityType() == true
&& (isTargetWeakOrOwned
|| conventionModel.IsShared(targetType)
|| conventionModel.FindEntityType(targetType) != null
|| targetType.GetRuntimeProperties().Any(p => p.IsCandidateProperty())))
{
Expand Down
6 changes: 3 additions & 3 deletions src/EFCore/Internal/EntityFinder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public class EntityFinder<TEntity> : IEntityFinder<TEntity>
private readonly IStateManager _stateManager;
private readonly IDbSetSource _setSource;
private readonly IDbSetCache _setCache;
private readonly IModel _model;
private readonly IEntityType _entityType;
private readonly IQueryable<TEntity> _queryRoot;

/// <summary>
Expand All @@ -47,7 +47,7 @@ public EntityFinder(
_stateManager = stateManager;
_setSource = setSource;
_setCache = setCache;
_model = entityType.Model;
_entityType = entityType;
_queryRoot = (IQueryable<TEntity>)BuildQueryRoot(entityType);
}

Expand Down Expand Up @@ -274,7 +274,7 @@ private static IReadOnlyList<IProperty> GetLoadProperties(INavigation navigation

private TEntity FindTracked(object[] keyValues, out IReadOnlyList<IProperty> keyProperties)
{
var key = _model.FindEntityType(typeof(TEntity)).FindPrimaryKey();
var key = _entityType.FindPrimaryKey();
keyProperties = key.Properties;

if (keyProperties.Count != keyValues.Length)
Expand Down
5 changes: 5 additions & 0 deletions src/EFCore/Internal/InternalDbSet.cs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,11 @@ private IEntityType EntityType
throw new InvalidOperationException(CoreStrings.InvalidSetTypeWeak(typeof(TEntity).ShortDisplayName()));
}

if (_context.Model.IsShared(typeof(TEntity)))
{
throw new InvalidOperationException(CoreStrings.InvalidSetSharedType(typeof(TEntity).ShortDisplayName()));
}

throw new InvalidOperationException(CoreStrings.InvalidSetType(typeof(TEntity).ShortDisplayName()));
}

Expand Down
12 changes: 7 additions & 5 deletions src/EFCore/Metadata/Conventions/BackingFieldConvention.cs
Original file line number Diff line number Diff line change
Expand Up @@ -86,29 +86,31 @@ private FieldInfo GetFieldToSet(IConventionPropertyBase propertyBase)
return null;
}

var type = propertyBase.DeclaringType.ClrType;
var entityType = (IConventionEntityType)propertyBase.DeclaringType;
var type = entityType.ClrType;
var baseTypes = entityType.GetAllBaseTypes().ToArray();
while (type != null)
{
var fieldInfo = TryMatchFieldName(propertyBase, type);
var fieldInfo = TryMatchFieldName(propertyBase, entityType, type);
if (fieldInfo != null
&& (propertyBase.PropertyInfo != null || propertyBase.Name == fieldInfo.GetSimpleMemberName()))
{
return fieldInfo;
}

type = type.BaseType;
entityType = baseTypes.FirstOrDefault(et => et.ClrType == type);

}

return null;
}

private static FieldInfo TryMatchFieldName(IConventionPropertyBase propertyBase, Type entityClrType)
private static FieldInfo TryMatchFieldName(IConventionPropertyBase propertyBase, IConventionEntityType entityType, Type entityClrType)
{
var model = propertyBase.DeclaringType.Model;
var propertyName = propertyBase.Name;

IReadOnlyDictionary<string, FieldInfo> fields;
var entityType = model.FindEntityType(entityClrType);
if (entityType == null)
{
var newFields = new Dictionary<string, FieldInfo>(StringComparer.Ordinal);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ public virtual void ProcessEntityTypeAdded(
var entityType = entityTypeBuilder.Metadata;
var clrType = entityType.ClrType;
if (clrType == null
|| entityType.HasSharedClrType
|| entityType.HasDefiningNavigation()
|| entityType.FindDeclaredOwnership() != null
|| entityType.Model.FindIsOwnedConfigurationSource(clrType) != null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,10 @@ public virtual void ProcessEntityTypeAdded(
var entityType = entityTypeBuilder.Metadata;
var clrType = entityType.ClrType;
if (clrType == null
|| entityType.HasDefiningNavigation())
|| entityType.HasSharedClrType
|| entityType.HasDefiningNavigation()
|| entityType.FindDeclaredOwnership() != null
|| entityType.Model.FindIsOwnedConfigurationSource(clrType) != null)
{
return;
}
Expand Down
13 changes: 10 additions & 3 deletions src/EFCore/Metadata/IConventionModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,13 @@ public interface IConventionModel : IModel, IConventionAnnotatable
IConventionEntityType AddEntityType([NotNull] Type clrType, bool fromDataAnnotation = false);

/// <summary>
/// Adds an entity type to the model.
/// <para>
/// Adds a shared type entity type to the model.
/// </para>
/// <para>
/// Shared type entity type is an entity type which can share CLR type with other types in the model but has
/// a unique name and always identified by the name.
/// </para>
/// </summary>
/// <param name="name"> The name of the entity to be added. </param>
/// <param name="clrType"> The CLR class that is used to represent instances of the entity type. </param>
Expand Down Expand Up @@ -88,6 +94,7 @@ IConventionEntityType AddEntityType(

/// <summary>
/// Gets the entity with the given name. Returns <see langword="null" /> if no entity type with the given name is found
/// or the given CLR type is being used by shared type entity type
/// or the entity type has a defining navigation.
/// </summary>
/// <param name="name"> The name of the entity type to find. </param>
Expand Down Expand Up @@ -136,10 +143,10 @@ IConventionEntityType FindEntityType(
string RemoveIgnored([NotNull] string typeName);

/// <summary>
/// Gets whether the CLR type is used by shared entities in the model.
/// Gets whether the CLR type is used by shared type entities in the model.
/// </summary>
/// <param name="clrType"> The CLR type. </param>
/// <returns> Whether the CLR type is used by shared entities in the model. </returns>
/// <returns> Whether the CLR type is used by shared type entities in the model. </returns>
bool IsShared([NotNull] Type clrType);

/// <summary>
Expand Down
1 change: 1 addition & 0 deletions src/EFCore/Metadata/IModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ public interface IModel : IAnnotatable

/// <summary>
/// Gets the entity type with the given name. Returns null if no entity type with the given name is found
/// or the given CLR type is being used by shared type entity type
/// or the entity type has a defining navigation.
/// </summary>
/// <param name="name"> The name of the entity type to find. </param>
Expand Down
9 changes: 8 additions & 1 deletion src/EFCore/Metadata/IMutableModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,13 @@ public interface IMutableModel : IModel, IMutableAnnotatable
IMutableEntityType AddEntityType([NotNull] Type clrType);

/// <summary>
/// Adds an entity type to the model.
/// <para>
/// Adds a shared type entity type to the model.
/// </para>
/// <para>
/// Shared type entity type is an entity type which can share CLR type with other types in the model but has
/// a unique name and always identified by the name.
/// </para>
/// </summary>
/// <param name="name"> The name of the entity to be added. </param>
/// <param name="clrType"> The CLR class that is used to represent instances of the entity type. </param>
Expand Down Expand Up @@ -75,6 +81,7 @@ IMutableEntityType AddEntityType(

/// <summary>
/// Gets the entity with the given name. Returns <see langword="null" /> if no entity type with the given name is found
/// or the given CLR type is being used by shared type entity type
/// or the entity type has a defining navigation.
/// </summary>
/// <param name="name"> The name of the entity type to find. </param>
Expand Down
24 changes: 15 additions & 9 deletions src/EFCore/Metadata/Internal/Model.cs
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,11 @@ private EntityType AddEntityType(EntityType entityType)

if (entityType.HasSharedClrType)
{
if (_entityTypes.Any(et => !et.Value.HasSharedClrType && et.Value.ClrType == entityType.ClrType))
{
throw new InvalidOperationException(CoreStrings.ClashingNonSharedType(entityType.DisplayName()));
}

_sharedEntityClrTypes.Add(entityType.ClrType);
}
else if (_sharedEntityClrTypes.Contains(entityType.ClrType))
Expand All @@ -249,15 +254,7 @@ private EntityType AddEntityType(EntityType entityType)
/// 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 EntityType FindEntityType([NotNull] Type type)
{
if (_sharedEntityClrTypes.Contains(type))
{
throw new InvalidOperationException(CoreStrings.CannotFindEntityWithClrTypeWhenShared(type.DisplayName()));
}

return FindEntityType(GetDisplayName(type));
}
public virtual EntityType FindEntityType([NotNull] Type type) => FindEntityType(GetDisplayName(type));

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
Expand Down Expand Up @@ -714,6 +711,15 @@ private string AddIgnored(

_ignoredTypeNames[name] = configurationSource;

if (type == null)
{
// This is to populate Type for convention when removing shared type entity type
type = _entityTypes.TryGetValue(name, out var existingEntityType)
&& existingEntityType.HasSharedClrType
? existingEntityType.ClrType
: null;
}

return ConventionDispatcher.OnEntityTypeIgnored(Builder, name, type);
}

Expand Down
34 changes: 25 additions & 9 deletions src/EFCore/Properties/CoreStrings.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit f99b5f7

Please sign in to comment.