如何从Entity Framework Core中的DbContext获取列名和相应的数据库类型

时间:2017-02-25 11:10:58

标签: c# entity-framework .net-core entity-framework-core

假设我有这张表:

enter image description here

如何从Entity Framework Core中的#include <Wire.h> void setup() { Wire.begin(8); // join i2c bus with address #8 Wire.onReceive(receiveEvent); // register event Serial.begin(115200); // start serial for output } void loop() { delay(100); } // function that executes whenever data is received from master // this function is registered as an event, see setup() void receiveEvent(int howMany) { Serial.println("Received.."); /* while (1 < Wire.available()) { // loop through all but the last char c = Wire.read(); // receive byte as a character Serial.print(c); // print the character } */ int x = Wire.read(); // receive byte as an integer Serial.println(x); // print the integer } 获取列名和数据库数据类型?

提示

  1. 名称为clg#的列由EF Core Scaffold工具转换为clg1所以我需要真正的列名而不是当前的EF名称

  2. 我需要数据库类型,而不是clrType,当然必须是跨平台。也许我会更改数据库,因此该方法也必须正常工作。

  3. 期望的结果:

    DbContext

    有人能提供解决方案吗?

3 个答案:

答案 0 :(得分:14)

更新:从EF Core 2.0开始,情况发生了变化,原来的答案不再适用了。现在EF Core为每种数据库类型构建单独的模型,因此代码更简单并直接使用Relational()扩展名:

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;
};

原始答案(EF Core 1.x):

与EF相比,EF Core中访问相关元数据要容易得多 - 您从DbContext.Model属性开始获取IModel,使用GetEntityTypesFindEntityType获取IEntityType,然后GetPropertiesFindProperty获取IProperty等。

但问题是EF Core允许您对不同的目标数据库使用不同的设置。为了获取与上下文使用的当前数据库相对应的属性,您需要访问IRelationalDatabaseProviderServices并使用AnnotationProviderTypeMapper属性来获取所需信息。

以下是一个例子:

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
               };
    }
}

答案 1 :(得分:1)

对于那些来到这里但没有使用.NET CORE的人,就像我一样。 试试这个:

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();
    }
}

也许是多余的,但可以节省时间。

antonio

答案 2 :(得分:0)

基于EFC3.1的答案,我创建了此帮助程序,用于将所有表名和列名存储到第一次使用时填充的单例字典中,因此代码不必遍历所有内容。我们将其用于NPGSQL批量复制操作,它似乎可以正常工作。该版本不会从实体类中过滤掉任何属性,因此在进行列名列表/字符串时,请注意字段的顺序。但是再一次,据我了解,您将仅获得在上下文中映射的属性,因此一切正常。

助手

public class ContextHelper
  {
    private readonly ILogger<ContextHelper> logger;
    private readonly ApplicationDbContext context;

    private static Dictionary<Type, string> tableNames = new Dictionary<Type, string>(30);

    private Dictionary<Type, Dictionary<string, string>> columnNames = new Dictionary<Type, Dictionary<string, string>>(30);

    public ContextHelper(ILogger<ContextHelper> logger, ApplicationDbContext context)
    {
      this.logger = logger;
      this.context = context;

      PopulateTableNames();
      PopulateColumnNames();
    }

    private void PopulateTableNames()
    {
      logger.LogInformation("Populating table names in context helper");

      foreach (var entityType in context.Model.GetEntityTypes())
      {
        tableNames.Add(entityType.ClrType, entityType.GetTableName());
      }
    }

    private void PopulateColumnNames()
    {
      logger.LogInformation("Populating column names in context helper");

      foreach (var entityType in context.Model.GetEntityTypes())
      {
        var clrType = entityType.ClrType;

        if (!columnNames.ContainsKey(clrType))
        {
          columnNames.Add(clrType, new Dictionary<string, string>(30));
        }

        foreach (var property in entityType.GetProperties())
        {
          columnNames[clrType].Add(property.Name, property.GetColumnName());
        }
      }
    }

    public string GetTableName<T>()
    {
      return context.Model.FindEntityType(typeof(T)).GetTableName();
    }

    public string GetColumnName<T>(string propertyName)
    {
      return columnNames[typeof(T)][propertyName];
    }

    public List<string> GetColumnNames<T>()
    {
      return columnNames[typeof(T)].Select(x => x.Value).ToList();
    }
  }

启动注册

services.AddSingleton<ContextHelper>();

用法,类似的话

var columnNames = contextHelper.GetColumnNames<OvenEventLog>().Where(x=>x != contextHelper.GetColumnName<OvenEventLog>(nameof(OvenEventLog.IdLog)));
var separatedCN = string.Join(", ", columnNames);

using (var writer = conn.BeginBinaryImport(
    $"COPY {contextHelper.GetTableName<OvenEventLog>()} ({separatedCN}) FROM STDIN (FORMAT BINARY)")