一、前言
成都创新互联公司公司2013年成立,先为德化等服务建站,德化等地企业,进行企业商务咨询服务。为德化企业网站制作PC+手机+微官网三网同步一站式服务解决您的所有建站问题。
经过EF的《第一篇》,我们已经把数据访问层基本搭建起来了,但并没有涉及实体关系。实体关系对于一个数据库系统来说至关重要,而且EF的各个实体之间的联系,实体之间的协作,联合查询等也都依赖于这些实体关系。
二、实体映射
实体与数据库的映射可以通过DataAnnotation与FluentAPI两种方式来进行映射:
(一) DataAnnotation
DataAnnotation 特性由.NET 3.5中引进,给.NET中的类提供了一种添加验证的方式。DataAnnotation由命名空间System.ComponentModel.DataAnnotations提供。下面列举实体模型中常用的DataAnnotation特性:
System.ComponentModel.DataAnnotations命名空间中只定义了部分实体验证的特性,在EntityFramework程序集中定义了更多的数据映射特性:
对于实体关系对应的数据表关系,无非“0:1,1:1,0:N,1:N,N:N”这几种,可以使用导航属性中的数据类型来表示,0…1端使用单实体类型表示,N端使用ICollection
(二) Fluent API
使用DataAnnotation非常简单,但对于EntityFramework中的特性,就要在实体类中引入EntityFramework程序集,但实体类最好能是保持与架构无关性的POCO类,才能更具通用性。所以,最好是在数据层中使用FluentAPI在数据层中进行实体类与数据库之间的映射工作。
当然,System.ComponentModel.DataAnnotations命名空间的DataAnnotation在EntityFramework程序集中也有相应的API:
上面这些API均无需引用EntityFramework,推荐使用DataAnnotation方式来设置映射。
以下API的DataAnnotation特性是在EntityFramework中定义,如果也使用DataAnnotation方式来设置映射,就会给实体类增加额外的第三方程序集的依赖。所以以下API的映射推荐使用FluentAPI的方式来设置映射:
经常用到的DataAnnotation与FluentAPI列举完了,使用上还是遵守这个原则:
如果在System.ComponentModel.DataAnnotations命名空间存在相应的标签,就使用 DataAnnotation 的方式,如果不存在,则使用 FluentAPI 的方式。
(三) 映射代码示例
实体类关系图:
上图是一个以用户信息为中心的实体关系图,关系说明如下:
实体类定义:
用户信息
- namespace GMF.Demo.Core.Models
- {
- ///
- /// 实体类——用户信息
- ///
- [Description("用户信息")]
- public class Member : Entity
- {
- public int Id { get; set; }
- [Required]
- [StringLength(20)]
- public string UserName { get; set; }
- [Required]
- [StringLength(32)]
- public string Password { get; set; }
- [Required]
- [StringLength(20)]
- public string NickName { get; set; }
- [Required]
- [StringLength(50)]
- public string Email { get; set; }
- ///
- /// 获取或设置 用户扩展信息
- ///
- public virtual MemberExtend Extend { get; set; }
- ///
- /// 获取或设置 用户拥有的角色信息集合
- ///
- public virtual ICollection
Roles { get; set; } - ///
- /// 获取或设置 用户登录记录集合
- ///
- public virtual ICollection
LoginLogs { get; set; } - }
- }
用户扩展信息
- namespace GMF.Demo.Core.Models
- {
- ///
- /// 实体类——用户扩展信息
- ///
- [Description("用户扩展信息")]
- public class MemberExtend : Entity
- {
- ///
- /// 初始化一个 用户扩展实体类 的新实例
- ///
- public MemberExtend()
- {
- Id = CombHelper.NewComb();
- }
- public Guid Id { get; set; }
- public string Tel { get; set; }
- public MemberAddress Address { get; set; }
- public virtual Member Member { get; set; }
- }
- }
用户地址信息
- namespace GMF.Demo.Core.Models
- {
- ///
- /// 用户地址信息
- ///
- public class MemberAddress
- {
- [StringLength(10)]
- public string Province { get; set; }
- [StringLength(20)]
- public string City { get; set; }
- [StringLength(20)]
- public string County { get; set; }
- [StringLength(60, MinimumLength = 5)]
- public string Street { get; set; }
- }
- }
登录记录信息
- namespace GMF.Demo.Core.Models
- {
- ///
- /// 实体类——登录记录信息
- ///
- [Description("登录记录信息")]
- public class LoginLog : Entity
- {
- ///
- /// 初始化一个 登录记录实体类 的新实例
- ///
- public LoginLog()
- {
- Id = CombHelper.NewComb();
- }
- public Guid Id { get; set; }
- [Required]
- [StringLength(15)]
- public string IpAddress { get; set; }
- ///
- /// 获取或设置 所属用户信息
- ///
- public virtual Member Member { get; set; }
- }
- }
角色信息
- namespace GMF.Demo.Core.Models
- {
- ///
- /// 实体类——角色信息
- ///
- [Description("角色信息")]
- public class Role : Entity
- {
- public Role()
- {
- Id = CombHelper.NewComb();
- }
- public Guid Id { get; set; }
- [Required]
- [StringLength(20)]
- public string Name { get; set; }
- [StringLength(100)]
- public string Description { get; set; }
- ///
- /// 获取或设置 角色类型
- ///
- public RoleType RoleType { get; set; }
- ///
- /// 获取或设置 角色类型的数值表示,用于数据库存储
- ///
- public int RoleTypeNum { get; set; }
- ///
- /// 获取或设置 拥有此角色的用户信息集合
- ///
- public virtual ICollection
Members { get; set; } - }
- }
角色类型(枚举)
- namespace GMF.Demo.Core.Models
- {
- ///
- /// 表示角色类型的枚举
- ///
- [Description("角色类型")]
- public enum RoleType
- {
- ///
- /// 用户类型
- ///
- [Description("用户角色")]
- User = 0,
- ///
- /// 管理员类型
- ///
- [Description("管理角色")]
- Admin = 1
- }
- }
#p#
实体类映射:
实体类映射中,关系的映射配置在关系的两端都可以配置。例如,用户信息与登录信息的 一对多 关系可以在用户信息端配置:
HasMany(m => m.LoginLogs).WithRequired(n => n.Member);
等效于在登录日志信息端配置:
HasRequired(m => m.Member).WithMany(n => n.LoginLogs);
但是,如果所有的关系映射都在作为主体的用户信息端进行配置,势必造成用户信息端配置的臃肿与职责不明。所以,为了保持各个实体类型的职责单一,实体关系推荐在关系的非主体端进行映射。
用户信息映射类,用户信息是关系的主体,所有的关系都不在此映射类中进行配置
- namespace GMF.Demo.Core.Data.Configurations
- {
- public class MemberConfiguration : EntityTypeConfiguration
- {
- }
- }
用户扩展信息映射类,配置用户扩展信息与用户信息的 0:1 关系
- namespace GMF.Demo.Core.Data.Configurations
- {
- public class MemberExtendConfiguration : EntityTypeConfiguration
- {
- public MemberExtendConfiguration()
- {
- HasRequired(m => m.Member).WithOptional(n => n.Extend);
- }
- }
- }
用户地址信息映射类,配置用户地址信息的复杂类型映射,复杂类型继承于 ComplexTypeConfiguration<>
- namespace GMF.Demo.Core.Data.Configurations
- {
- public class MemberAddressConfiguration : ComplexTypeConfiguration
- {
- public MemberAddressConfiguration()
- {
- Property(m => m.Province).HasColumnName("Province");
- Property(m => m.City).HasColumnName("City");
- Property(m => m.County).HasColumnName("County");
- Property(m => m.Street).HasColumnName("Street");
- }
- }
- }
登录记录信息映射,配置登录信息与用户信息的 N:1 的关系
- namespace GMF.Demo.Core.Data.Configurations
- {
- public class LoginLogConfiguration : EntityTypeConfiguration
- {
- public LoginLogConfiguration()
- {
- HasRequired(m => m.Member).WithMany(n => n.LoginLogs);
- }
- }
- }
角色信息映射,配置角色信息与用户信息的 N:N 的关系
- namespace GMF.Demo.Core.Data.Configurations
- {
- public class RoleConfiguration : EntityTypeConfiguration
- {
- public RoleConfiguration()
- {
- HasMany(m => m.Members).WithMany(n => n.Roles);
- }
- }
- }
映射类的应用:
映射类需要在数据访问上下文中进行应用才能生效,只要在DbContext的OnModelCreating方法中进行映射配置添加即可。
- protected override void OnModelCreating(DbModelBuilder modelBuilder)
- {
- //移除一对多的级联删除约定,想要级联删除可以在 EntityTypeConfiguration
的实现类中进行控制 - modelBuilder.Conventions.Remove
(); - //多对多启用级联删除约定,不想级联删除可以在删除前判断关联的数据进行拦截
- //modelBuilder.Conventions.Remove
(); - modelBuilder.Configurations.Add(new MemberConfiguration());
- modelBuilder.Configurations.Add(new MemberExtendConfiguration());
- modelBuilder.Configurations.Add(new MemberAddressConfiguration());
- modelBuilder.Configurations.Add(new RoleConfiguration());
- modelBuilder.Configurations.Add(new LoginLogConfiguration());
- }
#p#
三、数据迁移
经过上面的折腾,数据库结构已经大变,项目当然运行不起来了。
根据提示,必须进行迁移来更新数据库结构。EntityFramework的数据迁移通过 NuGet 来进行。打开程序包管理器控制台(Package Manager Console),键入“ get-help EntityFramework”命令,可以获得相关的帮助信息。
若想了解各个子命令的帮助细节,也可键入“get-help 子命令”命令,例如:get-help Enable-Migrations
下面我们来对项目进行数据迁移,在我们的项目中,EntityFramework的依赖止于项目GMF.Demo.Core.Data,项目的数据迁移也是在此项目中进行。迁移步骤如下:
添加生成以下代码:
- namespace GMF.Demo.Core.Data.Migrations
- {
- internal sealed class Configuration : DbMigrationsConfiguration
- {
- public Configuration()
- {
- AutomaticMigrationsEnabled = false;
- }
- protected override void Seed(DemoDbContext context)
- {
- // This method will be called after migrating to the latest version.
- // You can use the DbSet
.AddOrUpdate() helper extension method - // to avoid creating duplicate seed data. E.g.
- //
- // context.People.AddOrUpdate(
- // p => p.FullName,
- // new Person { FullName = "Andrew Peters" },
- // new Person { FullName = "Brice Lambson" },
- // new Person { FullName = "Rowan Miller" }
- // );
- //
- }
- }
- }
方法Seed中可以进行数据迁移后的数据初始化工作,将在每次迁移之后运行。如上代码所示,AddOrUpdate是IDbSet
还有一个名为InitialCreate的类,配置生成数据库的细节:
InitialCreate
- namespace GMF.Demo.Core.Data.Migrations
- {
- public partial class InitialCreate : DbMigration
- {
- public override void Up()
- {
- CreateTable(
- "dbo.Roles",
- c => new
- {
- Id = c.Guid(nullable: false),
- Name = c.String(nullable: false, maxLength: 20),
- Description = c.String(maxLength: 100),
- IsDeleted = c.Boolean(nullable: false),
- AddDate = c.DateTime(nullable: false),
- Timestamp = c.Binary(nullable: false, fixedLength: true, timestamp: true, storeType: "rowversion"),
- })
- .PrimaryKey(t => t.Id);
- CreateTable(
- "dbo.Members",
- c => new
- {
- Id = c.Int(nullable: false, identity: true),
- UserName = c.String(nullable: false, maxLength: 20),
- Password = c.String(nullable: false, maxLength: 32),
- NickName = c.String(nullable: false, maxLength: 20),
- Email = c.String(nullable: false, maxLength: 50),
- IsDeleted = c.Boolean(nullable: false),
- AddDate = c.DateTime(nullable: false),
- Timestamp = c.Binary(nullable: false, fixedLength: true, timestamp: true, storeType: "rowversion"),
- })
- .PrimaryKey(t => t.Id);
- CreateTable(
- "dbo.MemberExtends",
- c => new
- {
- Id = c.Guid(nullable: false),
- IsDeleted = c.Boolean(nullable: false),
- AddDate = c.DateTime(nullable: false),
- Timestamp = c.Binary(nullable: false, fixedLength: true, timestamp: true, storeType: "rowversion"),
- Member_Id = c.Int(nullable: false),
- })
- .PrimaryKey(t => t.Id)
- .ForeignKey("dbo.Members", t => t.Member_Id)
- .Index(t => t.Member_Id);
- CreateTable(
- "dbo.LoginLogs",
- c => new
- {
- Id = c.Guid(nullable: false),
- IpAddress = c.String(nullable: false, maxLength: 15),
- IsDeleted = c.Boolean(nullable: false),
- AddDate = c.DateTime(nullable: false),
- Timestamp = c.Binary(nullable: false, fixedLength: true, timestamp: true, storeType: "rowversion"),
- Member_Id = c.Int(),
- })
- .PrimaryKey(t => t.Id)
- .ForeignKey("dbo.Members", t => t.Member_Id)
- .Index(t => t.Member_Id);
- CreateTable(
- "dbo.MemberRoles",
- c => new
- {
- Member_Id = c.Int(nullable: false),
- Role_Id = c.Guid(nullable: false),
- })
- .PrimaryKey(t => new { t.Member_Id, t.Role_Id })
- .ForeignKey("dbo.Members", t => t.Member_Id, cascadeDelete: true)
- .ForeignKey("dbo.Roles", t => t.Role_Id, cascadeDelete: true)
- .Index(t => t.Member_Id)
- .Index(t => t.Role_Id);
- }
- public override void Down()
- {
- DropIndex("dbo.MemberRoles", new[] { "Role_Id" });
- DropIndex("dbo.MemberRoles", new[] { "Member_Id" });
- DropIndex("dbo.LoginLogs", new[] { "Member_Id" });
- DropIndex("dbo.MemberExtends", new[] { "Member_Id" });
- DropForeignKey("dbo.MemberRoles", "Role_Id", "dbo.Roles");
- DropForeignKey("dbo.MemberRoles", "Member_Id", "dbo.Members");
- DropForeignKey("dbo.LoginLogs", "Member_Id", "dbo.Members");
- DropForeignKey("dbo.MemberExtends", "Member_Id", "dbo.Members");
- DropTable("dbo.MemberRoles");
- DropTable("dbo.LoginLogs");
- DropTable("dbo.MemberExtends");
- DropTable("dbo.Members");
- DropTable("dbo.Roles");
- }
- }
- }
如果更新数据库存在冲突而不能执行更新,可以添加 -Force强制执行,例如:“Update-Database -Force”
- namespace GMF.Demo.Core.Data.Migrations
- {
- internal sealed class Configuration : DbMigrationsConfiguration
- {
分享文章:EF Code First:实体映射,数据迁移,重构
网站URL:http://www.mswzjz.cn/qtweb/news11/210561.html攀枝花网站建设、攀枝花网站运维推广公司-贝锐智能,是专注品牌与效果的网络营销公司;服务项目有等
声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 贝锐智能