Entity Framework 6 中如何获取 EntityTypeConfiguration 的 Edm 信息?(一)
1. 案例1 - 类型和表之间的EF代码优先映射
从EF6.1开始,有一种更简单的方法可以做到这一点。有关 详细信息,请参阅我的新EF6.1类型和表格之间的映射。
直接贴代码了
从EF6.1开始,有一种更简单的方法可以做到这一点。
有关 详细信息,请参阅我的新EF6.1类型和表格之间的映射。
实体框架包括MetadataWorkspace,可让您访问EF保持模型形状的元数据。问题是,映射部分 - 将实体类型映射到表和属性到列的位 - 不是公共的。
我们确实有一个工作项可以在即将发布的版本之一中公开,但在EF6和早期版本中,它仍然是内部版本。你问,为什么它是内部的...因为EF中的元数据API是一团糟。已经公开的元数据API很糟糕......但是映射更糟糕。虽然我们无法证明花时间重写整个API - 我们确实希望在公开之前将其清理一下。
然而,有一种方法可以获得信息 - 虽然非常黑客。对于Code First,您可以将编写的元数据写入EDMX格式(这是设计者使用的xml格式)。从那里,我们可以使用LINQ to XML来获取映射信息。
EF Designer怎么样?
这篇文章中的代码适用于Code First,但是它使用的是EF Designer使用的相同xml格式,因此您可以轻松地使用它来使用设计器创建的EDMX文件。
对象模型
我们将编写一个帮助程序库,以易于使用的格式返回映射信息。我们将使用以下对象模型来表示元数据。
|
1
2
3
4
五
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
三十
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
|
using System;using System.Collections.Generic;using System.Reflection;namespace MappingDemo{ /// <summary> /// Represents the mapping of an entitiy type to one or mode tables in the database /// /// A single entity can be mapped to more than one table when 'Entity Splitting' is used /// Entity Splitting involves mapping different properties from the same type to different tables /// See http://msdn.com/data/jj591617#2.7 for more details /// </summary> public class TypeMapping { /// <summary> /// The type of the entity from the model /// </summary> public Type EntityType { get; set; } /// <summary> /// The table(s) that the entity is mapped to /// </summary> public List<TableMapping> TableMappings { get; set; } } /// <summary> /// Represents the mapping of an entity to a table in the database /// </summary> public class TableMapping { /// <summary> /// The name of the table the entity is mapped to /// </summary> public string TableName { get; set; } /// <summary> /// Details of the property-to-column mapping /// </summary> public List<PropertyMapping> PropertyMappings { get; set; } } /// <summary> /// Represents the mapping of a property to a column in the database /// </summary> public class PropertyMapping { /// <summary> /// The property from the entity type /// </summary> public PropertyInfo Property { get; set; } /// <summary> /// The column that property is mapped to /// </summary> public string ColumnName { get; set; } }} |
填充对象模型
现在我们有了一个对象模型,我们可以编写一些hacky 不那么直观的有趣代码来填充它。
|
1
2
3
4
五
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
三十
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
|
using System;using System.Collections.Generic;using System.Data.Entity;using System.Data.Metadata.Edm;using System.Data.Entity.Infrastructure;using System.IO;using System.Linq;using System.Xml;using System.Xml.Linq;namespace MappingDemo{ /// <summary> /// Represents that mapping between entity types and tables in an EF model /// </summary> public class EfMapping { /// <summary> /// Mapping information for each entity type in the model /// </summary> public List<TypeMapping> TypeMappings { get; set; } /// <summary> /// Initializes an instance of the EfMapping class /// </summary> /// <param name="db">The context to get the mapping from</param> public EfMapping(DbContext db) { this.TypeMappings = new List<TypeMapping>(); var metadata = ((IObjectContextAdapter)db).ObjectContext.MetadataWorkspace; // Conceptual part of the model has info about the shape of our entity classes var conceptualContainer = metadata.GetItems<EntityContainer>(DataSpace.CSpace).Single(); // Storage part of the model has info about the shape of our tables var storeContainer = metadata.GetItems<EntityContainer>(DataSpace.SSpace).Single(); // Object part of the model that contains info about the actual CLR types var objectItemCollection = ((ObjectItemCollection)metadata.GetItemCollection(DataSpace.OSpace)); // Mapping part of model is not public, so we need to write to xml and use 'LINQ to XML' var edmx = GetEdmx(db); // Loop thru each entity type in the model foreach (var set in conceptualContainer.BaseEntitySets.OfType<EntitySet>()) { var typeMapping = new TypeMapping { TableMappings = new List<TableMapping>() }; this.TypeMappings.Add(typeMapping); // Get the CLR type of the entity typeMapping.EntityType = metadata .GetItems<EntityType>(DataSpace.OSpace) .Select(e => objectItemCollection.GetClrType(e)) .Single(e => e.FullName == set.ElementType.FullName); // Get the mapping fragments for this type // (types may have mutliple fragments if 'Entity Splitting' is used) var mappingFragments = edmx .Descendants() .Single(e => e.Name.LocalName == "EntityTypeMapping" && e.Attribute("TypeName").Value == set.ElementType.FullName) .Descendants() .Where(e => e.Name.LocalName == "MappingFragment"); foreach (var mapping in mappingFragments) { var tableMapping = new TableMapping { PropertyMappings = new List<PropertyMapping>() }; typeMapping.TableMappings.Add(tableMapping); // Find the table that this fragment maps to var storeset = mapping.Attribute("StoreEntitySet").Value; tableMapping.TableName = (string)storeContainer .BaseEntitySets.OfType<EntitySet>() .Single(s => s.Name == storeset) .MetadataProperties["Table"].Value; // Find the property-to-column mappings var propertyMappings = mapping .Descendants() .Where(e => e.Name.LocalName == "ScalarProperty"); foreach (var propertyMapping in propertyMappings) { // Find the property and column being mapped var propertyName = propertyMapping.Attribute("Name").Value; var columnName = propertyMapping.Attribute("ColumnName").Value; tableMapping.PropertyMappings.Add(new PropertyMapping { Property = typeMapping.EntityType.GetProperty(propertyName), ColumnName = columnName }); } } } } private static XDocument GetEdmx(DbContext db) { XDocument doc; using (var memoryStream = new MemoryStream()) { using (var xmlWriter = XmlWriter.Create( memoryStream, new XmlWriterSettings { Indent = true })) { EdmxWriter.WriteEdmx(db, xmlWriter); } memoryStream.Position = 0; doc = XDocument.Load(memoryStream); } return doc; } }} |
测试出来
现在让我们通过它运行Code First模型来测试我们的代码。您会注意到我已经包含Entity Splitting来演示为什么我们需要一个实体映射到的表的List。
|
1
2
3
4
五
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
三十
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
|
using System;using System.Collections.Generic;using System.Data.Entity;namespace MappingDemo{ class Program { static void Main(string[] args) { Database.SetInitializer(new DropCreateDatabaseAlways<BloggingContext>()); using (var db = new BloggingContext()) { var mappingInfo = new EfMapping(db); foreach (var item in mappingInfo.TypeMappings) { Console.WriteLine(item.EntityType.FullName); foreach (var table in item.TableMappings) { Console.WriteLine(" => {0}", table.TableName); foreach (var column in table.PropertyMappings) { Console.WriteLine( " {0} => {1}", column.Property.Name, column.ColumnName); } } } } } } public class BloggingContext : DbContext { public DbSet<Blog> Blogs { get; set; } public DbSet<Post> Posts { get; set; } protected override void OnModelCreating(DbModelBuilder modelBuilder) { // Rename a column so that not all property/column names match modelBuilder.Entity<Post>() .Property(p => p.PostId) .HasColumnName("post_id"); // Perform 'Entity Splitting' on the Blog entity to test // mapping a single entity to multiple tables modelBuilder.Entity<Blog>() .Map(m => { m.Properties(b => new { b.Name, b.Url }); m.ToTable("Blog_Details"); }) .Map(m => { m.Properties(b => new { b.Image }); m.ToTable("Blog_Photo"); }); } } public class Blog { public int BlogId { get; set; } public string Name { get; set; } public string Url { get; set; } public byte[] Image { get; set; } public virtual List<Post> Posts { get; set; } } public class Post { public int PostId { get; set; } public string Title { get; set; } public string Content { get; set; } public int BlogId { get; set; } public virtual Blog Blog { get; set; } }} |
运行我们的应用程序会产生以下输出。
![]()
有关
2. 案例2(已经在 EF6.0.0 中测试)
增加 DbContextExtensions.cs 类。
代码如下:
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Data.Entity.Core.Mapping;
using System.Data.Entity.Core.Metadata.Edm;
using System.Data.Entity.Core.Objects;
using System.Data.Entity.Infrastructure;
using System.Linq; namespace Sino.SnapshotComparsion.Data
{
/// <summary>
/// Entity Framework 6 中的 DbContext 扩展
/// </summary>
public static class DbContextExtensions
{
private readonly static Dictionary<string, EntitySetBase> _mappingCache = new Dictionary<string, EntitySetBase>(); private static EntitySetBase GetEntitySet(DbContext dbContext, Type type)
{
if (dbContext == null)
{
throw new ArgumentNullException("dbContext");
}
if (type == null)
{
throw new ArgumentNullException("type");
}
if (_mappingCache.ContainsKey(type.FullName))
{
return _mappingCache[type.FullName];
}
string baseTypeName = type.BaseType.Name;
string typeName = type.Name; ObjectContext octx = ((IObjectContextAdapter)dbContext).ObjectContext;
EntitySetBase es = octx.MetadataWorkspace
.GetItemCollection(DataSpace.SSpace)
.GetItems<EntityContainer>()
.SelectMany(c => c.BaseEntitySets
.Where(e => e.Name == typeName
|| e.Name == baseTypeName))
.FirstOrDefault();
if (es == null)
{
throw new ArgumentException("Entity type not found in GetEntitySet", typeName);
}
_mappingCache.Add(type.FullName, es);
return es;
} public static string GetTableName(DbContext context, Type type)
{
var metadata = ((IObjectContextAdapter)context).ObjectContext.MetadataWorkspace; // Get the part of the model that contains info about the actual CLR types
var objectItemCollection = ((ObjectItemCollection)metadata.GetItemCollection(DataSpace.OSpace)); // Get the entity type from the model that maps to the CLR type
var entityType = metadata
.GetItems<EntityType>(DataSpace.OSpace)
.Single(e => objectItemCollection.GetClrType(e) == type); // Get the entity set that uses this entity type
var entitySet = metadata
.GetItems<EntityContainer>(DataSpace.CSpace)
.Single()
.EntitySets
.Single(s => s.ElementType.Name == entityType.Name); // Find the mapping between conceptual and storage model for this entity set
var mapping = metadata.GetItems<EntityContainerMapping>(DataSpace.CSSpace)
.Single()
.EntitySetMappings
.Single(s => s.EntitySet == entitySet); // Find the storage entity set (table) that the entity is mapped
var table = mapping
.EntityTypeMappings.Single()
.Fragments.Single()
.StoreEntitySet; // Return the table name from the storage entity set
return (string)table.MetadataProperties["Table"].Value ?? table.Name;
} /// <summary>
/// 获取映射的表的名称
/// </summary>
/// <typeparam name="TEntity">EF 实体的类型</typeparam>
/// <param name="dbContext">EF的 DbContext 上下文</param>
/// <returns></returns>
public static string[] GetPrimaryKeyName<TEntity>(this DbContext dbContext)
{
return GetPrimaryKeyName(dbContext, typeof(TEntity));
} /// <summary>
/// 获取映射的表的主键名称集合
/// </summary>
/// <param name="dbContext">EF的 DbContext 上下文</param>
/// <param name="type">EF 实体的类型</param>
/// <returns></returns>
public static string[] GetPrimaryKeyName(this DbContext dbContext, Type type)
{
EntitySetBase es = GetEntitySet(dbContext, type);
return es.ElementType.KeyProperties.Select(c => c.Name).ToArray();
} /// <summary>
/// 获取映射的表的主键名称集合的字符串形式
/// </summary>
/// <param name="dbContext">EF的 DbContext 上下文</param>
/// <param name="type">EF 实体的类型</param>
/// <returns></returns>
public static string GetPrimaryKeyNameString(this DbContext dbContext, Type type)
{
return string.Join(",", GetPrimaryKeyName(dbContext, type));
} /// <summary>
/// 获取映射的表的主键名称集合的字符串形式
/// </summary>
/// <typeparam name="TEntity">EF 实体的类型</typeparam>
/// <param name="dbContext">EF的 DbContext 上下文</param>
/// <returns></returns>
public static string GetPrimaryKeyNameString<TEntity>(this DbContext dbContext)
{
return GetPrimaryKeyNameString(dbContext, typeof(TEntity));
}
}
}
3. 案例3 - EF6.1类型和表之间的映射
前段时间我在博客上写了一篇关于如何找到给定实体映射到的表的文章。该帖子中的解决方案解决了访问此信息的API是内部的问题。在EF6.1中,我们将映射API公之于众,因此它现在变得更加容易。
此代码的另一个优点是它适用于Code First和EF Designer模型。
代码
不用多说,这里是找到给定CLR类型的表名的代码。我已经包含了一个完整的控制台应用程序列表,该应用程序演示了代码的运行情况,但是如果您需要的话,您可以获取GetTableName方法。
|
1
2
3
4
五
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
三十
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
|
using System;using System.Collections.Generic;using System.Data.Entity;using System.Data.Entity.Core.Mapping;using System.Data.Entity.Core.Metadata.Edm;using System.Data.Entity.Infrastructure;using System.Linq;namespace MappingDemo{ class Program { static void Main(string[] args) { using (var db = new BloggingContext()) { Console.WriteLine("Blog maps to: {0}", GetTableName(typeof(Blog), db)); Console.WriteLine("Post maps to: {0}", GetTableName(typeof(Post), db)); } } public static string GetTableName(Type type, DbContext context) { var metadata = ((IObjectContextAdapter)context).ObjectContext.MetadataWorkspace; // Get the part of the model that contains info about the actual CLR types var objectItemCollection = ((ObjectItemCollection)metadata.GetItemCollection(DataSpace.OSpace)); // Get the entity type from the model that maps to the CLR type var entityType = metadata .GetItems<EntityType>(DataSpace.OSpace) .Single(e => objectItemCollection.GetClrType(e) == type); // Get the entity set that uses this entity type var entitySet = metadata .GetItems<EntityContainer>(DataSpace.CSpace) .Single() .EntitySets .Single(s => s.ElementType.Name == entityType.Name); // Find the mapping between conceptual and storage model for this entity set var mapping = metadata.GetItems<EntityContainerMapping>(DataSpace.CSSpace) .Single() .EntitySetMappings .Single(s => s.EntitySet == entitySet); // Find the storage entity set (table) that the entity is mapped var table = mapping .EntityTypeMappings.Single() .Fragments.Single() .StoreEntitySet; // Return the table name from the storage entity set return (string)table.MetadataProperties["Table"].Value ?? table.Name; } } public class BloggingContext : DbContext { public DbSet<Blog> Blogs { get; set; } public DbSet<Post> Posts { get; set; } protected override void OnModelCreating(DbModelBuilder modelBuilder) { modelBuilder.Entity<Blog>().ToTable("t_blog"); modelBuilder.Entity<Post>().ToTable("t_post"); } } public class Blog { public int BlogId { get; set; } public string Url { get; set; } public List<Post> Posts { get; set; } } public class Post { public int PostId { get; set; } public string Title { get; set; } public string Body { get; set; } public int BlogId { get; set; } public Blog Blog { get; set; } }} |
高级映射的调整
EF支持称为“实体拆分”的高级映射模式。在此模式中,您可以在多个表之间拆分实体的属性。以下是实体拆分Post类的Fluent API调用示例。
|
1
2
3
4
五
6
7
8
9
10
11
|
modelBuilder.Entity<Post>() .Map(m => { m.Properties(p => new { p.PostId, p.Title, p.BlogId }); m.ToTable("t_post"); }) .Map(m => { m.Properties(p => new { p.PostId, p.Body }); m.ToTable("t_post_body"); }); |
为了处理这个问题,我们可以更新GetTableName方法以返回类型映射到的表的可枚举数。对前一个实现的唯一更改是从映射片段中查找表名的最后两个代码块。
|
1
2
3
4
五
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
三十
31
32
33
|
public static IEnumerable<string> GetTableName(Type type, DbContext context){ var metadata = ((IObjectContextAdapter)context).ObjectContext.MetadataWorkspace; // Get the part of the model that contains info about the actual CLR types var objectItemCollection = ((ObjectItemCollection)metadata.GetItemCollection(DataSpace.OSpace)); // Get the entity type from the model that maps to the CLR type var entityType = metadata .GetItems<EntityType>(DataSpace.OSpace) .Single(e => objectItemCollection.GetClrType(e) == type); // Get the entity set that uses this entity type var entitySet = metadata .GetItems<EntityContainer>(DataSpace.CSpace) .Single() .EntitySets .Single(s => s.ElementType.Name == entityType.Name); // Find the mapping between conceptual and storage model for this entity set var mapping = metadata.GetItems<EntityContainerMapping>(DataSpace.CSSpace) .Single() .EntitySetMappings .Single(s => s.EntitySet == entitySet); // Find the storage entity sets (tables) that the entity is mapped var tables = mapping .EntityTypeMappings.Single() .Fragments; // Return the table name from the storage entity set return tables.Select(f => (string)f.StoreEntitySet.MetadataProperties["Table"].Value ?? f.StoreEntitySet.Name);} |
其他变化
有些人已从这篇文章中获取代码并将其扩展以适应其他映射场景:
- 属性和列之间的映射 - 本杰明马蒂
- 在模型中使用继承进行映射 --Brian Lowry
有关
谢谢浏览!
Entity Framework 6 中如何获取 EntityTypeConfiguration 的 Edm 信息?(一)的更多相关文章
- Entity Framework 6 中如何获取 EntityTypeConfiguration 的 Edm 信息?(五)
直接贴代码了: NewsInfo 实体类: public class NewsInfo { public int NewsInfoId { get; set; } public string News ...
- Entity Framework 6 中如何获取 EntityTypeConfiguration 的 Edm 信息?(四)
经过上一篇,里面有测试代码,循环60万次,耗时14秒.本次我们增加缓存来优化它. DbContextExtensions.cs using System; using System.Collectio ...
- Entity Framework 6 中如何获取 EntityTypeConfiguration 的 Edm 信息?(三)
接着上一篇,我们继续来优化. 直接贴代码了: LambdaHelper.cs using System; using System.Collections.Generic; using System. ...
- Entity Framework 6 中如何获取 EntityTypeConfiguration 的 Edm 信息?(二)
接着上一篇 直接贴代码了: using System; using System.Collections.Generic; using System.Data.Entity; using System ...
- 浅析Entity Framework Core中的并发处理
前言 Entity Framework Core 2.0更新也已经有一段时间了,园子里也有不少的文章.. 本文主要是浅析一下Entity Framework Core的并发处理方式. 1.常见的并发处 ...
- 在Entity Framework 7中进行数据迁移
(此文章同时发表在本人微信公众号“dotNET每日精华文章”,欢迎右边二维码来关注.) 题记:虽然EF7重新设计了Entity Framework,不过也还是能够支持数据迁移的. Entity Fra ...
- [Programming Entity Framework] 第3章 查询实体数据模型(EDM)(一)
http://www.cnblogs.com/sansi/archive/2012/10/18/2729337.html Programming Entity Framework 第二版翻译索引 你可 ...
- 如何处理Entity Framework / Entity Framework Core中的DbUpdateConcurrencyException异常(转载)
1. Concurrency的作用 场景有个修改用户的页面功能,我们有一条数据User, ID是1的这个User的年龄是20, 性别是female(数据库中的原始数据)正确的该User的年龄是25, ...
- Entity Framework添加记录时获取自增ID值
与Entity Framework相伴的日子痛并快乐着.今天和大家分享一下一个快乐,两个痛苦. 先说快乐的吧.Entity Framework在将数据插入数据库时,如果主键字段是自增标识列,会将该自增 ...
随机推荐
- SpringBoot2使用Jetty容器(替换默认Tomcat)
https://blog.csdn.net/hanchao5272/article/details/99649252 Jetty和tomcat的比较 Tomcat和Jetty都是一种Servlet ...
- C#数组1
using System; namespace ConsoleApp3 { class Program { static void Main(string[] args) { , , , , , }; ...
- frigate_TUNNEL
#coding=utf-8 Result=open('result.txt',"w") FileTunnel = open('tunnel.txt').readlines() Ne ...
- JS基础语法---数组基础知识总结
数组: 存储一组有序的数据 数组的作用: 一次性存储多个数据 数组的定义方式: 1.构造函数定义数组: var 数组名=new Array(); 2.字面量方式定义数组: var 数组名=[]; ...
- 关于 SONY WF1000XM3 在 Windows 10 下蓝牙连接只有 Handfree 没有 Stereo 模式
应该是驱动适配问题,目前粗暴的解决方案貌似下载安装一个 Intel APTX 驱动就可以了: https://www.dell.com/support/home/cn/zh/cndhs1/driver ...
- JavaScript初探 三 (学习js数组)
JavaScript初探 (三) JavaScript数组 定义 创建数组 var 数组名 = [元素0,元素1,元素2,--] ; var arr = ["Huawei",&qu ...
- Android 蓝牙开发(1)
普通蓝牙设备官方文档 Android 平台包含蓝牙网络堆栈支持,凭借此支持,设备能以无线方式与其他蓝牙设备交换数据.应用框架提供了通过 Android Bluetooth API 访问蓝牙功能的途径. ...
- .net core从版本 3.0 迁移到 3.1引发的BUG
前几天微软的.net core3.1发布后,随把visual studio 2019升级到16.4.1版本并把项目进行框架升级.升级后的项目在IdentityServer4授权后在360安全浏览器竟然 ...
- mysql简单的sql操作语句
一,常用.简单的SQL操作语句 1.数据库操作: 1)创建数据库: create database database_name: 创建并设置字符编码 create database database_ ...
- UEFI Install CentOS 7
bios必须设置u盘为第一启动项 编辑E:\EFI\BOOT\grub.cfg中所有inst.stage2=hd:LABEL=*与卷标名称一致(区分大小写)(linux系统写入镜像无需修改) inst ...