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

其他变化

有些人已从这篇文章中获取代码并将其扩展以适应其他映射场景:

谢谢浏览!

最新文章

  1. 写了cookie阻止通过输入地址直接访问下一个html,但是直接输入地址访问时,会闪一下下一个页面,怎么回事啊????、
  2. 解决为什么每次打开Eclipse新的workspace需要更新nexus-maven-repository-index问题
  3. Java编程思想学习笔记_6(并发)
  4. MPI初学-安装及OpenMPI函数说明
  5. 网络编码 GB2312、GBK与UTF-8的区别
  6. FireFly 服务端 Unity3D黑暗世界 客户端 问题
  7. linux mysql目录详解
  8. 系统spt_values表--生成时间方便left join
  9. 将MYSQL查询导出到文件
  10. [LintCode]快速幂(数论)
  11. 对于BFS的理解和部分例题(
  12. Python进阶3---python类型注解、functools
  13. MongoDB中的读写锁
  14. 斯坦福大学公开课机器学习:machine learning system design | data for machine learning(数据量很大时,学习算法表现比较好的原理)
  15. HTML 基础知识汇总(一)
  16. 使用 curses 函数库管理基于文本的屏幕
  17. 使用gitlab, jenkins搭建CI(持续集成)系统(3) -- 根据不同触发条件执行不同的构建任务
  18. codeforce 148D. Bag of mice[概率dp]
  19. 移动端H5页面解决软件键盘把页面顶起
  20. 八、Django之Models(译)

热门文章

  1. gitlab与jenkins结合构建持续集成
  2. javascript构造函数深度克隆递归
  3. Javase之多线程(2)
  4. CSS3/CSS之居中解析(水平+垂直居中、水平居中,垂直居中)
  5. 【XML】XML基本结构以及XML-Schema约束
  6. Window常用且通用快捷键
  7. [b0002] Hadoop HDFS cmd常用命令练手
  8. python中的随机模块random
  9. 海思屏幕HAL代码解析
  10. C学习笔记(10)--- 强制类型转换,错误处理,递归