文档章节

MVC3+EF4.1学习系列(四)----- ORM关系的处理

短短的歼击机
 短短的歼击机
发布于 2014/02/08 17:46
字数 2509
阅读 64
收藏 0

上篇文章 终于把基础的一些操作写完了 但是这些都是单表的处理 而EF做为一个ORM框架  就必须点说说对于关系的处理

 处理好关系 才能灵活的运用EF

关于关系的处理 一般就是  一对一   一对多  多对多  还有就是采用双向关联还是单项关联  而关系的处理  站长dudu的文章 就已经有了很好的介绍

推荐大家去看下 -------dudu的实体关系总结   这样大家对实体关系也就有了初步的认识了  但是在dudu的文章里 一直没有说如何处理多对多时 关系表

里有其他数据时怎么办(这个问题曾经困扰了我好久~~ 见人就问)  这里写下当时得到的几种方案 也希望能跟大家探讨下  好了 从实际项目开始 继续完善我们

的demo  并在从中探讨关系

先把原文中的完成后的图贴上来 也就是我们要处理的关系

这就是这次完成后样子    比以前多了几个类 关系也复杂了很多 下面我来解释这些关系

以及怎么建立的 一个一个慢慢来~~

一.创建教师实体

复制代码

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;

namespace ContosoUniversity.Models
{
   
public class Instructor
   {
       
public Int32 InstructorID { get; set; }

       [Required(ErrorMessage
= "Last name is required.")]
       [Display(Name
="Last Name")]
       [MaxLength(
50)]
       
public string LastName { get; set; }

       [Required(ErrorMessage
= "First name is required.")]
       [Column(
"FirstName")]
       [Display(Name
= "First Name")]
       [MaxLength(
50)]
       
public string FirstMidName { get; set; }

       [DisplayFormat(DataFormatString
= "{0:d}", ApplyFormatInEditMode = true)]
       [Required(ErrorMessage
= "Hire date is required.")]
       [Display(Name
= "Hire Date")]
       
public DateTime? HireDate { get; set; }

       
public string FullName
       {
           
get
           {
               
return LastName + ", " + FirstMidName;
           }
       }

       
public virtual ICollection<Course> Courses { get; set; }
       
public virtual OfficeAssignment OfficeAssignment { get; set; }
   }
}

复制代码

在第二篇文章里 简单的说了下用特性进行控制与MVC的结合 这里再详细的讲解几个

1.The Column Attribute

  [Column("FirstName")]
 
public string FirstMidName { get; set; }

利用这个特性 我们可以使FirstMidName属性映射到数据库的列为FirstName   在这里 如果使用[MaxLength(10)] 可以规定字段长度 [Required]这个来规定是否允许空 这是比较常用的 

2. 请大家注意这个FullName

 这个是不会被创建到数据库里的  因为它仅仅是获得 也就是一个get  这样是不会创建一个FullName列 在这个数据库表里的

3.普通的多对多的关系

 这篇文章是这样  一个老师可以教多个课程  一个课程 也可以被多个老师教   与我们平时的习惯似乎有些不符合 但是尊重原文 这里依然这样设计  于是这就是一个多对多的关系

于是有了这个导航属性

public virtual ICollection<Course> Courses { get; set; }

4.一对一的关系

每个老师都要有一个办公位置  而一个办公位置 也只应该有一个老师  所以这是一个一对一的关系 用dudu的话 采用两情相悦的方式 双向关联  ps:这个类还没建呢~~

public virtual OfficeAssignment OfficeAssignment { get; set; }

二.创建办公地点实体

复制代码

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;

namespace ContosoUniversity.Models
{
   
public class OfficeAssignment
   {
       [Key]
       
public int InstructorID { get; set; }

       [MaxLength(
50)]
       [Display(Name
= "Office Location")]
       
public string Location { get; set; }

       
public virtual Instructor Instructor { get; set; }
   }
}

复制代码

一对一的关系和KEY特效

继续刚才的一对一  因为一个老师只有一个办公地点 一个办公地点只有一个老师  所以 这张表希望和教师表一样  都用InstructorID做为主键 (就像有的时候 1对1的关系 我们放在一张表里一样) 而通过前面的学习 我们知道  EF寻找主键 的规则是 命名为ID 或者命名为 类名+ID   而这里面 没有符合要求的   遇到这种情况  我们可以通过[key] 来指定让谁做主键

[Key]
public int InstructorID { get; set; }

三.修改课程实体

复制代码

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;

namespace ContosoUniversity.Models
{
   
public class Course
   {
       [DatabaseGenerated(DatabaseGeneratedOption.None)]
       [Display(Name
= "Number")]
       
public int CourseID { get; set; }

       [Required(ErrorMessage
= "Title is required.")]
       [MaxLength(
50)]
       
public string Title { get; set; }

       [Required(ErrorMessage
= "Number of credits is required.")]
       [Range(
0,5,ErrorMessage="Number of credits must be between 0 and 5.")]
       
public int Credits { get; set; }

       [Display(Name
= "Department")]
       
public int DepartmentID { get; set; }

       
public virtual Department Department { get; set; }
       
public virtual ICollection<Enrollment> Enrollments { get; set; }
       
public virtual ICollection<Instructor> Instructors { get; set; }
   }
}

复制代码

1.The DatabaseGenerated Attribute

用这个特性是关闭自增长   然后用主键当课程编号  创建时需要输入 其实这样做我并不赞同 但是这篇文章主要是讲EF 所以不纠结于细节 可能这里主要是为了讲解这个特性吧

有兴趣的可以看下原文~~

2.一对多的关系

因为一个院系可以开多个课程  一个课程只能属于一个院系 所以这里是一对多关系  

在这个例子请注意 我们不仅拥有 院系的导航属性 还拥有一个导航属性ID 

public int DepartmentID { get; set; }
public virtual Department Department { get; set; }

当你的命名是这种情况时 聪明的EF 是不会在数据库里给你建两个列的  如果你依然担心  可以使用 ForeignKey特效  来制定外键的名字

但是这里我有个疑问  我看到很多人的设计  都喜欢这样子做  既有导航属性  又有导航属性ID  这样做的意义是什么呢?方便获取 设置初始化值么?还是什么?我用EF时 不推荐这么用

四.创建院系类

复制代码

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;

namespace ContosoUniversity.Models
{
   
public class Department
   {
       
public int DepartmentID { get; set; }

       [Required(ErrorMessage
= "Department name is required.")]
       [MaxLength(
50)]
       
public string Name { get; set; }

       [DisplayFormat(DataFormatString
="{0:c}")]
       [Required(ErrorMessage
= "Budget is required.")]
       [Column(TypeName
="money")]
       
public decimal? Budget { get; set; }

       [DisplayFormat(DataFormatString
="{0:d}", ApplyFormatInEditMode=true)]
       [Required(ErrorMessage
= "Start date is required.")]
       
public DateTime StartDate { get; set; }

       [Display(Name
="Administrator")]
       
public int? InstructorID { get; set; }

       
public virtual Instructor Administrator { get; set; }
       
public virtual ICollection<Course> Courses { get; set; }
   }
}

复制代码

一.The Column Attribute

[Column(TypeName="money")]
public decimal? Budget { get; set; }

通过这个特性 我们可以指定 生成到数据库里的列的属性

五.修改学生类

复制代码

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;

namespace ContosoUniversity.Models
{
   
public class Student
   {
       
public int StudentID { get; set; }

       [Required(ErrorMessage
= "Last name is required.")]
       [Display(Name
="Last Name")]
       [MaxLength(
50)]
       
public string LastName { get; set; }

       [Required(ErrorMessage
= "First name is required.")]
       [Column(
"FirstName")]
       [Display(Name
= "First Name")]
       [MaxLength(
50)]
       
public string FirstMidName { get; set; }

       [Required(ErrorMessage
= "Enrollment date is required.")]
       [DisplayFormat(DataFormatString
= "{0:d}", ApplyFormatInEditMode = true)]
       [Display(Name
= "Enrollment Date")]
       
public DateTime? EnrollmentDate { get; set; }

       
public string FullName
       {
           
get
           {
               
return LastName + ", " + FirstMidName;
           }
       }

       
public virtual ICollection<Enrollment> Enrollments { get; set; }
   }
}

复制代码

六.修改登记表(学生课程关系表)

修改登记表

七.多对多关系的思考与讨论

项目的类和关系建完了 这里我最想和大家探讨与分享的就是 这个如何处理多对多的关系

看我们的例子  其实有两个多对多  一个是课程和教师   还有一个是学生和选课

课程与教师 我们是直接使用多对多 没有为关系表建立实体类  因为我们的关系表不需要存储其他数据

而在学生和选课这里 要存储成绩  关系表里要存储其他的东西  这时 我们为关系表也建立了实体类  关系变成里  学生 1对多  关系表   课程也是1对多关系表

也就是说 当遇到使用多对多时 关系表还需要存储其他东西时(本文是成绩)  可以把多对多拆穿 两个1对多  还有 实际项目不建议使用多对多来处理关系 

这是这个项目给的启事 请问大家是如何解决这种情况的呢? 所有的ORM都会遇到这种问题的吧?

这里 感谢下  桀骜的灵魂 和 dax.net

他们在领域驱动群里 给了我另一种解答 直接上图

晴天 15:23:04 
分数之间不需要区分个体,所以是值对象 
晴天 15:23:23 
学生和课程是需要系统维护的,所以是聚合根

晴天 15:25:13 
CourseMark保存针对Course的引用 
晴天 15:25:28 
Student下包含多个CourseMark

dax.net  关于双向关联  

是不是双向关联不重要,首先要确定聚合根

班级是聚合根,学生也是聚合根  如果你的应用程序只是维护班级的基本信息,而不需要读取这个班级到底有哪些学生,那么就不需要往班级的聚合里添加学生

相反,通常情况下,学生的一个属性是班级,所以学生聚合里可以将班级加进去

八.关于EF的关系映射

看了这么多各种关系的类  他们之间存才的关系 多对多 一对一  一对多  处理这些映射是很麻烦的事 

我们有三种方法  1.按EF默认的规则 自动生成  但是有时生成的不是我们想要的 

                           2.通过特性的定义 来配置如何映射关系  以及如何映射到数据库  这篇文章提到了一些

                           3.通过流利的API 方式来定义   这也是我推荐的方式

看这篇文章的例子

复制代码

using System;
using System.Collections.Generic;
using System.Data.Entity;
using ContosoUniversity.Models;
using System.Data.Entity.ModelConfiguration.Conventions;

namespace ContosoUniversity.Models
{
   
public class SchoolContext : DbContext
   {
       
public DbSet<Course> Courses { get; set; }
       
public DbSet<Department> Departments { get; set; }
       
public DbSet<Enrollment> Enrollments { get; set; }
       
public DbSet<Instructor> Instructors { get; set; }
       
public DbSet<Student> Students { get; set; }
       
public DbSet<OfficeAssignment> OfficeAssignments { get; set; }

       
protected override void OnModelCreating(DbModelBuilder modelBuilder)
       {
           modelBuilder.Conventions.Remove
<PluralizingTableNameConvention>();
           modelBuilder.Entity
<Instructor>()
               .HasOptional(p
=> p.OfficeAssignment).WithRequired(p => p.Instructor);
           modelBuilder.Entity
<Course>()
               .HasMany(c
=> c.Instructors).WithMany(i => i.Courses)
               .Map(t
=> t.MapLeftKey("CourseID")
                   .MapRightKey(
"InstructorID")
                   .ToTable(
"CourseInstructor"));
           modelBuilder.Entity
<Department>()
               .HasOptional(x
=> x.Administrator);
       }
   }
}

复制代码

依然很痛苦啊   这要多勇敢 才能把编写model 到再编写数据库映射等 写完  还有这么多api要记什么意思 实在是太痛苦了

不要怕 想用code fisrt  有干净的poco  但又不想写这些的  你们的福音来了  强烈推荐使用

Entity Framework Power Tools

可以根据数据库 一键生成想要的model 与映射    原子中也有介绍的了  介绍连接

生成后 和我们自己写的一样 而且更加规范 统一的映射管理 真的很完美~~


© 著作权归作者所有

短短的歼击机

短短的歼击机

粉丝 82
博文 268
码字总数 269797
作品 0
武汉
高级程序员
私信 提问
MVC3+EF4.1学习系列(十)----MVC+EF处理树形结构

通过前几篇文章 我们处理了 一对一, 一对多,多对多关系 很好的发挥了ORM框架的做用 但是 少说了一种 树形结构的处理, 而这种树形关系 我们也经常遇到,常见的N级类别的处理, 以及经常有数据与...

postdep
2014/02/08
2.1K
0
MVC3+EF4.1学习系列(三)-----排序 刷选 以及分页

上篇文章 已经做出了基本的增删改查 但这远远不足以应付实际的项目 今天讲下实际项目中 肯定会有的 排序 刷选 以及分页。 重点想多写点分页的 毕竟这个是任何时候都要有的 而且 我会尽量把这...

postdep
2014/02/08
91
0
Step by Step-构建自己的ORM系列-开篇

一、开篇 首先、园子里面之前的很多同仁已经讨论过了ORM相关的框架及其优点和缺点。虽然我本篇讨论的有点晚,但是其毕竟优点大于缺点,本文只是简单的介绍我讨 论ORM的目的,及为什么要讨论这...

何戈洲
2011/03/16
252
0
MVC3+EF4.1学习系列(五)----- EF查找导航属性的几种方式

通过上一篇的学习 我们把demo的各种关系终于搭建里起来 以及处理好了如何映射到数据库等问题 但是 只是搭建好了关系 问题还远没有解决 这篇就来写如何查找导航属性 和查找导航属性的几种方式...

postdep
2014/02/08
112
0
一步一步教你使用AgileEAS.NET基础类库进行应用开发-基础篇-演示ORM的基本操作

系统回顾 前面的四篇文章我详细的介绍了AgileEAS.NET平台中统一数据访问(UDA)组件的用法,分析了两种数据处理流程的优缺点,以及基于懒惰模式的委托执行,事务处理方法,基本涵盖了基于数据...

agilelab
2010/09/24
0
0

没有更多内容

加载失败,请刷新页面

加载更多

Spring Boot + Mybatis + Ehcache 二级缓存实例

二级缓存是多个SqlSession共享的,其作用域是mapper的同一个namespace,不同的sqlSession两次执行相同namespace下的sql语句且向sql中传递参数也相同即最终执行相同的sql语句,第一次执行完毕...

xiaolyuh
24分钟前
4
0
Spring源码学习(二)哎呦,按菜谱做菜与AbstractAutowireCapableBeanFactory.createBean流程差不多

记得跟老婆谈恋爱时,有一天心血来潮给老婆做饭,按照菜谱一步一步的做,结果差点把厨房烧了!!! 这事至今老婆还记得。 入口 上一篇说了,AbstractBeanFactory.getBean的主流程 ,今天来说下...

温安适
26分钟前
37
0
前端UI攻城狮 你们该抛弃jQuery了

你不再需要jQuery! Web工程师太依赖jQuery了,某种意义上说jQuery已经成了JavaScript的同义词。但是我们真的需要他么?或许我们应该反思一下什么时候才真的需要jQuery。 对我个人而言开始使...

前端老手
28分钟前
5
0
六、Java设计模式之工厂方法

工厂方法定义: 定义一个创建对象的接口,但让实现这个接口的类来决定实例化哪个类,工厂方法让类的实例化推迟到子类中进行 类型:创建型 工厂方法-使用场景: 创建对象需要大量重复的代码 ...

东风破2019
今天
6
0
win服务器管理遇到的一系列问题记录

有些小伙伴在使用iis7远程桌面管理工具的时候总是会遇到一系列的问题,下面就是为大家介绍一下服务器日常管理过程中出现的问题及我的解决办法和心得。希望能帮到大家。   拒绝服务器重新启...

1717197346
今天
6
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部