【Entity Framework】Code First 数据批注

【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.hqwc.cn/news/572430.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

用c++实现猴子偷桃、Fibonacci数列

4.1.2 猴子吃桃 【问题】一只猴子摘了很多桃子&#xff0c;每天吃现有桃子的一半多一个&#xff0c;到第10天时只有一个桃子&#xff0c;问原有桃子多少个? 【想法】设an表示第n天桃子的个数&#xff0c;猴子吃桃问题存在如下递推式&#xff1a; 【算法实现】由于每天的桃子…

vue2 export default写法,computed、methods的使用

<template><div><h2>{{nameAll}}</h2><h2>{{method}}</h2><h2>{{tt()}}</h2><h2>{{firstName}}</h2><h2>更新后赋值数据&#xff1a;{{lastName}}</h2><h2>赋值数据:{{writeValue}}</h2>…

[Qt] QString::fromLocal8Bit 的使用误区

QString::fromLocal8Bit 是一个平台相关的函数。默认情况下在 Windows 下 就是 gbk 转 utf-8 ,在 Linux就应该是无事发生。因为Linux平台默认的编码方式就是 utf-8 可以通过 void QTextCodec::setCodecForLocale(QTextCodec *c)来修改 Qt默认的编码方式。如下 第一输出乱码的…

网页版短信平台软件开发要点|手机短信系统搭建建设

开发网页版短信平台软件时&#xff0c;需要考虑以下关键要点&#xff0c;以确保平台功能完喂、性能稳定和用户体验良好&#xff1a; 用户管理&#xff1a;实现用户注册、登录、Q:290615413权限管理等功能&#xff0c;确保用户信息安全可控。 短信发送功能&#xff1a;集成短信…

Flutter开发之下标

Flutter开发之下标 在iOS开发中使用下标就很方便&#xff0c;本文主要是记录一下Flutter中系统自带的下标&#xff0c;还可以通过对应的方法编写自己的下标。 在Objective-C中的下标 关键字Subscript。 NSArray - (ObjectType)objectAtIndexedSubscript:(NSUInteger)idx A…

EFI Driver Model(下)-SCSI 驱动设计

1、SCSI简介 SCSI是Small Computer System Interface&#xff08;小型计算机系统接口&#xff09;的缩写&#xff0c;使用50针接口&#xff0c;外观和普通硬盘接口有些相似。SCSI硬盘和普通IDE硬盘相比有很多优点&#xff1a;接口速度快&#xff0c;并且由于主要用于服务器&…

通往荣耀之路! 在 The Sandbox 中种植树木,拯救真正的森林

The Sandbox 团队祝你国际森林日快乐&#xff01; 我们相信&#xff0c;在创造一个更美好、更包容、更友善的地球的过程中&#xff0c;我们每个人都有责任采取具有影响力和目的性的行动。这就是为什么我们平台的核心支柱是利用元宇宙来推动公益事业。 国际森林日是我们践行这一…

数据分析之POWER Piovt透视表分析与KPI设置

将几个数据表之间进行关联 生成数据透视表 超级透视表这里的字段包含子字段 这三个月份在前面的解决办法 1.选中这三个月份&#xff0c;鼠标可移动的时候移动到后面 2.在原数据进行修改 添加列获取月份&#xff0c;借助month的函数双击日期 选择月份这列----按列排序-----选择月…

2024年目前阿里云服务器一个月收费价格表多少钱?

阿里云服务器一个月多少钱&#xff1f;最便宜5元1个月。阿里云轻量应用服务器2核2G3M配置61元一年&#xff0c;折合5元一个月&#xff0c;2核4G服务器30元3个月&#xff0c;2核2G3M带宽服务器99元12个月&#xff0c;轻量应用服务器2核4G4M带宽165元12个月&#xff0c;4核16G服务…

Django开发复盘

一、URL 对于一个不会写正则表达式的蒟蒻来说&#xff0c;在urls.py中就只能傻傻的写死名字&#xff0c;但是即便这样&#xff0c;还会有很多相对路径和绝对路径的问题&#xff08;相对ip端口的路径&#xff09;&#xff0c;因为我们网页中涉及到页面跳转&#xff0c;涉及到发送…

机器学习复习手册

机器学习的要素 基本概念 泛化&#xff1a; The ability to predict accurately the target for new examples that differ from those used in the training set is known as generalization.监督学习&#xff1a;The training data comprises examples of the input variab…

python在运行时控制台以表格形式输出结果prettytable.PrettyTable()

使用prettytable库按表格的形式美化输出结果 效果如图&#xff1a; 表格中可接收列表格式的数据&#xff0c;列表中装字符串 # 引入模块 import prettytable as pt# 创建表格与表头&#xff0c;包含五列&#xff0c;分别为train-epoch&#xff0c;class&#xff0c;precisio…