ErikEJ / EFCorePowerTools

Entity Framework Core Power Tools - reverse engineering, migrations and model visualization in Visual Studio & CLI
MIT License
2.21k stars 298 forks source link

Property names not being applied when using efpt.property-renaming.json #2160

Closed ososnilknarf closed 9 months ago

ososnilknarf commented 9 months ago

I'm using the efpt.property-renaming.json file to rename some of the Navigation properties, but it is having no effect.

I understand that this is an experimental feature, and have read that there are many pitfalls, but I'd really like to get it working, and have no clue what the problem might be. So, if there is any guidance on common causes of failure, ways to troubleshoot, or things to try, it would be much appreciated.

Some details of my setup: I am using an extensive efpt.renaming.json file that was generated from an edmx file, so that it explicitly provides the names for every table and column. These options are checked: "Pluralize or singularize generated object names" "Use nullable reference types" "Remove default dbContext constructor"

I'm trying to get this working with this file to rename a single property:

 {
    "Classes": [
      {
        "Name": "AppRole",
        "Properties": [
          {
            "Name": "TenantsNavigation",
            "NewName": "HideRoleTenants"
          },
        ]
      },
}

This gives the following errors in the extensions window:

Property renamer => Could not find table AppRole property TenantNavigation
Property renamer => No properties renamed

Technical details

Let me know if more information is required. Any help is appreciated.

Thanks!

ososnilknarf commented 9 months ago

I've found the problem here. I sed the code to generate the renaming file from the EDMX referenced here: https://github.com/ErikEJ/EFCorePowerTools/issues/1331

This is unexpectedly pluralizing the names of the tables, and I didn't notice that. And the table names I was using in the efpt.property-renaming.json were the unpluralized names (the names that are actually generated in the EF6 edmx file).

So now to understand how or why those names are getting pluralized, when they are not in the edmx file itself??

ososnilknarf commented 9 months ago

Update: So, I modified the code in the EDMX translation so that it wouldn't pluralize the names of the classes created from the tables (I think it is really a bug, I should make a post on that page in case it might help someone else). Because it was using the values from the EntitySets which are the names used when you have a collection of the class. But anyway...

Now, with the renaming files and tables all aligned correctly in the efpt config files, I am still unable to get it to work properly. It seems to be intermittent though. There was one time that it actually did work, and renamed the file, but it logged this error:

File name: 'C:\Bitbucket\EnbaseDashboard\Enbase.CoreNet\Entities\CoreEF\AppUserAudit.cs'
   at void System.IO.__Error.WinIOError(int errorCode, string maybeFullPath)
   at void System.IO.FileStream.Init(string path, FileMode mode, FileAccess access, int rights, bool useRights, FileShare share, int bufferSize, FileOptions options, SECURITY_ATTRIBUTES secAttrs, string msgPath, bool bFromProxy, bool useLongPath, bool checkHost)
   at new System.IO.FileStream(string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, FileOptions options, string msgPath, bool bFromProxy, bool useLongPath, bool checkHost)
   at new System.IO.StreamReader(string path, Encoding encoding, bool detectEncodingFromByteOrderMarks, int bufferSize, bool checkHost)
   at string System.IO.File.InternalReadAllText(string path, Encoding encoding, bool checkHost)
   at async Task<int> EFCorePowerTools.Helpers.RoslynExtensions.SaveDocumentsAsync(IEnumerable<Document> documents)
   at async Task<List<string>> EFCorePowerTools.Helpers.RoslynEntityPropertyRenamer.ApplyRenamingRulesAsync(Model model, string projectPath, string contextFolder, string modelsFolder)
   at async Task EFCorePowerTools.Handlers.ReverseEngineer.ReverseEngineerHandler.ApplyNavigationRenamersAsync(Project project, string referenceRenamingPath, ReverseEngineerOptions options)

The thing is though, AppUserAudit.cs does not exist. AppUserAudit is a table in the database, but it is NOT in the list of tables in the efpt.config.json, nor is it referenced anywhere in the project. So, it would seem that this code is using the full list of tables perhaps, instead of only the selected tables in the config?

Also, I was unable to get it to work again, without changing any of the settings.

ErikEJ commented 9 months ago

@ososnilknarf Is there anything for me to do here?

ErikEJ commented 9 months ago

@ososnilknarf Most likely a virus scanner interfering with the file writes

ososnilknarf commented 9 months ago

Thanks for the reply Erik. I'm not having much luck getting it working here. That error that was logged is very strange because the AppUserAudit table that it references is not one of the tables selected in the efpt.config.json. Could there be a bug where it isn't using the filtered list of tables for some part of the nav renaming operation?

ErikEJ commented 9 months ago

I think it is saving all files regardless. What error do you get?

ososnilknarf commented 9 months ago

I was referring to the error that I included in the post above, but I see now that I only posted the stack trace. The error was: System.IO.FileNotFoundException: Could not find file 'C:\Bitbucket\EnbaseDashboard\Enbase.CoreNet\Entities\CoreEF\AppUserAudit.cs'.

Which makes sense because that table was not selected so the file is not expected to exist.

ErikEJ commented 9 months ago

Can you provide a simple repro, and I will have a look.

ososnilknarf commented 9 months ago

Okay, I can't reproduce the case where I got that error above. But it still is not working. Here is the reproducible case for me here: This is all part of large database, but I'm only trying to rename a single property on a single table.

The class that EFPT generates looks like this:

public partial class AppRole
{
    public int AppRoleId { get; set; }

    public string RoleName { get; set; } = null!;

    public string RoleDescription { get; set; } = null!;

    public int DisplayOrder { get; set; }

    public bool? IsSystemRole { get; set; }

    public string? UserDescription { get; set; }

    public virtual ICollection<AppUserAppRole> AppUserAppRole { get; set; } = new List<AppUserAppRole>();

    public virtual ICollection<Permission> Permission { get; set; } = new List<Permission>();

    public virtual ICollection<Tenant> Tenant { get; set; } = new List<Tenant>();

    public virtual ICollection<Tenant> TenantNavigation { get; set; } = new List<Tenant>();
}

And this is as expected, for the pre-property-renaming step.

The contents of my efpt.property-renaming.json file:

  {
    "Classes": [
      {
        "Name": "AppRole",
        "Properties": [
          {
            "Name": "TenantNavigation",
            "NewName": "HideRoleTenants"
          },
        ]
      },
    ]
  }

Running the Reverse Engineer does not have any effect, and (this detail is IMPORTANT here) It doesn't write any output to the extensions window. Now, if I change the efpt.property-renaming.json file so that the "Name" property is incorrect, such that it is not expected to exist, like this:

            "Name": "TenantNavigationFoo",

Then I see the following output in the extension window:

Property renamer => Could not find table AppRole property TenantNavigationFoo
Property renamer => No properties renamed

This is important to note because this message comes from the EFPT source code here:

                    if (docWithRename != null)
                    {
                        // documents have been mutated. update reference to workspace:
                        project = docWithRename.Project;
                        renameCount++;
                        status.Add($"Property renamer => Renamed class {classRename.Name} property {fromNames[0]} -> {refRename.NewName}");
                    }
                    else
                    {
                        status.Add($"Property renamer => Could not find table {classRename.Name} property {string.Join(", ", fromNames)}");
                    }

Which means that in the case where the name it is looking for DOES match properly, it is never getting to that point, but if it doesn't match, then it is.

Selected properties from my configuration file:

{
   "CodeGenerationMode": 4,
   "DefaultDacpacSchema": null,
   "FilterSchemas": false,
   "IncludeConnectionString": false,
   "ModelNamespace": "Entities",
   "OutputContextPath": null,
   "PreserveCasingWithRegex": true,
   "Schemas": null,
   "SelectedHandlebarsLanguage": 2,
   "SelectedToBeGenerated": 0,
   "T4TemplatePath": null,
   "UncountableWords": null,
   "UseBoolPropertiesWithoutDefaultSql": false,
   "UseDatabaseNames": false,
   "UseDateOnlyTimeOnly": false,
   "UseDbContextSplitting": false,
   "UseDecimalDataAnnotationForSprocResult": true,
   "UseFluentApiOnly": true,
   "UseHandleBars": false,
   "UseHierarchyId": false,
   "UseInflector": false,
   "UseLegacyPluralizer": true,
   "UseManyToManyEntity": false,
   "UseNoDefaultConstructor": true,
   "UseNoNavigations": false,
   "UseNoObjectFilter": false,
   "UseNodaTime": false,
   "UseNullableReferences": true,
   "UsePrefixNavigationNaming": false,
   "UseSchemaFolders": false,
   "UseSchemaNamespaces": false,
   "UseSpatial": false,
   "UseT4": false
}

Thanks for offering to take a look. Let me know if I can provide any more details.

ososnilknarf commented 9 months ago

After I made that last post, I'm seeing some very strange behavior here. Not sure if it is related to source control, having git tracking these files, or what, but I found that after I checked in the generated files, a few moments later, a bunch of changes to the files popped up. Then this happened a few more times. This is after I'd been running EFPT repeatedly over and over trying different things before this.

I didn't mention this before but every time I run it the VS progress bar always seems to stop around halfway through, and I'd just been assuming that is just a progress bug, but maybe the process is getting hung up somewhere and that is why I'm not seeing the output or the renames happening, just because the process is hung and hasn't completed yet.

Seems like something related to git, like committing the files is freeing up the blocked process to continue, and writes them out. Also, seems like the extension should prevent a "Reverse Engineer" when one is in progress would help.

ErikEJ commented 9 months ago

If you have a large number of files, property renaming can take a very long time. It will always remain an experimental feature. Use t4 instead.

ososnilknarf commented 9 months ago

I see that leaving it alone last night it did finally complete after 49 minutes, but it looks like it worked anyway. We have 87 files. Is that considered a lot?

Okay, guess I'll have to dive into t4 templates to see how they work. Is there a sample or anything that you know of that could point me in the right direction for renaming nav properties?

Thanks for your time, and for the great utility. My apologies for the goose-chase on this one. :)

ErikEJ commented 9 months ago

Yeah, it is not a ready for prime time feature for sure. In the samples folder in this repo there are some templates with nav renaming that should get you started.

I plan to remove this feature soon, too much agony and support effort.