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

[WIP] - Shared-Type Entities Update Pipeline #14057

Closed
wants to merge 1 commit into from

Conversation

lajones
Copy link
Contributor

@lajones lajones commented Dec 1, 2018

DO NOT COMMIT THIS - work in progress.

Wanted to get some feedback on the work on Shared-Type Entity Types (#9914). So far I've updated the Model to have a new AddSharedEntityType() method to be used during OnModelCreating(); updated DbContext to allow specifying a Shared-Type-DbSet which uses that EntityType; and using that you can insert, update and delete those entities (Note: query not implemented yet).

As part of this I created a new service ISharedTypeEntityFinder. This is used by the StateManager, when given an entity instance, to associate that instance with a Shared-Type-EntityType defined during OnModelCreating(). At the moment there is only one implementation of this service - which identifies the instance by expecting it to have a property-indexer (see #13610) which takes a specific (but configurable) argument and which returns the name of the EntityType which it represents; however the service is replaceable - so other implementations are possible.

Added and updated some tests. More functional tests will be needed but wanted to get feedback first.

…able. Can do

insert, update and delete but not select yet.
/// </summary>
/// <returns> the shared-type EntityType corresponding to this instance if found in the model, or null if not. </returns>
IEntityType FindSharedTypeEntityType([NotNull] object entityInstance);
}
Copy link
Contributor Author

@lajones lajones Dec 1, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wondering about making this a Chain-Of-Responsibility pattern by adding a NextFinder getter/setter. That way if you are using multiple different patterns of identifying a Shared-Type-EntityType from instance in your context you can set up multiple of these chained together in the order you want to apply them.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think that would be necessary, this logic shouldn't be replaced or extended and it shouldn't need to change often. Furthermore it's in a perf-sensitive part of the code and keeping all the patterns for finding the EntityType in one class will be slightly faster.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can see your point. Don't agree that it shouldn't be replaced or extended. I'd be really surprised if different users of this functionality are completely happy with just the one way of doing it. It was not even agreed among us that the index property was the only way to go. That said I take your point on perf (though we do cache the result of course) and maybe we could wait on Chain-Of-Responsibility til we get feedback from customers.

: Model.FindEntityType(entityTypeName);

return entityType != null && entityType.IsSharedType ? entityType : null;
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could add a FindSharedTypeEntityType(string name) method on Model and use that instead of the above?

/// This API supports the Entity Framework Core infrastructure and is not intended to be used
/// directly from your code. This API may change or be removed in future releases.
/// </summary>
public virtual string EntityTypeNamePropertyName { get; [NotNull]set; }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't need to be writable

Copy link
Contributor Author

@lajones lajones Dec 3, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I actually thought was a nice feature, It just allows you to pick a different argument to the indexer - other than "__EntityTypeName__" - which I kind of picked at random.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One would need to do that in the constructor, allowing to do it afterwards could easily lead to inconsistency in the model.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also since it's overridable derived classes can just replace it with a different value.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is true that users could get themselves in trouble if they changed the value half way through. I was just trying to make it simple - so they didn't have to write a whole new class just to change that one value.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we really want to make this simple to configure we should add sugar API that doesn't require messing with services at all, perhaps something on DbContextOptions

@@ -155,7 +157,8 @@ public virtual InternalEntityEntry GetOrCreateEntry(object entity)
{
_trackingQueryMode = TrackingQueryMode.Multiple;

var entityType = _model.FindRuntimeEntityType(entity.GetType());
var entityType = _sharedTypeEntityFinder.FindSharedTypeEntityType(entity)
?? _model.FindRuntimeEntityType(entity.GetType());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_model.FindRuntimeEntityType(entity.GetType()) can be moved into the SharedTypeEntityFinder implementation and it could be renamed to EntityTypeFinder.
Are you planning to implement other type finding strategies? Is there a tracking issue that lists them?

Copy link
Contributor Author

@lajones lajones Dec 3, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure I'm following. This service is meant to find Shared-Type EntiyTypes only. I did have an idea for another one of those - which is for a CLR-type which is shared, and has an indexer for the other properties, but could have a known, fixed-name CLR-property for the EntityType name. But I hadn't discussed that with @ajcvickers or @divega yet and I seem to remember one of them had a 3rd idea on how we should do this. Basically I can see people wanting to identify Shared-Type entities in various different ways and wanted to make it as easy as possible to write your own service if you wanted to.

But I did not intend to change or make into a service the existing functionality to find EntityTypes using their CLR-type. There are 6 methods already existing on Model which do that in various different ways. Did you want to encapsulate all of that somehow?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In general I would like to avoid having to special case Shared type entity types as much as possible and here this can by accomplished by making the service look for non-shared types as well.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could also implement the strategy that uses a normal property and have the name passed in the constructor or a overridable readonly property.

/// This API supports the Entity Framework Core infrastructure and is not intended to be used
/// directly from your code. This API may change or be removed in future releases.
/// </summary>
public class InternalSharedTypeDbSet<TEntity> : InternalDbSet<TEntity>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could just add EntityTypeName to InternalDbSet, no need to add another type.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You mean add EntityTypeName to InternalDbSet, then add a constructor that takes the name, assign to EntityTypeName in that constructor and later use the fact that EntityTypeName is set to distinguish when trying to find the EntityType? That would be possible. It's true I didn't like having to make various things protected. to make this work But this seemed cleaner to me - during e.g. debugging I can immediately see what this is representing - but I'm open to the possibility.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again, I just want to reduce special casing and handling this in one class would result in less code and less code means there are fewer places to introduce bugs.

/// This API supports the Entity Framework Core infrastructure and is not intended to be used
/// directly from your code. This API may change or be removed in future releases.
/// </summary>
public virtual bool IsSharedType { get; set; }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be readonly

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was just following the same pattern as used for IsQueryType. See Model.AddQueryType() and then AddSharedTypeEntityType() immediately below it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, we would need to change IsQueryType too. Changing the value for either after the type is already in the model could leave it in an incosistent state, since it would avoid the checks done in AddSharedType and AddQueryType

@lajones lajones self-assigned this Mar 27, 2019
@ajcvickers ajcvickers closed this May 10, 2019
@smitpatel smitpatel deleted the 181130_SharedTypeEntities_07 branch September 9, 2019 17:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants