Npgsql integration with Entity Framework Code First

前端 未结 2 1310
佛祖请我去吃肉
佛祖请我去吃肉 2021-01-05 01:00

I have a project using the last version of EF CF with PostgreSQL and Npgsql.

My model looks like:

[Table(\"mytable\")]
public class MyTable
{
    [Co         


        
相关标签:
2条回答
  • 2021-01-05 01:42

    Here is an example for EF Core.

    Current code convert tables, properties, keys and indexes to snake case for Postgre, you can use it as a base for your custom conventions:

    using System;
    using System.Text.RegularExpressions;
    using Microsoft.EntityFrameworkCore;
    using Microsoft.EntityFrameworkCore.Metadata;
    using Npgsql;
    
    namespace Database.Customization
    {
        public class PostgreDbContext : DbContext
        {
            private static readonly Regex _keysRegex = new Regex("^(PK|FK|IX)_", RegexOptions.Compiled);
    
            public PostgreDbContext(DbContextOptions options)
                : base(options)
            {
            }
    
            protected override void OnModelCreating(ModelBuilder modelBuilder)
            {
                base.OnModelCreating(modelBuilder);
                FixSnakeCaseNames(modelBuilder);
            }
    
            private void FixSnakeCaseNames(ModelBuilder modelBuilder)
            {
                var mapper = new NpgsqlSnakeCaseNameTranslator();
                foreach (var table in modelBuilder.Model.GetEntityTypes())
                {
                    ConvertToSnake(mapper, table);
                    foreach (var property in table.GetProperties())
                    {
                        ConvertToSnake(mapper, property);
                    }
    
                    foreach (var primaryKey in table.GetKeys())
                    {
                        ConvertToSnake(mapper, primaryKey);
                    }
    
                    foreach (var foreignKey in table.GetForeignKeys())
                    {
                        ConvertToSnake(mapper, foreignKey);
                    }
    
                    foreach (var indexKey in table.GetIndexes())
                    {
                        ConvertToSnake(mapper, indexKey);
                    }
                }
            }
    
            private void ConvertToSnake(INpgsqlNameTranslator mapper, object entity)
            {
                switch (entity)
                {
                    case IMutableEntityType table:
                        var relationalTable = table.Relational();
                        relationalTable.TableName = ConvertGeneralToSnake(mapper, relationalTable.TableName);
                        if (relationalTable.TableName.StartsWith("asp_net_"))
                        {
                            relationalTable.TableName = relationalTable.TableName.Replace("asp_net_", string.Empty);
                            relationalTable.Schema = "identity";
                        }
    
                        break;
                    case IMutableProperty property:
                        property.Relational().ColumnName = ConvertGeneralToSnake(mapper, property.Relational().ColumnName);
                        break;
                    case IMutableKey primaryKey:
                        primaryKey.Relational().Name = ConvertKeyToSnake(mapper, primaryKey.Relational().Name);
                        break;
                    case IMutableForeignKey foreignKey:
                        foreignKey.Relational().Name = ConvertKeyToSnake(mapper, foreignKey.Relational().Name);
                        break;
                    case IMutableIndex indexKey:
                        indexKey.Relational().Name = ConvertKeyToSnake(mapper, indexKey.Relational().Name);
                        break;
                    default:
                        throw new NotImplementedException("Unexpected type was provided to snake case converter");
                }
            }
    
            private string ConvertKeyToSnake(INpgsqlNameTranslator mapper, string keyName) =>
                ConvertGeneralToSnake(mapper, _keysRegex.Replace(keyName, match => match.Value.ToLower()));
    
            private string ConvertGeneralToSnake(INpgsqlNameTranslator mapper, string entityName) =>
                mapper.TranslateMemberName(ModifyNameBeforeConvertion(mapper, entityName));
    
            protected virtual string ModifyNameBeforeConvertion(INpgsqlNameTranslator mapper, string entityName) => entityName;
        }
    }
    

    Update:

    If you are working with EF core 3 then Relational() will cause error as the method was removed lately. Change the definition of the ConvertToSnake function as following:

    private void ConvertToSnake(INpgsqlNameTranslator mapper, object entity)
    {
            switch (entity)
            {
                case IMutableEntityType table:
                    table.SetTableName(ConvertGeneralToSnake(mapper, table.GetTableName()));
                    if (table.GetTableName().StartsWith("asp_net_"))
                    {
                        table.SetTableName(table.GetTableName().Replace("asp_net_", string.Empty));
                        table.SetSchema("identity");
                    }
    
                break;
            case IMutableProperty property:
                property.SetColumnName(ConvertGeneralToSnake(mapper, property.GetColumnName()));
                break;
            case IMutableKey primaryKey:
                primaryKey.SetName(ConvertKeyToSnake(mapper, primaryKey.GetName()));
                break;
            case IMutableForeignKey foreignKey:
                foreignKey.SetConstraintName(ConvertKeyToSnake(mapper, foreignKey.GetConstraintName()));
                break;
            case IMutableIndex indexKey:
                indexKey.SetName(ConvertKeyToSnake(mapper, indexKey.GetName()));
                break;
            default:
                throw new NotImplementedException("Unexpected type was provided to snake case converter");
        }
    }
    
    0 讨论(0)
  • 2021-01-05 01:43

    If I'm not missing something - you'd want some generic way of changing the naming convention for tables?

    The EF6 has the custom conventions feature - it's still not official version, but if it works for you, some links...

    http://entityframework.codeplex.com/wikipage?title=Custom%20Conventions

    In your case, you'd have to implement it for the class/Type I guess - e.g. (some pseudo code)...

    1) implement IConfigurationConvention<Type, EntityTypeConfiguration> (you can check the EF source for EntityConventionBase)

    2) In the Apply - change how the Table names are generated via configuration (ToTable()) - to something like .ToLowerCase()

    3) add to conventions...

    For example...

    public class SmallCapsEntitiesConfigurationConvention
        : IConfigurationConvention<Type, EntityTypeConfiguration>
    {
        public void Apply(Type memberInfo, Func<EntityTypeConfiguration> configuration)
        {
            configuration().ToTable(memberInfo.Name.ToLowerInvariant(), null);
        }
    }
    

    You can see one example here
    http://blog.cincura.net/233167-custom-conventions-in-entity-framework-6-helping-firebird/

    Otherwise, I have no idea about Npgsql / PostgreSQL - it did seem a bit 'raw' to me. But you can handle it on the EF side.

    0 讨论(0)
提交回复
热议问题