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在将数据插入数据库时,如果主键字段是自增标识列,会将该自增 ...
随机推荐
- kfifo
kfifo 的一些伪代码 kfifo_len() out = LOAD fifo->out smp_rmb() len = LOAD fifo->in - out kfifo_in() k ...
- requests库的使用、安装及方法的简单介绍
requests库的使用.安装及方法的简单介绍 1.requests库的概述 requests库是一个简洁且简单的处理HTTP请求的第三方库,是公认的最好获得第三方信息的库. requests库更多信 ...
- Z从壹开始前后端分离【 .NET Core2.2/3.0 +Vue2.0 】框架之八 || API项目整体搭建 6.3 异步泛型仓储+依赖注入初探
本文梯子 本文3.0版本文章 回顾 1.Sqlsugar 的使用 2.修改数据连接字符串 今天要完成的浅紫色部分 一.设计仓储基类接口——IBaseRepository.cs 二.将其他的仓储接口,继 ...
- 仅支持基本增删改查的学生自制php操作mysql的工具类 DB.class.php (学生笔记)
<?php class DB{ //主机地址 var $host; //用户名 var $username; //密码 var $password; //数据库名 var $dbname; // ...
- 用Python复制文件的9个方法
Python 中有许多"开盖即食"的模块(比如 os,subprocess 和 shutil)以支持文件 I/O 操作.在这篇文章中,你将会看到一些用 Python 实现文件复制的 ...
- .net core使用百度webupload上传图片
后端代码: /// <summary> /// 图片上传,上传路径: "/Uploads/Images/" + folder + "/" + sav ...
- postman---postman发送请求
前面简单的介绍了Postman的页面介绍和功能介绍,今天我们一起学习postman如何发送请求 发送请求 我们介绍过http协议有多种请求方式,各个请求方法都代表不同的结果.例如,GET使您可以从服务 ...
- 01day-webpack
<!-- .sass后缀的文件名 比较老了 现在它的后缀名是.scss 其实他们是同一个东西 只是 后缀名发生了变化 以 .sass写的文件的内容是 他没有括号 没有分号 有点怪 它跟新为了.s ...
- 第十六周博客作业 <西北师范大学| 周安伟>
第十六周作业 助教博客链接https://home.cnblogs.com/u/zaw-315/ 作业要求链接https://www.cnblogs.com/nwnu-daizh/p/10980707 ...
- Drop_out--防止过拟合
With probability keep_prob, outputs the input element scaled up by 1 / keep_prob, otherwise outputs ...