更新(EF Core 3.x):从 EF Core 3.0 开始,元数据 API 再次发生了变化 - Relational()
扩展已被移除,属性被替换为 Get
和 Set
扩展方法,所以现在代码看起来像这样:
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();
};
更新(EF Core 2.x):从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
,使用 GetEntityTypes
或 FindEntityType
获取 IEntityType
,然后使用 GetProperties
或 FindProperty
获取 IProperty
等。
然而,问题在于 EF Core 允许您针对不同的目标数据库使用不同的设置。为了获取与上下文使用的当前数据库相对应的属性,您需要访问 IRelationalDatabaseProviderServices
并使用 AnnotationProvider
和 TypeMapper
属性获取所需的信息。
这里是一个例子:
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
};
}
}
对于 EF Core 5,您需要使用接受 StoreObjectIdentifier 的重载:GetColumnName(IProperty, StoreObjectIdentifier)。
EF 5 的更新:
var entityType = dbContext.Model.FindEntityType(clrEntityType);
var schema = entityType.GetSchema();
var tableName = entityType.GetTableName();
var storeObjectIdentifier = StoreObjectIdentifier.Table(tableName, schema);
var columnName = entityType.FindProperty(propertyName).GetColumnName(storeObjectIdentifier);
context.Model.GetRelationalModel()
开始,则无需处理多个类型保存到同一表的情况,例如拥有的类型或类型层次结构。 - Jeremy LakemanentityType
是什么?请在回答中添加您如何获得它的信息。 - Gert Arnoldpublic 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)")
public static List<ColumnInfo> GetAllColumns(this DbContext db, Type TableName)
{
var entityType = db.Model.FindEntityType(TableName);
var properties = entityType.GetProperties();
List<ColumnInfo> columns = new List<ColumnInfo>(properties.Count());
foreach (var property in properties)
columns.Add(new ColumnInfo(property.GetColumnName(StoreObjectIdentifier.SqlQuery(entityType)), property.GetColumnType()));
return columns;
}
public class ColumnInfo
{
public string Name;
public string Type;
public ColumnInfo(string Name, string Type)
{
this.Name = Name;
this.Type = Type;
}
}
您还可以将string Type
替换为SqlDbType Type
,因此foreach行将如下所示:
columns.Add(new ColumnInfo(
property.GetColumnName(StoreObjectIdentifier.SqlQuery(entityType)),
Enum.Parse<SqlDbType>( property.GetColumnType().Slice(0,"(",true), true)));
在我的函数 Slice
中,有一段必要的代码:
public static string Slice(this string s, int Start, string EndsWith, bool AlwaysReturnString = false)
{
var end =s.LastIndexOf(EndsWith);
if (end < 0) return AlwaysReturnString? s : null;
if (Start > end) throw new ArgumentException($"start ({Start}) is be bigger than end ({end})");
return s.Slice(Start, end);
}
public static int IndexOfEnd(this string s, string s2)
{
if (s == null)
if (s2.Length == 0)
return 0;
int i = s.IndexOf(s2);
return i == -1 ? -1 : i + s2.Length;
}
public static string Slice(this string s, int Start = 0, int End = Int32.MaxValue)
{
if (Start < 0) throw new ArgumentOutOfRangeException($"Start is {Start}");
if (Start > End) throw new ArgumentException($"start ({Start}) is be bigger than end ({End})");
if (End > s.Length) End = s.Length;
return s.Substring(Start, End - Start);
}
对于那些来到这里但没有使用.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
public IEnumerable<string> GetColumnNames<T>() where T : class
{
var storeObjectIdentifier = StoreObjectIdentifier.Table(Set<T>().EntityType.GetTableName(), Set<T>().EntityType.GetSchema());
var columns = Set<T>().EntityType
.GetProperties()
.Select(x => x.GetColumnName(storeObjectIdentifier))
.ToList();
return columns;
}
dbContext.Database.GetDbConnection().Database
。(3)不知道,最终从relationalDbServices.InvariantName
中推导出来。 - Ivan Stoev