EFCorePowerTools
EFCorePowerTools copied to clipboard
Reverse Engineer - Rename navigation properties
Is it possible to rename the generated navigation properties?
Steps to reproduce
Say there are two tables,
CREATE TABLE Product ( Id UNIQUEIDENTIFIER NOT NULL PRIMARY KEY, CategoryCode VARCHAR(20) FOREIGN KEY (CategoryCode) REFERENCES Category (Code) NULL, ParentId UNIQUEIDENTIFIER FOREIGN KEY (ParentId) REFERENCES Product (Id) NULL )
CREATE TABLE Category ( Code VARCHAR(20) NOT NULL PRIMARY KEY, Description VARCHAR(100) NOT NULL )
Product table references both Category and Product itself. The generated class contains the following navigation properties, which I very much want to rename to something more meaningful.
public Category CategoryCodeNavigation { get; set; }
public Product ParentProduct {get;set;}
public virtual ICollection<Product> InverseProduct {get; set;}
I have tried to use efpt.renaming.json, but it only supports renaming columns not FK relations.
I understand I can just manually rename the properties after the code-gen, but the changes will be overwritten when the project is re-generated which we do quite often.
So is there a workaround to rename FK navigation properties?
Thanks for any tips!
Further technical details
EF Core Power Tools version: (found in About dialog - blue questionmark icon on context menu) 2.2.12.0
Database engine: (SQlite, SQL Compact, SQL Server, Postgres) Azure SQL
Visual Studio version: (e.g. Visual Studio 2017 15.7) VS2019
I think it is not possible
Not even with Handlebars?
EDIT: I am facing a similar issue, where my navigation property name gets "ID" stripped from it, and leaves a weird name for the property.
Feel free to give it a try
+1 for this feature request. @ErikEJ - from your understanding, is this still not technically possible? It's been a year now, so I figured it was worth asking - even if a long shot. I'd be happy to have a go at contributing an update if there was a workable solution - would just need a few pointers to get going.
Thanks for this great tool. It is immensely helpful.
@jamshally Let's have a look - please explain (with some sample code) what you see now, and what you would like to see instead.
@ErikEJ - Thanks, here is the scenario:
I have a table with a two column foreign key. The Auto-name-generation of Navigation Property seems to look for common characters between the foreign key fields, sometimes resulting in unsuitable field names. I am looking for a way to rename these Navigation properties during a reverse-engineering code-generation cycle. Example code below:
Table SQL
CREATE TABLE [dbo].[Product] (
[FactoryId] NCHAR (10) NOT NULL, -- // <-- Part of the Composite Primary Key
[FactoryProductIndex] NCHAR (10) NOT NULL, -- // <-- Part of the Composite Primary Key
[Data] NCHAR (10) NULL,
CONSTRAINT [PK_Product] PRIMARY KEY CLUSTERED ([FactoryId] ASC, [FactoryProductIndex] ASC)
);
CREATE TABLE [dbo].[ProductMetaData] (
[FactoryId] NCHAR (10) NOT NULL,
[FactoryProductIndex] NCHAR (10) NOT NULL,
[MetaData] VARCHAR (50) NULL,
CONSTRAINT [PK_ProductMetaData] PRIMARY KEY CLUSTERED ([FactoryId] ASC, [FactoryProductIndex] ASC),
CONSTRAINT [FK_ProductMetaData_Product] FOREIGN KEY ([FactoryId], [FactoryProductIndex]) REFERENCES [dbo].[Product] ([FactoryId], [FactoryProductIndex])
);
Generated Model Class
public partial class Product
{
public string FactoryId { get; set; } // <-- Part of the Composite Primary Key
public string FactoryProductIndex { get; set; } // <-- Part of the Composite Primary Key
public string Data { get; set; }
public virtual ProductMetaDatum ProductMetaDatum { get; set; }
}
public partial class ProductMetaDatum
{
public string FactoryId { get; set; }
public string FactoryProductIndex { get; set; }
public string MetaData { get; set; }
public virtual Product Factory { get; set; } //<-- I would like to rename this Navigation property be "Product"
}
What custom name?
What custom name? @ErikEJ - I have several similar scenarios in a database I am working with. In this instance I'd like to name the Navigation Property "Product"
I have updated the above example to be a little more intuitive, and illustrate the problem better. Due to the auto-naming algorithm around composite primary keys, the Navigation Property is named "Factory", whereas I would like to name it "Product"
@jamshally Have you tried the renaming feature ?- with this efpt.renaming.json file, for example:
[
{
"UseSchemaName": false,
"SchemaName": "dbo",
"Tables": [
{
"Name": "ProductMetaData",
"Columns": [
{
"Name": "FactoryId",
"NewName": "ProductId"
}
]
}
]
}
]
Hi @ErikEJ , thanks for the suggestion.
This does result in the Navigation Property being generated with the name "Property", but then the name of the FactoryId is no longer correct/consistent with the business domain. The primary key of the Product is the combination of the FactoryId and the FactoryProductIndex.
Basically, the naming of the primary key fields are as-intended, it is just the auto-generated naming of the Navigation Property I would like to change.
Not something I plan to implement
Just coming to EF Core Power Tools, and it seems like this is should be an obvious problem. Every time I re-generate, I have to re-name several fields because they have bizarre names, to the extent that I'm the only one on my team who is allowed to re-generate... Bummed to discover there's no remedy and it isn't being considered.
Is this something you would accept a pull request on, if I could figure out how to get it done?
@kinetiq sure, a PR that does not pull in huge amounts of EF Core code to do this would be appreciated.
@ErikEJ Thanks, I will see what I can do. :)
@kinetiq great, let me know if I can help in any way.
@ErikEJ So I found what seem to be the key components, maybe you could confirm that I'm on the right track.
I'm also going to call on the other folks who care about this to pitch in and see if you can help solve this. There is plenty for everyone.
- ReverseEngineer20.ReverseEngineer.ReplacingCandidateNamingService seems to be where the magic happens. Not a lot of comments to be found, but it seems this whole class is about overriding internal APIs to customize the reverse engineering process. GenerateCandidateIdentifier for instance looks like it might be specific to generating names for tables, and that isn't what I need (or is it?), but I can see a pattern for using the Schema class to customize things. @ErikEJ could you confirm that I'm in the right place, and that this is the only place I need to care about for this? The fact that the namespace is numbered worries me.
- This lead me to Schema and the List of Schema on ReplacingCandidateNamingService. I initially wondered why a List<Schema> was required since Schema contains a list of table details, but then I realized it covers cases where a database goes beyond dbo and has multiple schema names. No problem. I also found some tests that were helpful. I could use some help understanding how to extend this.
- Back to ReplacingCandidateNamingService, I see GetDependentEndCandidateNavigationPropertyName and it seems important. I see that it takes IForeignKey as a parameter and you are doing something custom with it. What is this? I did some googling and found no clues.
- Well actually, I found out that GetPrincipalEndCandidateNavigationPropertyName also exists and can be overridden. Source for both is here: https://github.com/dotnet/efcore/blob/main/src/EFCore.Design/Scaffolding/Internal/CandidateNamingService.cs ... This appears to handle a lot of scary-looking edge cases without a single comment. The lack of comments is quite discouraging.
- I would imagine that GetDependentEndCandidateNavigationPropertyName and GetPrincipalEndCandidateNavigationPropertyName might handle the two sides of the navigation property... Can anyone confirm how these fit together? Both of them take IForeignKey which seems strange; for my theory to be correct, one of them would probably be a primary key.
So this is turning into a lot. Any help would be appreciated.
Update: heading to bed but I just found this and I wanted to capture it for the future! It seems to document how these two key methods work. https://entityframeworkcore.com/knowledge-base/50621409/ef-core--nomenclatura-de-propiedades-de-navegacion-en-andamios ... Could it be as simple as checking the schema at the top of these two methods and returning whatever it contains?
Not detecting a lot of passion on this. :)
Loud and clear, this is not something anyone cares to help with. Might as well close this one, I am tapping out.
There are a lot of complexities involved here, like how do you specify the revised naming rules, that need to be sorted out before actually implementation of any renaming mechanics
I wonder if the ability to rename tables and columns solves this issue?
There are a lot of complexities involved here, like how do you specify the revised naming rules, that need to be sorted out before actually implementation of any renaming mechanics
True, but that is relatively straightforward compared to the technical part. My intention was to set this up so that it does whatever it normally does, but defers to some new items in the configuration file.
I wonder if the ability to rename tables and columns solves this issue?
Based on the 9/22 response from @jamshally, it seemed like this was not direct enough. Although I should probably test it for my own purposes.
Could you outline what the configuration entries could look like?
@ErikEJ I haven't gotten that far, I am still in the "is this even possible and where would the code go" phase.
Maybe under Tables > Columns there could be a setting specifically for naming navigation properties, or maybe even more control than that would be required.
Initial (experimental) implementation of this is now available in the latest daily build, pls consult the PR to understand the changes and how to test it: https://github.com/ErikEJ/EFCorePowerTools/pull/734
The current experimental implementation is causing too much confusion and is broken.
I will remove and re-think.
Yes, really hoping you get this working at some point. Seems like in the experimental version, I was able to get it to work but there were cases that it just didn't cover.
but there were cases that it just didn't cover.
@kinetiq Exactly, and I did not want that support burden of a broken feature.
The previous attempt: https://github.com/ErikEJ/EFCorePowerTools/commit/f9e962d6607bca53e3787aa985a73028644a16a0
@ErikEJ Wouldn't it be possible to just use the "plural naming convention" on the auto generated navigation properties?
Please see the sample code below:
public partial class Order
{
public long OrderId { get; set; }
public long CustomerId { get; set; }
public Customer Customer { get; set; }
public virtual List<OrderItem> OrderItems { get; set; } // <-- Plural naming convention
// or public virtual IEnumerable<OrderItem> OrderItems { get; set; }
// or public virtual ICollection<OrderItem> OrderItems { get; set; }
// ...
}
public partial class OrderItem
{
public long OrderItemId { get; set; }
public string Description { get; set; }
// ...
}
The hook that is currently there is too early in the naming pipeline
Might be just very nainve suggestion, but wouldn't refactor/rename do the trick? I mean that's what I'm actually doing after running the tool.