-
Notifications
You must be signed in to change notification settings - Fork 3.2k
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
Refactor annotation code generation #21329
Conversation
Core annotations are an implementation detail and shouldn't be treated as other annotations, instead the configuration should only be accessed through public API. In the future they won't be returned together with other annotations - #19806. So it might make sense to add them to the ignore list pre-emptively. It's ok to depend on |
I guess that mean that annotation conventions (e.g. PropertyAnnotationChanged) wouldn't be fired for them, requiring new conventions for facets? Would we also do this for relational annotations (why not)? Am curious what actual problems this extra complexity is meant to solve... In any case, it's going to be a problem for the current approach in this PR:
There's ways around this (pass Anyway, let me know and if this is the way we're going, I'll change this PR accordingly. |
src/EFCore.Design/Scaffolding/Internal/CSharpDbContextGenerator.cs
Outdated
Show resolved
Hide resolved
That's already the case
With #19806
The core model might have other implementations (like compiled model) that choose to represent those facets without using annotations for perf.
Just treat them as any other core facet, this shouldn't require changing your design. I appreciate that having a uniform abstraction makes the code better. But that's not what the annotations were designed for, they are meant for extending the core model. |
/// </summary> | ||
/// <param name="annotations"> The annotations to remove from. </param> | ||
/// <param name="annotationNames"> The ignored annotation names. </param> | ||
protected virtual void IgnoreAnnotations( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't this a breaking change?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are several in this PR, since the actual design is changing quite a bit. But this is in the design assembly, where we usually have lower standards. If we feel like we need to maintain backwards compat here somehow I can look into bringing it back.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Then it contradicts
The AnnotationCodeGenerator base class exposes the same one-by-one API as before, so this is totally backwards compatible (unless the interface is implemented directly).
I am fine either if we want to break/be back-compat/obsolete but it should be consistent.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think breaking changes in Design aren't the same as in explicitly provider-facing types such as AnnotationCodeGenerator.
/// <param name="property"> The <see cref="IProperty" />. </param> | ||
/// <param name="annotation"> The <see cref="IAnnotation" />. </param> | ||
/// <returns> <see langword="null" />. </returns> | ||
public virtual AttributeCodeFragment GenerateDataAnnotation([NotNull] IProperty property, [NotNull] IAnnotation annotation) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this public?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The idea is to allow providers to generate arbitrary data annotation attributes for annotations - this is the hook for that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah sorry, you mean why not protected. Yeah, this and all the previously public GenerateFluentApi should be protected - though that would be a small breaking change for the latter (which I think we should do).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I mean why are we adding API for individual annotation when we are moving towards framework of all annotations together.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Most annotations still have a single fluent API (or data annotation), so this is a trivial way for providers to implement that. I'd only expect providers to override the multi-annotation API (GenerateFluentApiCalls) when multiple annotations need to go into the same fluent API call, as with identity increment/seed. This also preserves compatibility with the existing API to a large extent.
The design need to change because the core annotations no longer appear in the the dictionary I'm passing around, right? (because they've already been removed by RemoveIgnoredAnnotation, or after #19806, not even returned from GetAnnotations). So there needs to be a different mechanism for knowing whether a given annotation has already generated as a data annotation, etc.? (not pushing back on anything, just making sure we're aligned) |
That's correct, but you are already handling other facets that were never stored as annotations (e.g. .IsRequired() / [Required]), so that mechanism is in place. |
Am I? The idea for now was only to handle annotations. But if we want to move non-annotation handling into this as well (especially since core annotations aren't really annotations), we can do that. In that case the type should probably be renamed to something like CodeGenerationHelper - it seems like we're breaking it anyway? |
Yes, it's handled in |
That was the original idea, yes (see note in the original post). But I'm fine with going further and moving non-annotation-based code generation into this as well, should we do this? In that case, do we rename to CodeGenerationHelper or similar? Note that code the gets generated differently between scaffolding and model snapshot would still stay in the respective generators. |
I don't think there's enough benefit in doing this |
OK, I will leave core annotations and facet handling out of this type entirely, and handle only relational/provider-specific. |
527aee2
to
96becc2
Compare
96becc2
to
c4a2bad
Compare
Another thing: make sure that ProductVersion is still being added to the snapshot, add a test if it's not. |
@AndriySvyryd good catch, opened #21360. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good stuff
/// <summary> | ||
/// The type mapper. | ||
/// </summary> | ||
public IRelationalTypeMappingSource RelationalTypeMappingSource { get; } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing With
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Am surprised the API consistency tests didn't catch this. Will fix and look into the tests too.
Notes
Supercedes #21210