当前位置:首页 > 技术 > 正文内容

EF CodeFirst 数据注解实战指南

访客 技术 2026年7月7日 1

场景一:基础字段映射控制

通过特性精确控制实体属性与数据库列的映射关系:

using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace DemoApp
{
    public class Article
    {
        [Key]
        public int ArticleKey { get; set; }

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

        [MaxLength(100)]
        public string AuthorAlias { get; set; }

        [NotMapped]
        public string ArticleSlug => $"{Headline[..3]}-{AuthorAlias[..2]}".ToLower();
    }
}

对应的仓储上下文:

using System.Data.Entity;

namespace DemoApp
{
    public class AppDbContext : DbContext
    {
        public DbSet<Article> Articles { get; set; }
    }
}

数据操作示例:

using System;
using System.Linq;

namespace DemoApp
{
    class Program
    {
        static void Main()
        {
            SeedData();
            FetchData();
        }

        static void SeedData()
        {
            using var ctx = new AppDbContext();
            ctx.Articles.Add(new Article 
            { 
                Headline = "深入理解EF Core", 
                AuthorAlias = "代码匠人" 
            });
            ctx.SaveChanges();
        }

        static void FetchData()
        {
            using var ctx = new AppDbContext();
            foreach (var item in ctx.Articles)
            {
                Console.WriteLine($"{item.ArticleKey}: {item.Headline} by {item.AuthorAlias}");
            }
        }
    }
}

生成的表结构:

CREATE TABLE [dbo].[Articles] (
    [ArticleKey]   INT            IDENTITY(1,1) NOT NULL,
    [Headline]     NVARCHAR(MAX)  NOT NULL,
    [AuthorAlias]  NVARCHAR(100)  NULL,
    CONSTRAINT [PK_Articles] PRIMARY KEY CLUSTERED ([ArticleKey] ASC)
);

[Key] 标记主键,[Required] 映射为 NOT NULL,[MaxLength] 限制字符串长度,[NotMapped] 排除计算属性持久化。整型主键默认启用自增,字符串默认采用 NVARCHAR(MAX)。

场景二:复合键与复合外键配置

处理多字段联合主键及外键关联:

using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace DemoApp
{
    public class License
    {
        [Key]
        [Column(Order = 0)]
        [DatabaseGenerated(DatabaseGeneratedOption.None)]
        public int LicenseCode { get; set; }

        [Key]
        [Column(Order = 1)]
        public string Authority { get; set; }

        public DateTime ValidFrom { get; set; }
        public DateTime ValidUntil { get; set; }
    }

    public class LicenseLog
    {
        [Key]
        public int LogId { get; set; }

        public DateTime OperationTime { get; set; }
        public string OperationType { get; set; }

        [ForeignKey("License")]
        [Column(Order = 0)]
        public int LicenseCode { get; set; }

        [ForeignKey("License")]
        [Column(Order = 1)]
        public string Authority { get; set; }

        public License License { get; set; }
    }
}

生成的数据库结构:

CREATE TABLE [dbo].[Licenses] (
    [LicenseCode] INT            NOT NULL,
    [Authority]   NVARCHAR(128)  NOT NULL,
    [ValidFrom]   DATETIME       NOT NULL,
    [ValidUntil]  DATETIME       NOT NULL,
    CONSTRAINT [PK_Licenses] PRIMARY KEY CLUSTERED ([LicenseCode] ASC, [Authority] ASC)
);

CREATE TABLE [dbo].[LicenseLogs] (
    [LogId]          INT            IDENTITY(1,1) NOT NULL,
    [OperationTime]  DATETIME       NOT NULL,
    [OperationType]  NVARCHAR(MAX)  NULL,
    [LicenseCode]    INT            NOT NULL,
    [Authority]      NVARCHAR(128)  NOT NULL,
    CONSTRAINT [PK_LicenseLogs] PRIMARY KEY CLUSTERED ([LogId] ASC),
    CONSTRAINT [FK_Logs_Licenses] FOREIGN KEY ([LicenseCode], [Authority]) 
        REFERENCES [dbo].[Licenses] ([LicenseCode], [Authority])
);

CREATE NONCLUSTERED INDEX [IX_LicenseComposite] 
    ON [dbo].[LicenseLogs]([LicenseCode] ASC, [Authority] ASC);

Column(Order) 用于指定复合键中字段的排序优先级,外键字段顺序必须与引用主键保持一致。DatabaseGeneratedOption.None 禁用自增,由应用层赋值;其他选项包括 Identity(数据库生成)和 Computed(计算列)。

场景三:复杂类型的扁平化映射

将值对象嵌入实体表,避免单独建表:

using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace DemoApp
{
    public class Post
    {
        public int PostId { get; set; }

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

        public PostMetadata Meta { get; set; } = new PostMetadata();
    }

    [ComplexType]
    public class PostMetadata
    {
        public DateTime? FirstPublished { get; set; }

        [MaxLength(500)]
        public string Summary { get; set; }
    }
}

上下文配置(仅注册聚合根):

public class AppDbContext : DbContext
{
    public DbSet<Post> Posts { get; set; }
}

数据操作:

using var ctx = new AppDbContext();
ctx.Posts.Add(new Post
{
    Subject = "设计模式精讲",
    Meta = new PostMetadata
    {
        FirstPublished = DateTime.UtcNow,
        Summary = "本文深入剖析常用设计模式..."
    }
});
ctx.SaveChanges();

生成的表结构:

CREATE TABLE [dbo].[Posts] (
    [PostId]                   INT            IDENTITY(1,1) NOT NULL,
    [Subject]                  NVARCHAR(MAX)  NOT NULL,
    [Meta_FirstPublished]      DATETIME       NULL,
    [Meta_Summary]             NVARCHAR(500)  NULL,
    CONSTRAINT [PK_Posts] PRIMARY KEY CLUSTERED ([PostId] ASC)
);

复杂类型无独立主键,其属性以 {ComplexType}_{Property} 命名模式展开到引用实体的表中。若类未显式标记 [ComplexType] 且无实体引用,EF 也会自动识别为复杂类型。

场景四:表名与列自定义映射

重写默认命名规则,适配遗留数据库或团队规范:

using System.ComponentModel.DataAnnotations.Schema;

namespace DemoApp
{
    [Table("cms_Content", Schema = "content")]
    public class Post
    {
        public int PostId { get; set; }

        [Column("BodyText", TypeName = "ntext")]
        public string Content { get; set; }
    }
}

生成的表结构:

CREATE TABLE [content].[cms_Content] (
    [PostId]    INT    IDENTITY(1,1) NOT NULL,
    [BodyText]  NTEXT  NULL,
    CONSTRAINT [PK_cms_Content] PRIMARY KEY CLUSTERED ([PostId] ASC)
);

[Table] 特性指定表名与架构,[Column] 特性可重命名列并显式声明数据类型(如 NTEXT、VARCHAR 等)。

场景五:并发冲突检测机制

实现乐观并发控制,防止更新丢失:

using System;
using System.ComponentModel.DataAnnotations;

namespace DemoApp
{
    public class Document
    {
        public int DocId { get; set; }

        [ConcurrencyCheck]
        public string EditorName { get; set; }

        [Timestamp]
        public byte[] RowVersion { get; set; }
    }
}

生成的表结构:

CREATE TABLE [dbo].[Documents] (
    [DocId]       INT            IDENTITY(1,1) NOT NULL,
    [EditorName]  NVARCHAR(MAX)  NULL,
    [RowVersion]  ROWVERSION     NOT NULL,
    CONSTRAINT [PK_Documents] PRIMARY KEY CLUSTERED ([DocId] ASC)
);

[Timestamp] 映射为数据库的 ROWVERSION 类型,每次更新自动变更。[ConcurrencyCheck] 标记的字段在生成 UPDATE 语句时,会将原始值加入 WHERE 条件进行校验。若执行 SaveChanges 时检测到记录已被修改,将抛出 DbUpdateConcurrencyException

返回列表

上一篇:迭代器模式:实现集合遍历的设计方案

没有最新的文章了...

相关文章

Linux crontab 详解

1) crontab 是什么cron 是 Linux 的定时任务守护进程;crontab 是用来编辑/查看“按时间周期执行命令”的表(cron table)。常见两类:用户 crontab:每个用户一份(crontab -e 编辑)系统级 crontab / cron.d:可指定执行用户(/etc/crontab、/etc/cron.d/*)2) crontab 时间...

富文本里可以允许的 HTML 属性

一、所有标签默认允许的安全属性(极少)class        (可选)id           (通常建议禁用)title️ 注意:id 容易被滥用做锚点注入,很多系统直接禁用class 允许的话最好只允许固定前缀(如 editor-*)二、a 标签允许属性<a href="" t...

Mac 安装 Node.js 指南

方法一:通过官网安装包(最简单,适合初学者)如果你只是想快速安装并开始使用,这是最直接的方法。访问 Node.js 官网。页面会显示两个版本:LTS (Recommended For Most Users):长期支持版,最稳定。建议选这个。Current:最新特性版,包含最新功能但可能不够稳定。下载 .pkg 安装包并运行。按照安装向导点击“下一步”即可完成。方法二:使用 Homebrew 安装(...

Dom\HTML_NO_DEFAULT_NS 的副作用:自动加闭合标签

在使用Dom\HTMLDocument时,Dom\HTML_NO_DEFAULT_NS 将禁止在解析过程中设置元素的命名空间, 此设置是为了与DOMDocument向后兼容而存在的。当使用它时,已知的一个副作用就是:自动加闭合标签例如 </img> 为什么会这样?当你使用:Dom\HTML_NO_DEFAULT_NS文档会变成 无命名空间模式,此时内部更接近 XML...

Laravel 事件和监听器创建

在 Laravel 中,使用 Artisan 命令创建 Events(事件) 和 Listeners(监听器) 是非常高效的。你可以通过以下几种方式来实现:1. 手动创建单个 Event如果你只想创建一个事件类,可以使用 make:event 命令:Bashphp artisan make:event UserRegistered执行后,文件将生成在 app/Even...

自定义域名解析神器 dnsmasq

什么是 dnsmasq?dnsmasq 是一个轻量级、功能强大的网络服务工具,专为小型和中等规模网络设计。它是一个综合的网络基础设施解决方案[1]。dnsmasq 能做什么?功能说明应用场景DNS 转发与缓存将 DNS 查询转发到上游服务器(ISP、Google DNS 等),并在本地缓存结果加快 DNS 查询速度,减少外部 DNS 流量本地 DNS解析本地网络设备的主机名,无需编辑&n...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。