Ef Core 3 migration: RelationalEntityTypeAnnotations

c# entity-framework-core entity-framework-core-3.0

Question

How to get annotations (e.g. table name, schema name) for IEntityType ?

Previously we had

var relationalEntityTypeAnnotations = entityType.Relational();
var schema = relationalEntityTypeAnnotations.Schema;
var tableName = relationalEntityTypeAnnotations.TableName;

But now relationalEntityTypeAnnotations class (and Relational extension) were removed.

The documnetation advice:

The Relational:TypeMapping annotation is now just TypeMapping Tracking Issue #9913

This change is introduced in EF Core 3.0-preview 2.

Old behavior

The annotation name for type mapping annotations was "Relational:TypeMapping".

New behavior

The annotation name for type mapping annotations is now "TypeMapping".

Why

Type mappings are now used for more than just relational database providers.

Mitigations

This will only break applications that access the type mapping directly as an annotation, which isn't common. The most appropriate action to fix is to use API surface to access type mappings rather than using the annotation directly.

but there are no method entityType.TypeMapping or something like this...

1
0
9/25/2019 7:14:37 PM

Accepted Answer

Update (EF Core 3.x): Starting with EF Core 3.0, the metadata API has changed again - Relational() extensions have been removed, and properties have been replaced with Get and Set extension methods, so now the code looks like this:

var entityType = dbContext.Model.FindEntityType(clrEntityType);

// Table info 
var tableName = entityType.GetTableName();
var tableSchema = entityType.GetSchema();

// Column info 
foreach (var property in entityType.GetProperties())
{
    var columnName = property.GetColumnName();
    var columnType = property.GetColumnType();
};

Update (EF Core 2.x): Starting with EF Core 2.0, the things have changed, so the original answer does not apply anymore. Now EF Core builds separate model for each database type, so the code is much simpler and uses directly the Relational() extensions:

var entityType = dbContext.Model.FindEntityType(clrEntityType);

// Table info 
var tableName = entityType.Relational().TableName;
var tableSchema = entityType.Relational().Schema;

// Column info 
foreach (var property in entityType.GetProperties())
{
    var columnName = property.Relational().ColumnName;
    var columnType = property.Relational().ColumnType;
};

Original answer (EF Core 1.x):

Getting the access to the associated metadata is much easier in EF Core compared to EF - you start from DbContext.Model property to get IModel, use GetEntityTypes or FindEntityType to get IEntityType, then GetProperties or FindProperty to get IProperty etc.

However the problem is that EF Core allows you to use different setting fro different target database. In order to get the attributes corresponding to the current database used by the context, you need to get access to the IRelationalDatabaseProviderServices and use AnnotationProvider and TypeMapper properties to get the information needed.

Here is an example:

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Internal;
using Microsoft.EntityFrameworkCore.Storage;

public class DbColumnInfo
{
    public string Name;
    public string Type;
}

public static class RelationalDbHelpers
{
    public static IEnumerable<DbColumnInfo> GetDbColums(this DbContext dbContext, Type clrEntityType)
    {
        var dbServices = dbContext.GetService<IDbContextServices>();
        var relationalDbServices = dbServices.DatabaseProviderServices as IRelationalDatabaseProviderServices;
        var annotationProvider = relationalDbServices.AnnotationProvider;
        var typeMapper = relationalDbServices.TypeMapper;

        var entityType = dbContext.Model.FindEntityType(clrEntityType);

        // Not needed here, just an example 
        var tableMap = annotationProvider.For(entityType);
        var tableName = tableMap.TableName;
        var tableSchema = tableMap.Schema;

        return from property in entityType.GetProperties()
               let columnMap = annotationProvider.For(property)
               let columnTypeMap = typeMapper.FindMapping(property)
               select new DbColumnInfo
               {
                   Name = columnMap.ColumnName,
                   Type = columnTypeMap.StoreType
               };
    }
}
28
9/25/2019 9:09:00 PM

Popular Answer

For those who arrived here, but are not using .NET CORE, like me. try this:

public partial class MyDbContext : System.Data.Entity.DbContext
{
    public string GetTableName(Type entityType)
    {
        var sql = Set(entityType).ToString();
        var regex = new Regex(@"FROM \[dbo\]\.\[(?<table>.*)\] AS");
        var match = regex.Match(sql);
        return match.Groups["table"].Value;
    }

    public string[] GetColumnName(Type entityType)
    {
        var strs = new List<string>();
        var sql = Set(entityType).ToString();
        var regex = new Regex(@"\[Extent1\]\.\[(?<columnName>.*)\] AS");
        var matches = regex.Matches(sql);
        foreach (Match item in matches)
        {
            var name = item.Groups["columnName"].Value;
            strs.Add(name);
        }
        return strs.ToArray();
    }
}

maybe redundante, but it saves time.

antonio



Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow