- 浏览: 83762 次
文章分类
- 全部博客 (136)
- 我的技术资料收集 (98)
- 具体技术 (1)
- 的技术资料收集 (4)
- All Articles (1)
- 机器学习 Machine Learning (1)
- 网络编程 (1)
- java (2)
- ava (1)
- 零散技术 (1)
- C# (3)
- 技术资料收集 (1)
- CQRS (1)
- 数据库技术(MS SQL) (1)
- .Net微观世界 (1)
- Oracle SQL学习之路 (1)
- C/C++ (1)
- JS/JQ (1)
- Js封装的插件/实例/方法 (2)
- 敏捷个人 (2)
- Javascript (1)
- 程序设计---设计模式 (1)
- Bug (1)
- 未知分类 (1)
- 程序设计 (1)
- Sharepoint (1)
- Computer Graphic (1)
- IT产品 (1)
- [06]JS/jQuery (1)
- [07]Web开发 (1)
- .NET Solution (1)
- Android (3)
- 机器学习 (1)
- 系统框架设计 (1)
- Others (1)
- 算法 (1)
- 基于Oracle Logminer数据同步 (1)
- 网页设计 (1)
- 原创翻译 (1)
- EXTJS (1)
- Jqgrid (1)
- 云计算 (1)
最新评论
〇、目录
一、前言
二、实体映射
(一) DataAnnotation
(二) Fluent API
(三) 映射代码示例
三、数据迁移
四、代码重构
五、源码下载
六、系列导航
一、前言
经过EF的《第一篇》,我们已经把数据访问层基本搭建起来了,但并没有涉及实体关系,实体关系对于一个数据库系统来说至关重要。
二、实体映射
实体与数据库的映射可以通过DataAnnotation与FluentAPI两种方式来进行映射:
(一) DataAnnotation
DataAnnotation 特性由.NET 3.5中引进,给.NET中的类提供了一种添加验证的方式。DataAnnotation由命名空间System.ComponentModel.DataAnnotations提供。下面列举实体模型中常用的DataAnnotation特性:
- KeyAttribute:对应数据库中的主键
- RequiredAttribute:对应数据库中字段的数据是否可以为null
- MaxLengthAttribute:对应数据库中字符串类型字段的最大长度
- MinLengthAttribute:在数据库中无对应,但在代码中字符串最小长度
- ConcurrencyCheckAttribute:指定用于开放式并发检查的列的数据类型
- TimestampAttribute:将列的数据类型指定为行版本
System.ComponentModel.DataAnnotations命名空间中只定义了部分实体验证的特性,在EntityFramework程序集中定义了更多的数据映射特性:
- DatabaseGeneratedAttribute:标记指定实体属性是由数据库生成的,并指定生成策略(None数据库不生成值,Identity当插入行时,数据库生成值,Computed当插入或更新行时,数据库生成值)
- ColumnAttribute:指定实体属性在数据库中的列名及数据类型
- TableAttribute:指定实体类对应的数据表名
- ForeignKeyAttribute:指定导航属性的外键字段
- NotMappedAttribute:标记指定实体属性在创建数据库中不创建对应字段
- ComplexTypeAttribute:标记指定实体属性是将一个对象作为另一个对象的属性,映射到数据库中则子对象表现为多个属性字段
对于实体关系对应的数据表关系,无非“0:1,1:1,0:N,1:N,N:N”这几种,可以使用导航属性中的数据类型来表示,0…1端使用单实体类型表示,N端使用ICollection<T>集合类型表示。对于单实体端,默认是可为空的,即为0关系,如果要设置为1关系,要使用[Required]标签来进行标记。但对于一对一中的关系主体与依赖对象确无法做更细节的控制。
(二) Fluent API
使用DataAnnotation非常简单,但对于EntityFramework中的特性,就要在实体类中引入EntityFramework程序集,但实体类最好能是保持与架构无关性的POCO类,才能更具通用性。所以,最好是在数据层中使用FluentAPI在数据层中进行实体类与数据库之间的映射工作。
当然,System.ComponentModel.DataAnnotations命名空间的DataAnnotation在EntityFramework程序集中也有相应的API:
- HasKey - KeyAttribute:配置此实体类型的主键属性
- IsRequired - RequiredAttribute:将此属性配置为必需属性。用于存储此属性的数据库列将不可以为null
- HasMaxLength - MaxLengthAttribute:将属性配置为具有指定的最大长度
- IsConcurrencyToken - ConcurrencyCheckAttribute:将属性配置为用作开放式并发标记
- IsRowVersion - TimestampAttribute:将属性配置为数据库中的行版本。实际数据类型将因使用的数据库提供程序而异。将属性设置为行版本会自动将属性配置为开放式并发标记。
上面这些API均无需引用EntityFramework,推荐使用DataAnnotation方式来设置映射。
以下API的DataAnnotation特性是在EntityFramework中定义,如果也使用DataAnnotation方式来设置映射,就会给实体类增加额外的第三方程序集的依赖。所以以下API的映射推荐使用FluentAPI的方式来设置映射:
- ToTable - TableAttribute:配置此实体类型映射到的表名
- HasColumnName - ColumnAttribute:配置用于存储属性的数据库列的名称
- HasForeignKey - ForeignKeyAttribute:将关系配置为使用在对象模型中的外键属性。如果未在对象模型中公开外键属性,则使用Map方法
- Ignore - NotMappedAttribute:从模型中排队某个属性,使该属性不会映射到数据库
- HasRequired:通过此实体类型配置必需关系。除非指定此关系,否则实体类型的实例将无法保存到数据库。数据库中的外键不可为null。
- HasOptional:从此实体类型配置可选关系。实体类型的实例将能保存到数据库,而无需指定此关系。数据库中的外键可为null。
- HasMany:从此实体类型配置一对多关系。
- WithOptional:将关系配置为required:optional。(required:0…1端的1,表示必需,不可为null;optional:0…1端的0,表示可选,可为null。下同)
- WithOptionalDependent:将关系配置为optional:optional。要配置的实体类型将成为依赖对象,且包含主体的外键。作为关系目标的实体类型将成为关系中的主体。
- WithOptionalPrincipal:将关系配置为optional:optional。要配置的实体类型将成为关系中的主体。作为关系目标的实体类型将成为依赖对象,且包含主体的外键。
- WithRequired:将关系的指定端配置为必需的,且在关系的另一端有导航属性。
- WithRequiredDependent:将关系配置为required:required。要配置的实体类型将成为依赖对象,且包含主体的外键。作为关系目标的实体类型将成为关系中的主体。
- WithRequiredPrincipal:将关系配置为required:required。要配置的实体类型将成为关系中的实体。作为关系目标的实体类型将成为依赖对象,且包含主体的外键。
经常用到的DataAnnotation与FluentAPI列举完了,使用上还是遵守这个原则:
如果在System.ComponentModel.DataAnnotations命名空间存在相应的标签,就使用 DataAnnotation 的方式,如果不存在,则使用 FluentAPI 的方式。
(三) 映射代码示例
实体类关系图:
上图是一个以用户信息为中心的实体关系图,关系说明如下:
- 一个用户可拥有一个可选的用户扩展信息(1 - 0)
- 一个用户扩展信息拥有一个必需的所属用户信息(0 - 1)
- 一个用户扩展信息拥有一个用户地址信息(复合类型)
- 一个用户可对应多个登录日志信息(1 - N)
- 一个登录日志拥有一个必需的所属用户信息(N- 1)
- 一个用户可以拥有多个角色(N - N)
- 一个角色可以分配给多个用户(N - N)
实体类定义:
1 namespace GMF.Demo.Core.Models
2 {
3 /// <summary>
4 /// 实体类——用户信息
5 /// </summary>
6 [Description("用户信息")]
7 public class Member : Entity
8 {
9 public int Id { get; set; }
10
11 [Required]
12 [StringLength(20)]
13 public string UserName { get; set; }
14
15 [Required]
16 [StringLength(32)]
17 public string Password { get; set; }
18
19 [Required]
20 [StringLength(20)]
21 public string NickName { get; set; }
22
23 [Required]
24 [StringLength(50)]
25 public string Email { get; set; }
26
27 /// <summary>
28 /// 获取或设置 用户扩展信息
29 /// </summary>
30 public virtual MemberExtend Extend { get; set; }
31
32 /// <summary>
33 /// 获取或设置 用户拥有的角色信息集合
34 /// </summary>
35 public virtual ICollection<Role> Roles { get; set; }
36
37 /// <summary>
38 /// 获取或设置 用户登录记录集合
39 /// </summary>
40 public virtual ICollection<LoginLog> LoginLogs { get; set; }
41 }
42 }
1 namespace GMF.Demo.Core.Models
2 {
3 /// <summary>
4 /// 实体类——用户扩展信息
5 /// </summary>
6 [Description("用户扩展信息")]
7 public class MemberExtend : Entity
8 {
9 /// <summary>
10 /// 初始化一个 用户扩展实体类 的新实例
11 /// </summary>
12 public MemberExtend()
13 {
14 Id = CombHelper.NewComb();
15 }
16
17 public Guid Id { get; set; }
18
19 public string Tel { get; set; }
20
21 public MemberAddress Address { get; set; }
22
23 public virtual Member Member { get; set; }
24 }
25 }
1 namespace GMF.Demo.Core.Models
2 {
3 /// <summary>
4 /// 用户地址信息
5 /// </summary>
6 public class MemberAddress
7 {
8 [StringLength(10)]
9 public string Province { get; set; }
10
11 [StringLength(20)]
12 public string City { get; set; }
13
14 [StringLength(20)]
15 public string County { get; set; }
16
17 [StringLength(60, MinimumLength = 5)]
18 public string Street { get; set; }
19 }
20 }
1 namespace GMF.Demo.Core.Models
2 {
3 /// <summary>
4 /// 实体类——登录记录信息
5 /// </summary>
6 [Description("登录记录信息")]
7 public class LoginLog : Entity
8 {
9 /// <summary>
10 /// 初始化一个 登录记录实体类 的新实例
11 /// </summary>
12 public LoginLog()
13 {
14 Id = CombHelper.NewComb();
15 }
16
17 public Guid Id { get; set; }
18
19 [Required]
20 [StringLength(15)]
21 public string IpAddress { get; set; }
22
23 /// <summary>
24 /// 获取或设置 所属用户信息
25 /// </summary>
26 public virtual Member Member { get; set; }
27 }
28 }
1 namespace GMF.Demo.Core.Models
2 {
3 /// <summary>
4 /// 实体类——角色信息
5 /// </summary>
6 [Description("角色信息")]
7 public class Role : Entity
8 {
9 public Role()
10 {
11 Id = CombHelper.NewComb();
12 }
13
14 public Guid Id { get; set; }
15
16 [Required]
17 [StringLength(20)]
18 public string Name { get; set; }
19
20 [StringLength(100)]
21 public string Description { get; set; }
22
23 /// <summary>
24 /// 获取或设置 角色类型
25 /// </summary>
26 public RoleType RoleType { get; set; }
27
28 /// <summary>
29 /// 获取或设置 角色类型的数值表示,用于数据库存储
30 /// </summary>
31 public int RoleTypeNum { get; set; }
32
33 /// <summary>
34 /// 获取或设置 拥有此角色的用户信息集合
35 /// </summary>
36 public virtual ICollection<Member> Members { get; set; }
37 }
38 }
1 namespace GMF.Demo.Core.Models
2 {
3 /// <summary>
4 /// 表示角色类型的枚举
5 /// </summary>
6 [Description("角色类型")]
7 public enum RoleType
8 {
9 /// <summary>
10 /// 用户类型
11 /// </summary>
12 [Description("用户角色")]
13 User = 0,
14
15 /// <summary>
16 /// 管理员类型
17 /// </summary>
18 [Description("管理角色")]
19 Admin = 1
20 }
21 }
实体类映射:
实体类映射中,关系的映射配置在关系的两端都可以配置。例如,用户信息与登录信息的 一对多 关系可以在用户信息端配置:
HasMany(m => m.LoginLogs).WithRequired(n => n.Member);
等效于在登录日志信息端配置:
HasRequired(m => m.Member).WithMany(n => n.LoginLogs);
但是,如果所有的关系映射都在作为主体的用户信息端进行配置,势必造成用户信息端配置的臃肿与职责不明。所以,为了保持各个实体类型的职责单一,实体关系推荐在关系的非主体端进行映射。
用户信息映射类,用户信息是关系的主体,所有的关系都不在此映射类中进行配置
1 namespace GMF.Demo.Core.Data.Configurations
2 {
3 public class MemberConfiguration : EntityTypeConfiguration<Member>
4 {
5 }
6 }
用户扩展信息映射类,配置用户扩展信息与用户信息的 0:1 关系
1 namespace GMF.Demo.Core.Data.Configurations
2 {
3 public class MemberExtendConfiguration : EntityTypeConfiguration<MemberExtend>
4 {
5 public MemberExtendConfiguration()
6 {
7 HasRequired(m => m.Member).WithOptional(n => n.Extend);
8 }
9 }
10 }
用户地址信息映射类,配置用户地址信息的复杂类型映射,复杂类型继承于 ComplexTypeConfiguration<>
1 namespace GMF.Demo.Core.Data.Configurations
2 {
3 public class MemberAddressConfiguration : ComplexTypeConfiguration<MemberAddress>
4 {
5 public MemberAddressConfiguration()
6 {
7 Property(m => m.Province).HasColumnName("Province");
8 Property(m => m.City).HasColumnName("City");
9 Property(m => m.County).HasColumnName("County");
10 Property(m => m.Street).HasColumnName("Street");
11 }
12 }
13 }
登录记录信息映射,配置登录信息与用户信息的 N:1 的关系
1 namespace GMF.Demo.Core.Data.Configurations
2 {
3 public class LoginLogConfiguration : EntityTypeConfiguration<LoginLog>
4 {
5 public LoginLogConfiguration()
6 {
7 HasRequired(m => m.Member).WithMany(n => n.LoginLogs);
8 }
9 }
10 }
角色信息映射,配置角色信息与用户信息的 N:N 的关系
1 namespace GMF.Demo.Core.Data.Configurations
2 {
3 public class RoleConfiguration : EntityTypeConfiguration<Role>
4 {
5 public RoleConfiguration()
6 {
7 HasMany(m => m.Members).WithMany(n => n.Roles);
8 }
9 }
10 }
映射类的应用:
映射类需要在数据访问上下文中进行应用才能生效,只要在DbContext的OnModelCreating方法中进行映射配置添加即可。
1 protected override void OnModelCreating(DbModelBuilder modelBuilder)
2 {
3 //移除一对多的级联删除约定,想要级联删除可以在 EntityTypeConfiguration<TEntity>的实现类中进行控制
4 modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
5 //多对多启用级联删除约定,不想级联删除可以在删除前判断关联的数据进行拦截
6 //modelBuilder.Conventions.Remove<ManyToManyCascadeDeleteConvention>();
7
8 modelBuilder.Configurations.Add(new MemberConfiguration());
9 modelBuilder.Configurations.Add(new MemberExtendConfiguration());
10 modelBuilder.Configurations.Add(new MemberAddressConfiguration());
11 modelBuilder.Configurations.Add(new RoleConfiguration());
12 modelBuilder.Configurations.Add(new LoginLogConfiguration());
13 }
三、数据迁移
经过上面的折腾,数据库结构已经大变,项目当然运行不起来了。
根据提示,必须进行迁移来更新数据库结构。EntityFramework的数据迁移通过 NuGet 来进行。打开程序包管理器控制台(Package Manager Console),键入“ get-help EntityFramework”命令,可以获得相关的帮助信息。
若想了解各个子命令的帮助细节,也可键入“get-help 子命令”命令,例如:get-help Enable-Migrations
下面我们来对项目进行数据迁移,在我们的项目中,EntityFramework的依赖止于项目GMF.Demo.Core.Data,项目的数据迁移也是在此项目中进行。迁移步骤如下:
- 在“程序包管理器控制台”键入命令:Enable-Migrations -ProjectName GMF.Demo.Core.Data
- 添加后,项目中添加了一个名为Migrations的文件夹
添加生成以下代码:1 namespace GMF.Demo.Core.Data.Migrations
2 {
3 internal sealed class Configuration : DbMigrationsConfiguration<DemoDbContext>
4 {
5 public Configuration()
6 {
7 AutomaticMigrationsEnabled = false;
8 }
9
10 protected override void Seed(DemoDbContext context)
11 {
12 // This method will be called after migrating to the latest version.
13
14 // You can use the DbSet<T>.AddOrUpdate() helper extension method
15 // to avoid creating duplicate seed data. E.g.
16 //
17 // context.People.AddOrUpdate(
18 // p => p.FullName,
19 // new Person { FullName = "Andrew Peters" },
20 // new Person { FullName = "Brice Lambson" },
21 // new Person { FullName = "Rowan Miller" }
22 // );
23 //
24 }
25 }
26 }
方法Seed中可以进行数据迁移后的数据初始化工作,将在每次迁移之后运行。如上代码所示,AddOrUpdate是IDbSet<TEntity>的扩展方法,如果指定条件的数据不存在,则会添加,如果存在,会更新。所以,如果数据是通过此方法来初始化的,在与业务更新之后,再次进行数据迁移后,还是会被还原。
还有一个名为InitialCreate的类,配置生成数据库的细节:1 namespace GMF.Demo.Core.Data.Migrations
2 {
3 public partial class InitialCreate : DbMigration
4 {
5 public override void Up()
6 {
7 CreateTable(
8 "dbo.Roles",
9 c => new
10 {
11 Id = c.Guid(nullable: false),
12 Name = c.String(nullable: false, maxLength: 20),
13 Description = c.String(maxLength: 100),
14 IsDeleted = c.Boolean(nullable: false),
15 AddDate = c.DateTime(nullable: false),
16 Timestamp = c.Binary(nullable: false, fixedLength: true, timestamp: true, storeType: "rowversion"),
17 })
18 .PrimaryKey(t => t.Id);
19
20 CreateTable(
21 "dbo.Members",
22 c => new
23 {
24 Id = c.Int(nullable: false, identity: true),
25 UserName = c.String(nullable: false, maxLength: 20),
26 Password = c.String(nullable: false, maxLength: 32),
27 NickName = c.String(nullable: false, maxLength: 20),
28 Email = c.String(nullable: false, maxLength: 50),
29 IsDeleted = c.Boolean(nullable: false),
30 AddDate = c.DateTime(nullable: false),
31 Timestamp = c.Binary(nullable: false, fixedLength: true, timestamp: true, storeType: "rowversion"),
32 })
33 .PrimaryKey(t => t.Id);
34
35 CreateTable(
36 "dbo.MemberExtends",
37 c => new
38 {
39 Id = c.Guid(nullable: false),
40 IsDeleted = c.Boolean(nullable: false),
41 AddDate = c.DateTime(nullable: false),
42 Timestamp = c.Binary(nullable: false, fixedLength: true, timestamp: true, storeType: "rowversion"),
43 Member_Id = c.Int(nullable: false),
44 })
45 .PrimaryKey(t => t.Id)
46 .ForeignKey("dbo.Members", t => t.Member_Id)
47 .Index(t => t.Member_Id);
48
49 CreateTable(
50 "dbo.LoginLogs",
51 c => new
52 {
53 Id = c.Guid(nullable: false),
54 IpAddress = c.String(nullable: false, maxLength: 15),
55 IsDeleted = c.Boolean(nullable: false),
56 AddDate = c.DateTime(nullable: false),
57 Timestamp = c.Binary(nullable: false, fixedLength: true, timestamp: true, storeType: "rowversion"),
58 Member_Id = c.Int(),
59 })
60 .PrimaryKey(t => t.Id)
61 .ForeignKey("dbo.Members", t => t.Member_Id)
62 .Index(t => t.Member_Id);
63
64 CreateTable(
65 "dbo.MemberRoles",
66 c => new
67 {
68 Member_Id = c.Int(nullable: false),
69 Role_Id = c.Guid(nullable: false),
70 })
71 .PrimaryKey(t => new { t.Member_Id, t.Role_Id })
72 .ForeignKey("dbo.Members", t => t.Member_Id, cascadeDelete: true)
73 .ForeignKey("dbo.Roles", t => t.Role_Id, cascadeDelete: true)
74 .Index(t => t.Member_Id)
75 .Index(t => t.Role_Id);
76
77 }
78
79 public override void Down()
80 {
81 DropIndex("dbo.MemberRoles", new[] { "Role_Id" });
82 DropIndex("dbo.MemberRoles", new[] { "Member_Id" });
83 DropIndex("dbo.LoginLogs", new[] { "Member_Id" });
84 DropIndex("dbo.MemberExtends", new[] { "Member_Id" });
85 DropForeignKey("dbo.MemberRoles", "Role_Id", "dbo.Roles");
86 DropForeignKey("dbo.MemberRoles", "Member_Id", "dbo.Members");
87 DropForeignKey("dbo.LoginLogs", "Member_Id", "dbo.Members");
88 DropForeignKey("dbo.MemberExtends", "Member_Id", "dbo.Members");
89 DropTable("dbo.MemberRoles");
90 DropTable("dbo.LoginLogs");
91 DropTable("dbo.MemberExtends");
92 DropTable("dbo.Members");
93 DropTable("dbo.Roles");
94 }
95 }
96 }
- 执行“Add-Migration FirstMigration”命令,添加一个名为FirstMigration的迁移
- 执行“Update-Database”命令,更新数据库架构
如果更新数据库存在冲突而不能执行更新,可以添加 -Force强制执行,例如:“Update-Database -Force” - 设置自动迁移
每次都通过控制台来进行迁移太过麻烦,可以设置为自动迁移。
有以下两个参数可以对自动迁移进行设置:
1. AutomaticMigrationsEnabled:获取或设置 指示迁移数据库时是否可使用自动迁移的值。
2. AutomaticMigrationDataLossAllowed:获取或设置 指示是否可接受自动迁移期间的数据丢失的值。如果设置为false,则将在数据丢失可能作为自动迁移一部分出现时引发异常。
修改迁移的Configuration类如下:1 namespace GMF.Demo.Core.Data.Migrations
2 {
3 internal sealed class Configuration : DbMigrationsConfiguration<DemoDbContext>
4 {
5 public Configuration()
6 {
7 AutomaticMigrationsEnabled = true;
8 AutomaticMigrationDataLossAllowed = true;
9 }
10
11 protected override void Seed(DemoDbContext context)
12 {
13 List<Member> members = new List<Member>
14 {
15 new Member { UserName = "admin", Password = "123456", Email = "admin@gmfcn.net", NickName = "管理员" },
16 new Member { UserName = "gmfcn", Password = "123456", Email = "mf.guo@qq.com", NickName = "郭明锋" }
17 };
18 DbSet<Member> memberSet = context.Set<Member>();
19 memberSet.AddOrUpdate(m => new { m.Id }, members.ToArray());
20 }
21 }
22 }
修改数据库初始化策略如下:
Database.SetInitializer(new MigrateDatabaseToLatestVersion<DemoDbContext, Configuration>());
四、代码重构
经过上面的演练,我们的项目变成如下图所示:
现在的项目中,数据访问上下文DemoDbContext代码如下所示:
1 namespace GMF.Demo.Core.Data.Context
2 {
3 /// <summary>
4 /// Demo项目数据访问上下文
5 /// </summary>
6 [Export(typeof(DbContext))]
7 public class DemoDbContext : DbContext
8 {
9 #region 构造函数
10
11 /// <summary>
12 /// 初始化一个 使用连接名称为“default”的数据访问上下文类 的新实例
13 /// </summary>
14 public DemoDbContext()
15 : base("default") { }
16
17 /// <summary>
18 /// 初始化一个 使用指定数据连接名称或连接串 的数据访问上下文类 的新实例
19 /// </summary>
20 public DemoDbContext(string nameOrConnectionString)
21 : base(nameOrConnectionString) { }
22
23 #endregion
24
25 #region 属性
26
27 public DbSet<Role> Roles { get; set; }
28
29 public DbSet<Member> Members { get; set; }
30
31 public DbSet<MemberExtend> MemberExtends { get; set; }
32
33 public DbSet<LoginLog> LoginLogs { get; set; }
34
35 #endregion
36
37 protected override void OnModelCreating(DbModelBuilder modelBuilder)
38 {
39 //移除一对多的级联删除约定,想要级联删除可以在 EntityTypeConfiguration<TEntity>的实现类中进行控制
40 modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
41 //多对多启用级联删除约定,不想级联删除可以在删除前判断关联的数据进行拦截
42 //modelBuilder.Conventions.Remove<ManyToManyCascadeDeleteConvention>();
43
44 modelBuilder.Configurations.Add(new MemberConfiguration());
45 modelBuilder.Configurations.Add(new MemberExtendConfiguration());
46 modelBuilder.Configurations.Add(new MemberAddressConfiguration());
47 modelBuilder.Configurations.Add(new RoleConfiguration());
48 modelBuilder.Configurations.Add(new LoginLogConfiguration());
49 }
50 }
51 }
由代码可以看出,当前的上下文类与业务实体是强耦合的,分别耦合在DbSet<TEntity>的属性与OnModelCreating方法上。如果要解耦,对于属性,可以使用DbContext.Set<TEntity>()方法来实现指定实体的属性,对于OnModelCreating中的方法实现中的映射配置对象,则可提取一个通用接口,通过接口进行分别映射。
定义接口如下:
1 namespace GMF.Component.Data
2 {
3 /// <summary>
4 /// 实体映射接口
5 /// </summary>
6 [InheritedExport]
7 public interface IEntityMapper
8 {
9 /// <summary>
10 /// 将当前实体映射对象注册到当前数据访问上下文实体映射配置注册器中
11 /// </summary>
12 /// <param name="configurations">实体映射配置注册器</param>
13 void RegistTo(ConfigurationRegistrar configurations);
14 }
15 }
IEntityMapper接口添加了MEF的InheritedExport特性,该特性可以沿着继承链传递所施加的特性。在需要的时候,就可以通过ImportManyAttribute一次性导出所有实现了IEntityMapper接口的实现类对象。
在实体映射类中添加IEntityMapper的实现,如角色映射类中:
1 namespace GMF.Demo.Core.Data.Configurations
2 {
3 public class RoleConfiguration : EntityTypeConfiguration<Role>, IEntityMapper
4 {
5 public RoleConfiguration()
6 {
7 HasMany(m => m.Members).WithMany(n => n.Roles);
8 }
9
10 public void RegistTo(ConfigurationRegistrar configurations)
11 {
12 configurations.Add(this);
13 }
14 }
15 }
下面来对数据访问上下文进行改造,并转移到数据组件 GMF.Component.Data 中。
添加一个IEnumerable<IEntityMapper>类型的属性EntityMappers,并添加ImportManyAttribute,用于引入所有实现了IEntityMapper的类的对象。
在重写的OnModelCreating方法中,遍历EntityMappers集合,调用其中的RegistTo进行实体映射类对象的添加。
1 namespace GMF.Component.Data
2 {
3 /// <summary>
4 /// EF数据访问上下文
5 /// </summary>
6 [Export(typeof (DbContext))]
7 public class EFDbContext : DbContext
8 {
9 public EFDbContext()
10 : base("default") { }
11
12 public EFDbContext(string nameOrConnectionString)
13 : base(nameOrConnectionString) { }
14
15 [ImportMany]
16 public IEnumerable<IEntityMapper> EntityMappers { get; set; }
17
18 protected override void OnModelCreating(DbModelBuilder modelBuilder)
19 {
20 modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
21
22 if (Configuration == null)
23 {
24 return;
25 }
26 foreach (var mapper in EntityMappers)
27 {
28 mapper.RegistTo(modelBuilder.Configurations);
29 }
30 }
31 }
32 }
上下文EFDbContext在单元操作类 EFRepositoryContext 类中进行使用:
1 namespace GMF.Component.Data
2 {
3 /// <summary>
4 /// 数据单元操作类
5 /// </summary>
6 [Export(typeof (IUnitOfWork))]
7 internal class EFRepositoryContext : UnitOfWorkContextBase
8 {
9 /// <summary>
10 /// 获取 当前使用的数据访问上下文对象
11 /// </summary>
12 protected override DbContext Context
13 {
14 get { return EFDbContext; }
15 }
16
17 [Import(typeof (DbContext))]
18 private EFDbContext EFDbContext { get; set; }
19 }
20 }
经过如此重构,DbContext上下文就与实体无关了,数据访问功能与业务实体便完成解耦。
五、源码下载
六、系列导航
- MVC实用架构设计(〇)——总体设计
- MVC实用架构设计(一)——项目结构搭建
- MVC实用架构设计(二)——使用MEF实用IOC
- MVC实用架构设计(三)——EF-Code First(1):Repository,UnitOfWork,DbContext
- MVC实用架构设计(三)——EF-Code First(2):实体映射、数据迁移,重构
- MVC实用架构设计(三)——EF-Code First(3):使用T4模板生成代码
- MVC实用架构设计(三)——EF-Code First(4):数据查询
- MVC实用架构设计(三)——EF-Code First(5):二级缓存
- MVC实体架构设计(四)——日志记录
- 未完待续。。。
发表评论
-
C#WebBrowser控件使用教程与技巧收集--苏飞收集 - sufeinet
2013-06-28 12:07 1067原帖地址:http://www.cnblogs.com/suf ... -
我要喷一个自认为很垃圾的网站架构 - 老赵【苏州】
2013-06-28 12:01 1126原帖地址:http://www.cnblogs.com/lao ... -
[翻译] Oracle Database 12c 新特性Multitenant - Cheney Shue
2013-06-28 11:43 620原帖地址:http://www.cnblogs.com/ese ... -
memcahd 命令操作详解 - 阿正-WEB
2013-06-28 11:37 470原帖地址:http://www.cnblogs.com/azh ... -
面向过程的代码符合大众的思维方式吗? - 史蒂芬.王
2013-06-27 10:28 593原帖地址:http://www.cnblogs.com/ste ... -
面向过程的代码符合大众的思维方式吗? - 史蒂芬.王
2013-06-27 10:28 558原帖地址:http://www.cnblogs.com/ste ... -
RPG游戏之组队测试 - zthua
2013-06-27 10:22 557原帖地址:http://www.cnblogs.com/zth ... -
IT人们给个建议 - SOUTHER
2013-06-26 14:06 523原帖地址:http://www.cnblogs.com/sou ... -
Java向前引用容易出错的地方 - 银河使者
2013-06-26 14:00 494原帖地址:http://www.cnblogs.com/nok ... -
使用Func<T1, T2, TResult> 委托返回匿名对象 - 灰身
2013-06-26 13:54 800原帖地址:http://www.cnblo ... -
【web前端面试题整理03】来看一点CSS相关的吧 - 叶小钗
2013-06-25 10:45 783原帖地址:http://www.cnblogs.com/yex ... -
Windows 8 动手实验系列教程 实验6:设置和首选项 - zigzagPath
2013-06-25 10:27 619原帖地址:http://www.cnblogs.com/zig ... -
闲聊可穿戴设备 - shawn.xie
2013-06-25 10:21 562原帖地址:http://www.cnblo ... -
CentOS下Mysql安装教程 - 小学徒V
2013-06-23 15:24 608原帖地址:http://www.cnblogs.com/xia ... -
vmware安装ubuntu12.04嵌套安装xen server(实现嵌套虚拟化) - skyme
2013-06-23 15:18 836原帖地址:http://www.cnblogs.com/sky ... -
之前专门为IE6、7开发的网站如何迁移到IE10及可能遇到的问题和相应解决方案汇总 - 海之澜
2013-06-23 15:12 949原帖地址:http://www.cnblogs.com/wuz ... -
Android学习笔记--解析XML之SAX - 承香墨影
2013-06-23 15:01 408原帖地址:http://www.cnblo ... -
SQL Server 性能优化之——T-SQL TVF和标量函数
2013-06-19 09:32 670原帖地址:http://www.cnblogs.com/Boy ... -
Nginx学习笔记(二) Nginx--connection&request
2013-06-19 09:26 666原帖地址:http://www.cnblogs.com/cod ... -
从郭美美霸气侧漏看项目管理之项目经理防身术
2013-06-19 09:20 500原帖地址:http://www.cnblogs.com/had ...
相关推荐
首先,Entity Framework Code First是一种ORM(对象关系映射)工具,允许开发者使用.NET类来表示数据库中的表,通过代码定义数据模型,简化数据库的创建和维护。在本项目中,我们将利用Code First的工作流来建立与...
Code First 数据迁移是指使用 Entity Framework Code First 模式在 ASP.NET MVC 4 框架中实现数据迁移的过程。在这个过程中,我们可以通过 Entity Framework 的 Code First 模式来定义实体模型,然后使用 NuGet ...
在本文中,我们将深入探讨如何使用ASP.NET MVC 5.2、Entity Framework 6.0 (EF6) 和 MySQL 数据库通过CodeFirst模式构建一个基本的增删改查(CRUD)应用程序。这个例子展示了如何将这三个技术有效地结合起来,以实现...
"EasyUI + MVC4 + EF + CodeFirst"的组合提供了一种强大的解决方案,它整合了多种技术和框架,帮助开发者快速构建用户界面和数据驱动的应用。让我们深入探讨这个组合中的每一个组件以及它们如何协同工作。 **EasyUI...
本项目“Csharp-MVC-CRUD-Functions-With-CodeFirst”着重展示了如何使用Entity Framework的CodeFirst方法来实现CRUD(创建、读取、更新和删除)操作。CodeFirst是数据库开发的一种方式,允许开发者通过定义模型类和...
MVC控制器可以使用EF来操作数据库,模型类可以直接映射到Code First的实体类,或者在Model First中,可以从数据库生成的模型类中获取数据。 在这个“EFtest”项目中,你可能会看到如何创建和配置DbContext类,这是...
这个示例项目很可能是展示如何将现有的数据库集成到CodeFirst的MVC应用中,通过实际的代码实例来说明如何进行数据库迁移、模型映射以及错误处理等关键环节。通过学习和实践这个示例,开发者可以更好地理解和掌握Code...
ASP.NET MVC4、Entity Framework 4 (EF4) 和 CodeFirst 开发模式是现代Web应用程序构建中的关键组件,尤其在创建高效、灵活的影院系统时。在这个项目中,开发人员利用了这些技术的优势来实现一个功能丰富的在线影院...
为了启动这个项目,你需要确保已安装必要的依赖项,如.NET Core SDK、ASP.NET Core MVC、EF Core MySQL数据提供程序以及Layui的前端资源。然后,通过`dotnet run`命令运行项目,浏览器中输入指定的URL即可查看和操作...
它支持Code First、Database First和Model First三种开发模式,本例可能使用的是Code First,即通过定义模型类来自动生成数据库结构。 **5. Visual Studio 2013** Visual Studio 2013是Microsoft提供的集成开发环境...
在本项目中,"EF+MVC+三层(MVC +三层架构+EF对数据库对数据库进行增删改查小案例).zip"是一个综合性的示例,它演示了如何利用Entity Framework(EF)、Model-View-Controller(MVC)架构以及三层架构来实现对...
在软件开发中,EF Code First是一种流行的对象关系映射(ORM)工具,它允许开发者通过编写C#代码定义数据模型,然后自动创建数据库结构。结合MVC4(Model-View-Controller架构的第4版),这是一个用于构建Web应用...
Code First是Entity Framework(EF)的一种开发模式,它是微软推出的ORM(对象关系映射)工具,用于简化.NET应用程序与数据库之间的数据操作。在Code First中,开发者首先编写C#类来表示数据库中的表,然后通过EF...
【Web程序设计——JSP】是Web开发领域中一种重要的技术,它允许开发者在服务器端创建动态网页。JSP(JavaServer Pages)是Java平台上的一个标准,它结合了HTML和Java代码,使得开发者可以利用Java的强大功能来构建...
这是MVC设计模式中的三个核心组成部分。 ##### 第二章:Struts2框架入门 1. **选择题解析**: - 问题1:选项B。考查Struts2的基本配置。 - 问题2:选项C。涉及Struts2拦截器的工作原理。 - 问题3:选项B。关注的...
AspNetCore+EfCore+Mysql+CodeFrist+DDD的一个开发模板,含:Efcore全自动化迁移,代码运行时迁移。 具体构建过程和学习的链接为:https://blog.csdn.net/qq_38762313/article/details/100765606
在“WebApplication2”这个项目中,我们可以假设它是一个使用.NET MVC和Code First的Web应用程序,包含了自动迁移的配置和应用。开发者可能通过查看项目中的`DbContext`和`Migrations`文件夹下的迁移配置文件,了解...
ASP.NET MVC 应用程序利用 Entity Framework Code First 是一种现代Web开发的技术组合,它结合了ASP.NET MVC框架、Entity Framework(EF)数据访问库以及Code First开发策略。这个技术栈为构建高效、可扩展的数据库...
ASP.NET-Core-2-MVC-CRUD-datatables-jQuery-Plugin 首先实现ASP.NET Core 2 MVC CRUD数据表jQuery插件和EF Core代码的示例实现。你会学什么ASP.NET Core 2 使用ASP.NET Core 2的Web API 使用实体框架(EF)的CRUD ...