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

Disable Naming Scheme? Is there a way I disable the naming scheme for ALL entities (except the enum types) #24

Open
jeffward01 opened this issue Jan 4, 2023 · 5 comments

Comments

@jeffward01
Copy link

Hello!

I'd like to configure my database table naming scheme manually within my IEntityTypeConfigureation<> classes. I don't want SpatialFocus/EntityFrameworkCore.Extensions to apply any NamingScheme to any non-enum classes at all.

For context, this is my use-case

  • I am using SpatialFocus/EntityFrameworkCore.Extensions for the enum to table features
  • I only want SpatialFocus/EntityFrameworkCore.Extensions to work with enums in this table capacity
  • I do not want SpatialFocus/EntityFrameworkCore.Extensions to touch my other 'non-enum' tables

Is it possible to disable this without manually selecting all entities to exclude?

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.ApplyConfigurationsFromAssembly(typeof(AppIdentityUserConfiguration).Assembly);

        modelBuilder.ConfigureEnumLookup(
            EnumLookupOptions.Default.UseNumberAsIdentifier()
                .UseEnumsWithAttributeOnly()
                .SetDeleteBehavior(DeleteBehavior.NoAction));

        modelBuilder.ConfigureNames(
            NamingOptions.Default.SkipEntireEntities(_ => _.Name.Length > 0) // <--- It would be nice to not do this
        );
    }

This is the result if I specify:

        modelBuilder.ConfigureNames(
            NamingOptions.Default.SkipEntireEntities(_ => _.Name.Length > 0) // <--- It would be nice to not do this
        );

image

If I do not specify the above code snippet, then all tables will be styled to the configuration of the enum which in this example is snake_case with ClrType

@Dresel
Copy link
Member

Dresel commented Jan 5, 2023

Couldn't you just omit modelBuilder.ConfigureNames and set SetNamingScheme of EnumLookupOptions?

@jeffward01
Copy link
Author

Couldn't you just omit modelBuilder.ConfigureNames and set SetNamingScheme of EnumLookupOptions?

I will give that a go and see, I think the reason why I did that was either because:

  • I configured it wrong and did not understand the API, thus all my PascalCase was transformmed to Screaming_Snake_Case (likely I configured it wrong)
  • When I configured EnumLookupOption, it impacted my other tables

I will be able to test this in a few days and see, I ended up spending to much time being 'fancy' with configurations and using Enum in my dbo entities, it did not play nice with EfCore, and I was finding myself losing much time in working with EfCore and configuration.

Now I just use SmartEnums (https://github.com/ardalis/SmartEnum) side by side with my dbo entities, not a great solution, but no configuration required and it works.

I will test your recommendation and let you know.


What would be really cool, would be some sort of SourceGenerator that was a hybrid option of your library, SmartEnum, and also 'SourceGeneration', so that a Database developer, or perhaps user, could add a new entry into the LookupTable within a database, then the Source Generator would 'read' the database, and generate an Enum or SmartEnum to go with it. This way, you loose the constraint on 'Code is Law', and can also use the database.

Source Generators are a bit of a pain, so the tool could even be 'stand-alone', similar to a classic .tt file.

Sorry for rant, just been thinking about that feature for a bit.


Thanks for the advice, I will give it a try and report back. If there is no activity on this for a while, we can close the ticket.

Thanks

@Dresel
Copy link
Member

Dresel commented Jan 11, 2023

I configured it wrong and did not understand the API...

Understandable... you should consider ConfigureEnumLookup and ConfigureNames as two different things. First one will create enum tables and will name them depending on the SetNamingScheme of EnumLookupOptions.

The latter one will just rename all tables - we used this for postgres table names like "user_accounts". You can use the first one without ConfigureNames.

What would be really cool, would be some sort of SourceGenerator that was a hybrid option of your library, SmartEnum, and also 'SourceGeneration', so that a Database developer, or perhaps user, could add a new entry into the LookupTable within a database, then the Source Generator would 'read' the database, and generate an Enum or SmartEnum to go with it. This way, you loose the constraint on 'Code is Law', and can also use the database.

SourceGenerators are great, challenge here is that we are querying the ef model for metadata (see here) so it's more a runtime operation than a compile time operation. I don't know if there are already existing solutions for that (accessing ModelBuilder within a SourceGenerator).

@jeffward01
Copy link
Author

jeffward01 commented Jan 11, 2023

I configured it wrong and did not understand the API...

Understandable... you should consider ConfigureEnumLookup and ConfigureNames as two different things. First one will create enum tables and will name them depending on the SetNamingScheme of EnumLookupOptions.

The latter one will just rename all tables - we used this for postgres table names like "user_accounts". You can use the first one without ConfigureNames.

What would be really cool, would be some sort of SourceGenerator that was a hybrid option of your library, SmartEnum, and also 'SourceGeneration', so that a Database developer, or perhaps user, could add a new entry into the LookupTable within a database, then the Source Generator would 'read' the database, and generate an Enum or SmartEnum to go with it. This way, you loose the constraint on 'Code is Law', and can also use the database.

SourceGenerators are great, challenge here is that we are querying the ef model for metadata (see here) so it's more a runtime operation than a compile time operation. I don't know if there are already existing solutions for that (accessing ModelBuilder within a SourceGenerator).

I agree with the joys and very much pure painful experiences with source generators haha.

I am wondering if you are overcomplicating things with 'hooking into' the modelbuilder. In my opinion, the source generator will be responsible for:

  • Generating enum classes, or SmartEnum classes based upon the 'source of truth of what is found in the source generator.

In my head, that's all it would do.

So somewhere you must specify a class like:

// You could even do by 'convention' of having the Lookup prefix
// or LU_ as an example
[SomeIdentifierAttributeLinkingItToDbTable(SomeConfig.Param)]
public sealed partial  class LookupCreditCardAuthorizationStatusEnum : 
SmartEnum<LookupCreditCardAuthorizationStatusEnum, Guid>
{
    
}

Then the source generator would 'simply' (with source generators, this is a funny word -- with classic code-gen, its much easier):

  • scan and find all classes of enum or SmartEnum like the example above by convention or some attribute
  • Look at the linked DB table, and see if there is value in one or more columns
  • If the column is not marked in the source-generator config as 'exclude', then it can even generate a new 'property' in the SmartEnum
  • Then the generator would 'fire' and generate code like below:
public partial sealed class LookupCreditCardAuthorizationStatusEnum : SmartEnum<LookupCreditCardAuthorizationStatusEnum, Guid>
{
    public static readonly LookupCreditCardAuthorizationStatusEnum Disabled = new(
        nameof(Disabled),
        Guid.Parse("A4D1A3D6-EB35-44CE-8D8A-3A9ED9FEC169"));

    public static readonly LookupCreditCardAuthorizationStatusEnum Failed = new(
        nameof(Failed),
        Guid.Parse("0EFBE9B2-5ADF-4401-B307-6D7F46644B57"));

    public static readonly LookupCreditCardAuthorizationStatusEnum Expired = new(
        nameof(Expired),
        Guid.Parse("1169ED73-5E8C-4655-9449-A9C7F4A42DBA"));

    public static readonly LookupCreditCardAuthorizationStatusEnum Active = new(
        nameof(Active),
        Guid.Parse("62750D9A-F0C8-4A81-AA15-D2D102AF5A99"));

    public static readonly LookupCreditCardAuthorizationStatusEnum PendingVerification = new(
        nameof(PendingVerification),
        Guid.Parse("FC966769-80D1-40B8-A552-E8630207B613"));

    private LookupCreditCardAuthorizationStatusEnum(
        string name,
        Guid value)
        : base(name, value)
    {
    }
}

In this example, I only have (2) columns:

  • Id (the GUID)
  • Type or status, that is the Expired, Failed etc values

You can add more columns and such, and make it fancier, or more simple.

This was how I designed it in my head, I didn't see any need to use the ModelBuilder, or god-forbid, the ever-changing API of the Entity Graph,


  • Is this what you had in mind as well?
  • Why would you want to plug into the ModelBuilder, I could be absolutely missing or overlooking something obvious
  • If this is a discussion you are interested in continuing with, Should we move this discussion elsewhere?
    • I just don't want to bog up an unrelated issue with it.

As of now, all of this is just concept, I don't have any code for this written, but I do love code-generators and am a contributor of Testura.Code in addition to having my own libraries


Understandable... you should consider ConfigureEnumLookup and ConfigureNames as two different things. First one will create enum tables and will name them depending on the SetNamingScheme of EnumLookupOptions.

The latter one will just rename all tables - we used this for postgres table names like "user_accounts". You can use the first one without ConfigureNames.

Thanks for this, this clears things up alot. I very much want to use this library, I will write some documentaiton for it once I implement it. Your advice on what ConfigureEnumLookup and ConfigureNames clears things up alot.

its really cool to see you guys so active, I see a few of your team members in the issues of efcore fairly often. Very cool to see.

Thanks again!

@Dresel
Copy link
Member

Dresel commented Jan 11, 2023

I am wondering if you are overcomplicating things with 'hooking into' the modelbuilder. In my opinion, the source generator will be responsible for:

Generating enum classes, or SmartEnum classes based upon the 'source of truth of what is found in the source generator.

In my head, that's all it would do.

I was thinking this with our library in mind (migrating it to source generators), which does the following:

  • Iterating over all entity types / properties to find enum properties
  • Adding entities for those enums, setting an unique index, specifying HasData, ...
  • Specifying the one to many relationship for the entity property

But you are right - with marker attributes we could omit the first part, and generate the source code for 2) and 3) as ModelBuilder extension methods (or similar). I must admit I somehow like the idea 😄

Your suggestion / addition would be to go the other way round and specify the enum values from the data in the database?

I have heard about but never used SmartEnum, so the generated code there might be different. This library was designed as a simple wrapper for some EF core configuration for plain enums.

its really cool to see you guys so active, I see a few of your team members in the issues of efcore fairly often. Very cool to see.

Too kind 😃

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

No branches or pull requests

2 participants