SpatialFocus / EntityFrameworkCore.Extensions

A set of useful extensions for EntityFrameworkCore (Enum Lookup Tables, Naming of tables / properties / keys, Pluralize).
MIT License
23 stars 11 forks source link

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

Open jeffward01 opened 1 year ago

jeffward01 commented 1 year ago

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

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 commented 1 year ago

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

jeffward01 commented 1 year ago

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 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 commented 1 year ago

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 commented 1 year ago

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:

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):

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:

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,


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 commented 1 year ago

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:

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 πŸ˜ƒ