【Entity Framework】Code First 数据批注

news2024/11/16 5:58:56

【Entity Framework】Code First 数据批注

文章目录

  • 【Entity Framework】Code First 数据批注
    • 一、概述
    • 二、模型
    • 二、键Key
    • 三、组合键
    • 四、外键-ForeigKey
        • 第一种:指定导航属性,会自动生成外键,命名规则为:“对象名称_主键名“
        • 第二种:默认情况下与导航属性的主键名称相同的字段会自动被标记为外键
        • 第三种方法:可指定生成的数据库中的列名。
        • 第四种方法:可指定生成的数据库中的列名。
    • 五、验证-Required
    • 六、MaxLength 和 MinLength
    • 七、NotMapped
    • 八、ComplexType
    • 九、ComplexType
    • 十、表和列
    • 十一、DatabaseGenerated
    • 十二、索引
    • 十三、多列索引
    • 十四、关系属性:InverseProperty 和 ForeignKey
    • 十五、总结

在这里插入图片描述

一、概述

利用实体框架Code First,可以使用自己的域类来表示EF执行查询,更改跟踪和更新功能所依赖的模型。Code First利用称为“约定优于配置”的编程模式。Code First将假设你的类遵循实体框架的约定,在这种情况下,将自动确定如何执行其工作。如果需要你的类不遵循这些约定,可以向类添加配置以向EF提供必要的信息。

Code First 提供了两种将这些配置添加到类的方法。

  • 一种方法是使用称为 DataAnnotations 的简单属性
  • 第二种方法是使用 Code First 的 Fluent API,它提供了一种在代码中强制描述配置的方法

本文将重点介绍使用DataAnnotations(System.ComponentModel.DataAnnotations命名空间中)来配置类。并重点介绍最常用的配置。许多.NET应用程序也能理解DataAnnotations。

二、模型

我将使用一对简单的类来演示 Code First DataAnnotations:Blog 和 Post。

public class Blog
{
    public int Id { get; set; }
    public string Title { get; set; }
    public string BloggerName { get; set;}
    public virtual ICollection<Post> Posts { get; set; }
}

public class Post
{
    public int Id { get; set; }
    public string Title { get; set; }
    public DateTime DateCreated { get; set; }
    public string Content { get; set; }
    public int BlogId { get; set; }
    public ICollection<Comment> Comments { get; set; }
}

Blog 和 Post 类可以方便地遵循 Code First 约定,无需调整即可启用 EF 兼容性。 但是,也可以使用注释向 EF 提供有关这些类和它们映射到的数据库的更多信息。

二、键Key

实体框架依赖于每个具有用于实体跟踪的键值的实体。Code First的一个约定是隐式键属性;Code First将查找名为"Id"的属性,或类名和"Id"的组合,例如“BlogId”,此属性将映射到数据库中的主键列。

Blog和Post类都遵循这个约定。如果没有,会引发异常。这个是因为Code First要求实例框架必须具有键属性。可以使用注释来指定哪个属性用作EntityKey

public class Blog
{
    [Key]
    public int PrimaryTrackingKey { get; set; }
    public string Title { get; set; }
    public string BloggerName { get; set;}
    public virtual ICollection<Post> Posts { get; set; }
}

三、组合键

实体框架支持组合键,既由多个属性组合的主键。如:可以有一个Passport类,其主键是PassportNumber和IssuingCountry的组合。

public class Passport
{
     [Key]
     public int PassportNumber { get; set; }
     [Key]
     public string IssuingCountry { get; set; }
     public DateTime Issued { get; set; }
     public DateTime Expires { get; set; }
}

尝试在EF模型中使用上述类会导致InvalidOperationException:无法确定“Passport”类型的组合主键顺序。使用ColumnAttributeHasKey方法来指定组合主键的顺序。

若要使用组合键,实体框架要求定义键属性的顺序。为此,可以使用Column注释指定顺序。

Column顺序值是相对的(而不是基于索引的),因此可以使用任意值,如:可以使用100和200替代1和2

public class Passport
{
    [key]
    [Column(Order=1)]
    public int PassportNumber{get;set;}
    [key]
    [Column(Order=2)]
    public string IssuingCountry { get; set; }
    public DateTime Issued { get; set; }
    public DateTime Expires { get; set; }
}

如果实体具有组合外键,则必须指定用于相应主键属性的相同列顺序

四、外键-ForeigKey

所有一对一和一对多关系均由依赖端上的外键所定义,用于引用主体端上的主键或备用键。为了方便起见,此主键或备用键称为关系的"主键"。多对多关系由两个一对多关系组成,每个关系本身由引用主键的外键所定义。

第一种:指定导航属性,会自动生成外键,命名规则为:“对象名称_主键名“
public class TUsers
{
     [Key]
     public int UserId { get; set; }
     public string Account { get; set; }
     public string Password { get; set; }
     public DateTime CreateDate { get; set; }
     public List<TUsersRoles> TUsersRolesList { get; set; }
}
public class TUsersExtInfo
{
     [Key]
     public int id { get; set; }
     //生产的外键名称是Users_UserId,格式为"对象名称_主角名称"
     public virtual TUsers Users { get; set; }
}
第二种:默认情况下与导航属性的主键名称相同的字段会自动被标记为外键
public class TUsers
{
     [Key]
     public int UserId { get; set; }
     public string Account { get; set; }
     public string Password { get; set; }
     public DateTime CreateDate { get; set; }
}
public class TUsersExtInfo
{
     [Key]
     public int id { get; set; }
     public int UserID { get; set; }
     //如果没有声明TUsers对象,则UserID是一个普通的字段,没有外键关系
     public virtual TUsers Users { get; set; }
}
第三种方法:可指定生成的数据库中的列名。
public class TUsers
{
    [Key]
    public int UserId { get; set; }
    public string Account { get; set; }
    public string Password { get; set; }
    public DateTime CreateDate { get; set; }
}
public class TUsersExtInfo
{
    [Key]
    public int id { get; set; }
    public int TUsers_Id { get; set; }
    [ForeignKey("TUsers_Id")]
    public virtual TUsers Users { get; set; }
}
第四种方法:可指定生成的数据库中的列名。

方式2的升级版,与导航属性的主键名称相同的字段会自动被标记为外键,然后指定字段对应的数据库中的列名

public class TUsers
{
    [Key]
    public int UserId { get; set; }
    public string Account { get; set; }
    public string Password { get; set; }
    public DateTime CreateDate { get; set; }
}
public class TUsersExtInfo
{
    [Key]
    public int id { get; set; }
    [Column("TUsers_Id")]
    public int UserId { get; set; }
    public virtual TUsers Users { get; set; }
}

五、验证-Required

Required 注释告诉 EF 需要特定属性。Required 属性还将通过使映射属性不可为 null 来影响生成的数据库。 请注意,Title 字段已更改为“非 null”。

[Required]
public string Title { get; set; }

在某些情况下,即使此属性是必需的,数据库中的该列也可能为 null。 例如,使用 TPH 继承策略时,多种类型的数据存储在单个表中。 如果派生的类型包含所需的属性,则该列可能为 null,因为层次结构中的所有类型并非都具有此属性。

六、MaxLength 和 MinLength

MaxLengthMinLength 特性使你可以指定额外的属性验证,就像对 Required 执行的操作一样。

[MaxLength(10),MinLength(5)]
public string BloggerName { get; set; }

七、NotMapped

Code First 约定规定,支持的数据类型的每个属性都在数据库中表示。 但在应用程序中并非总是如此。 如:Blog类中可能有一个属性,该属性根据Title和BloggerName字段创建代码,该属性可以动态创建,不需要存储。可以使用NotMapped注释标记未映射到数据库的任何属性。

[NotMapped]
public string BlogCode
{
    get
    {
            return Title.Substring(0, 1) + ":" + BloggerName.Substring(0, 1);
    }
}

八、ComplexType

跨一组类描述域实体,然后将这些类分层以描述完整实体的情况并不少见。 例如,可以向模型中添加一个名为 BlogDetails 的类。

public class BlogDetails
{
    public DateTime? DateCreated { get; set; }
    [MaxLength(250)]
    public string Description { get; set; }
}

请注意,BlogDetails 没有任何类型的键属性。 在域驱动设计中,BlogDetails 被称为值对象。 实体框架将值对象称为复杂类型。 不能单独跟踪复杂类型。

但是,作为 Blog 类中的属性,BlogDetails 将作为 Blog 对象的一部分进行跟踪。 为了让 Code First 识别此项,必须将 BlogDetails 类标记为 ComplexType

[ComplexType]
public class BlogDetails
{
     public DateTime? DateCreated { get; set; }
     [MaxLength(250)]
     public string Description { get; set; }
}

九、ComplexType

使用 ConcurrencyCheck 注释,可以标记一个或多个属性,以便在用户编辑或删除实体时,将该属性用于数据库中的并发检查。 如果使用的是 EF 设计器,这与将属性的 ConcurrencyMode 设置为 Fixed 一致。

让我们通过将 ConcurrencyCheck 添加到 BloggerName 属性来看看它是如何工作的。

[ConcurrencyCheck, MaxLength(10, ErrorMessage="BloggerName must be 10 characters or less"),MinLength(5)]
public string BloggerName { get; set; }

十、表和列

如果允许 Code First 创建数据库,则可能需要更改其要创建的表和列的名称。 还可以将 Code First 与现有数据库一起使用。 但是,域中的类和属性的名称并不总是与数据库中的表和列的名称相匹配。

我的类名为 Blog,按照惯例,Code First 会假定这将映射到名为 Blogs 的表。 如果不是这种情况,则可以使用 Table 属性指定表的名称。 例如,这里的注释指定表名称为 InternalBlogs。

[Table("InternalBlogs")]
public class Blog;

Column 注释更擅长指定映射列的属性。 可以规定名称、数据类型甚至列在表中显示的顺序。 以下是 Column 属性的示例。

[Column("BlogDescription", TypeName="ntext")]
public String Description {get;set;}

十一、DatabaseGenerated

一个重要的数据库特性是具有计算属性的能力。 如果要将 Code First 类映射到包含计算列的表,则不希望实体框架尝试更新这些列。 但是,在插入或更新数据后,你确实需要 EF 从数据库返回这些值。 可以使用 DatabaseGenerated 注释与 Computed 枚举一起标记类中的这些属性。 其他枚举为 NoneIdentity

[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public DateTime DateCreated { get; set; }

当 Code First 生成数据库时,可以使用在字节或时间戳列上生成的数据库,否则只应在指向现有数据库时使用此数据库,因为 Code First 将无法确定计算列的公式。

如上所述,在默认情况下,整数的键属性将成为数据库中的标识键。 这与将 DatabaseGenerated 设置为 DatabaseGeneratedOption.Identity 相同。 如果不希望它成为标识键,则可以将该值设置为 DatabaseGeneratedOption.None

十二、索引

可以使用 IndexAttribute 在一个或多个列上创建索引。 将属性添加到一个或多个属性时,将导致 EF 在创建数据库时在数据库中创建相应的索引,或者如果使用的是 Code First Migrations,则将为相应的 CreateIndex 调用基架。

例如,以下代码将导致在数据库中 Posts 表的 Rating 列上创建索引。

public class Post
{
     public int Id { get; set; }
     public string Title { get; set; }
     public string Content { get; set; }
     [Index]
     public int Rating { get; set; }
     public int BlogId { get; set; }
}

默认情况下,索引将命名为 IX_<属性名称>(上例中为 IX_Rating)。 也可以为索引指定一个名称。 以下示例指定索引应命名为 PostRatingIndex

[Index("PostRatingIndex")]
public int Rating { get; set; }

十三、多列索引

通过在给定表的多个索引注释中使用相同的名称来指定跨越多个列的索引。 创建多列索引时,需要为索引中的列指定顺序。 例如,以下代码在 RatingBlogId 上创建了一个名为 IX_BlogIdAndRating 的多列索引。 BlogId 是索引中的第一列,Rating 是第二列。

public class Post
{
     public int Id { get; set; }
     public string Title { get; set; }
     public string Content { get; set; }
     [Index("IX_BlogIdAndRating", 2)]
     public int Rating { get; set; }
     [Index("IX_BlogIdAndRating", 1)]
     public int BlogId { get; set; }
}

十四、关系属性:InverseProperty 和 ForeignKey

Code First 约定将处理模型中最常见的关系,但在某些情况下需要帮助。

更改 Blog 类中键属性的名称会导致其与 Post 的关系出现问题。

生成数据库时,Code First 看到 Post 类中的 BlogId 属性,并按照约定将其识别为与类名加 Id 相匹配的 Blog 类的外键。 但是 Blog 类中没有 BlogId 属性。 对此的解决方案是在 Post 中创建导航属性,并使用 ForeignKey DataAnnotation 帮助 Code First 了解如何生成两个类之间的关系(使用 Post.BlogId 属性),以及如何指定数据库中的约束。

[InverseProperty("CreatedBy")]
public List<Post> PostsWritten { get; set; }
[InverseProperty("UpdatedBy")]
public List<Post> PostsUpdated { get; set; }

十五、总结

DataAnnotations 不仅使你能够在 Code First 类中描述客户端和服务器端验证,而且还可以增强甚至更正 Code First 将根据其约定对类做出的假设。 使用 DataAnnotations,不仅可以驱动数据库模式生成,还可以将 Code First 类映射到预先存在的数据库。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1548912.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

YOLOv9改进策略:注意力机制 | 二阶通道注意力机制(Second-order Channel Attention,SOCA),实现单图超分效果

&#x1f4a1;&#x1f4a1;&#x1f4a1;本文改进内容&#xff1a;CVPR_2019 SOCA注意力&#xff0c;一种基于二阶通道注意力机制&#xff0c;能够单幅图像超分辨率&#xff0c;从原理角度分析能够在小目标检测领域实现大幅涨点效果&#xff01;&#xff01;&#xff01; &am…

文件编辑命令—vim

1.vim vim 是vi的升级版本.vi 文件名(vi方向键用不了) vim 的官方网站 (welcome home : vim online) 自己也说 vim 是一个程序开发工具而不是文字处理软件。 2.安装vim sudo apt install vim 如果出错了:apt update:刷新软件源; 出现"无法获得锁 之类的"sudo rm 文件…

Vit Transformer

一 VitTransformer 介绍 vit : An Image is Worth 16x16 Words: Transformers for Image Recognition at Scale 论文是基于Attention Is All You Need&#xff0c;由于图像数据和词数据数据格式不一样&#xff0c;经典的transformer不能处理图像数据&#xff0c;在视觉领域的应…

【jenkins+cmake+svn管理c++项目】Windows环境安装以及工具配置

一、目标和环境 目标&#xff1a;搭建一个jenkins环境&#xff0c;实现jenkins调用cmake和svn和VS编译c项目&#xff0c;并将生成的库上传svn。 环境&#xff1a;win10虚拟机&#xff08;练习流程用&#xff0c;正式用的话还是放到服务器&#xff09;&#xff0c;VS2017. 二、…

肿瘤靶向肽 iRGD peptide环肽 1392278-76-0 c(CRGDKGPDC)

RGD环肽 c(CRGDKGPDC)&#xff0c;iRGD peptide 1392278-76-0 结 构 式&#xff1a; H2N-CRGDKGPDC-OH(Disulfide Bridge:C1-C9) H2N-Cys-Arg-Gly-Asp-Lys-Gly-Pro-Asp-Cys-COOH(Disulfide Bridge:Cys1-Cys9) 氨基酸个数&#xff1a; 9 C35H57N13O14S2 平均分子量:…

智能优化算法 | Matlab实现牛顿-拉夫逊优化算法Newton-Raphson-based optimize(内含完整源码)

文章目录 效果一览文章概述源码设计参考资料效果一览 文章概述 智能优化算法 | Matlab实现牛顿-拉夫逊优化算法Newton-Raphson-based optimize(内含完整源码) 源码设计 % ------------------------------------------------------------------------------------------------…

GIS与Python机器学习:开创地质灾害风险评价新纪元

地质灾害是指全球地壳自然地质演化过程中&#xff0c;由于地球内动力、外动力或者人为地质动力作用下导致的自然地质和人类的自然灾害突发事件。由于降水、地震等自然作用下&#xff0c;地质灾害在世界范围内频繁发生。我国除滑坡灾害外&#xff0c;还包括崩塌、泥石流、地面沉…

55、Qt/事件机制相关学习20240326

一、代码实现设置闹钟&#xff0c;到时间后语音提醒用户。示意图如下&#xff1a; 代码&#xff1a; #include "widget.h" #include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget), speecher(new QTextToSpeech(t…

【Redis】Redis 介绍Redis 为什么这么快?Redis数据结构Redis 和Memcache区别 ?为何Redis单线程效率也高?

目录 Redis 介绍 Redis 为什么这么快&#xff1f; Redis数据结构 Redis 和Memcache区别 &#xff1f; 为何Redis单线程效率也高&#xff1f; Redis 介绍 Redis 是一个开源&#xff08;BSD 许可&#xff09;、基于内存、支持多种数据结构的存储系统&#xff0c;可以作为数据…

【Linux】从零开始认识进程 — 中下篇

送给大家一句话&#xff1a; 人一切的痛苦&#xff0c;本质上都是对自己无能的愤怒。而自律&#xff0c;恰恰是解决人生痛苦的根本途径。—— 王小波 从零认识进程 1 进程优先级1.1 什么是优先级1.2 为什么要有优先级1.3 Linux优先级的特点 && 查看方式1.4 其他概念 2…

1.6.1 变换

我们要想改变物体的位置&#xff0c;现有解决办法是&#xff0c;每一帧改变物体的顶点并且重配置缓冲区从而使物体移动&#xff0c;但是这样太繁琐&#xff0c;更好的解决方式是使用矩阵&#xff08;Matrix&#xff09;来更好的变换&#xff08;Transform&#xff09;一个物体。…

Python更改Word文档的页面大小

页面大小确定文档中每个页面的尺寸和布局。在某些情况下&#xff0c;您可能需要自定义页面大小以满足特定要求。在这种情况下&#xff0c;Python可以帮助您。通过利用Python&#xff0c;您可以自动化更改Word文档中页面大小的过程&#xff0c;节省时间和精力。本文将介绍如何使…

每日一题 --- 删除链表的倒数第 N 个结点[力扣][Go]

删除链表的倒数第 N 个结点 题目&#xff1a;19. 删除链表的倒数第 N 个结点 给你一个链表&#xff0c;删除链表的倒数第 n 个结点&#xff0c;并且返回链表的头结点。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4,5], n 2 输出&#xff1a;[1,2,3,5]示例 2&#x…

Python中lambda函数使用方法

在Python中&#xff0c;lambda 关键字用于创建匿名函数&#xff08;无名函数&#xff09;&#xff0c;这些函数的特点是简洁、一次性使用&#xff0c;并且通常用于只需要一行表达式的简单场景。下面是lambda函数的基本结构和使用方法&#xff1a; 基本语法&#xff1a; lambd…

代码随想录算法训练营DAY7| C++哈希表Part.2|LeetCode:454.四数相加II、383.赎金信、15. 三数之和、18.四数之和

文章目录 454.四数相加II思路C代码 383.赎金信C 代码 15. 三数之和排序哈希法思路C代码 排序双指针法思路去重C代码 18.四数之和前言剪枝C代码 454.四数相加II 力扣题目链接 文章链接&#xff1a;454.四数相加II 视频链接&#xff1a;学透哈希表&#xff0c;map使用有技巧&…

STL的基本概念

一、STL的诞生 长久以来&#xff0c;软件界一直希望建立一种可重复利用的东西 C的面向对象和泛型编程思想&#xff0c;目的就是复用性的提升 面向对象的三大特性(简单理解) 封装&#xff1a;把属性和行为抽象出来作为一个整体来实现事和物 继承&#xff1a;子类继承父类&a…

linux下docker容器的使用

1、根据已有镜像images创建容器 1.1、查看镜像 如果是接手的别人的项目&#xff0c;需要从以往的images镜像中创建新容器&#xff0c;使用命令查看当前机器上的docker镜像&#xff1a; docker images1.2、创建容器 使用docker run 根据images镜像名创建容器&#xff0c;命令…

电阻的妙用:限流、分压、滤波,助力电路设计!

电阻可以降低电压&#xff0c;这是通过电阻的分压来实现的。事实上&#xff0c;利用电阻来降低电压只是电阻的多种功能之一。电路中的电阻与其他元件&#xff08;电容、电感&#xff09;结合用于限流、滤波等。&#xff08;本文素材来源&#xff1a;https://www.icdhs.com/news…

SV-7045V网络草坪音箱 室外网络广播POE供电石头音箱

SV-7045V网络草坪音箱 室外网络广播POE供电石头音箱 描述 IP网络广播草坪音箱 SV-7045V是深圳锐科达电子有限公司的一款防水网络草坪音箱&#xff0c;具有10/100M以太网接口&#xff0c;可将网络音源通过自带的功放和喇叭输出播放&#xff0c;可达到功率20W。用在公园&#…

Kotlin高效App爬取工具:利用HttpClient与代理服务器的技巧

在当今数字化时代&#xff0c;移动应用&#xff08;App&#xff09;数据的价值日益凸显&#xff0c;而为了获取并分析这些数据&#xff0c;开发高效的数据爬取工具变得至关重要。Kotlin作为一种现代化、功能强大的编程语言&#xff0c;与HttpClient等强大工具的结合&#xff0c…