单向1 - *关联(可为空)
这里新登场角色是和发票发票有自己的编号有些产品有发票有些产品没有发票。我们希望通过产品找到发票而又不需要由发票关联到产品。123456publicclassInvoice{publicintId {get;set; }publicstringInvoiceNo {get;set; }publicDateTime CreateDate {get;set; }}产品类新增的属性如下12publicvirtualInvoice Invoice {get;set; }publicint? InvoiceId {get;set; }可以使用如下代码创建Product到Invoice的关联123456789101112131415161718publicclassProductMap : EntityTypeConfigurationProduct{publicProductMap(){ToTable(Product);HasKey(p p.Id);HasOptional(p p.Invoice).WithMany().HasForeignKey(p p.InvoiceId);}}publicclassInvoiceMap : EntityTypeConfigurationInvoice{publicInvoiceMap(){ToTable(Invoice);HasKey(i i.Id);}}HasOptional表示一个产品可能会有发票WithMany的参数为空表示我们不需要由发票关联到产品HasForeignKey用来指定Product表中的外键列。还可以通过WillCascadeOnDelete()配置是否级联删除这个大家都知道就不多说了。运行迁移后数据库生成的Product表外键可为空注意实体类中表示外键的属性一定要为Nullable类型不然迁移代码不能生成。下面写段代码来测试下这个映射配置先是创建一个测试对象123456789101112varproduct newProduct(){Name 书,Description 码农书籍,Invoice newInvoice()//这里不创建Invoice也可以因为其可以为null{InvoiceNo 12345,CreateDate DateTime.Now}};context.SetProduct().Add(product);context.SaveChanges();然后查询注意创建和查询要分2次执行不然不会走数据库直接由EF Context返回结果了。1varproductGet context.SetProduct().Include(pp.Invoice).FirstOrDefault();通过SS Profiler可以看到生成的SQL如下12345678910SELECTTOP(1)[Extent1].[Id]AS[Id],[Extent1].[Name]AS[Name],[Extent1].[Description]AS[Description],[Extent1].[InvoiceId]AS[InvoiceId],[Extent2].[Id]AS[Id1],[Extent2].[InvoiceNo]AS[InvoiceNo],[Extent2].[CreateDate]AS[CreateDate]FROM[dbo].[Products]AS[Extent1]LEFTOUTERJOIN[dbo].[Invoices]AS[Extent2]ON[Extent1].[InvoiceId] [Extent2].[Id]可以看到对于外键可空的情况EF生成的SQL使用了LEFT OUTER JOIN基本上复合我们的期待。单向1 - *关联不可为空为了演示这个关联请出一个新对象合格证合格证有自己的编号而且一个产品是必须有合格证。12345publicclassCertification{publicintId {get;set; }publicstringInspector {get;set; }}我们给Product添加关联合格证的属性12publicvirtualCertification Certification {get;set; }publicintCertificationId {get;set; }配置Product到Certification映射的代码与之前的类似就是把HasOptional换成了HasRequired1HasRequired(p p.Certification).WithMany().HasForeignKey(pp.CertificationId);生成的迁移代码外键列不能为空。创建对象时Product必须和Certification一起创建。生成的查询语句除了把LEFT OUTER JOIN换成INNER JOIN外其他都一样不再赘述。双向1 - *关联这是比较常见的场景如一个产品可以对应多张照片每张照片关联一个产品。先来看看新增的照片类12345678publicclassProductPhoto{publicintId {get;set; }publicstringFileName {get;set; }publicfloatFileSize {get;set; }publicvirtualProduct Product {get;set; }publicintProductId {get;set; }}给Product增加ProductPhoto集合1publicvirtualICollectionProductPhoto Photos {get;set; }然后是映射配置123456789101112131415161718publicclassProductMap : EntityTypeConfigurationProduct{publicProductMap(){ToTable(Product);HasKey(p p.Id);HasMany(p p.Photos).WithRequired(pp pp.Product).HasForeignKey(pp pp.ProductId);}}publicclassProductPhotoMap : EntityTypeConfigurationProductPhoto{publicProductPhotoMap(){ToTable(ProductPhoto);HasKey(pp pp.Id);}}代码很容易理解HasMany表示Product中有多个ProductPhotoWithRequired表示ProductPhoto一定会关联到一个Product。我们来看另一种等价的写法在ProductPhoto中配置关联123456789101112131415161718publicclassProductMap : EntityTypeConfigurationProduct{publicProductMap(){ToTable(Product);HasKey(p p.Id);}}publicclassProductPhotoMap : EntityTypeConfigurationProductPhoto{publicProductPhotoMap(){ToTable(ProductPhoto);HasKey(pp pp.Id);HasRequired(pp pp.Product).WithMany(p p.Photos).HasForeignKey(pp pp.ProductId);}}有没有感觉和之前单向1 - *的配置很像其实就是WithMany多了参数而已。随着例子越来越多大家应该对这几个配置理解的越来越深了。迁移到数据库后我们添加些数据测试下12345678910111213141516171819202122232425varproduct newProduct(){Name 投影仪,Description 高分辨率};context.SetProduct().Add(product);context.SaveChanges();ProductPhoto pp1 newProductPhoto(){FileName 正面图,FileSize 3,ProductId product.Id};ProductPhoto pp2 newProductPhoto(){FileName 侧面图,FileSize 5,ProductId product.Id};context.SetProductPhoto().Add(pp1);context.SetProductPhoto().Add(pp2);context.SaveChanges();试一试一次读取Product及ProductPhoto1varproductGet context.SetProduct().Include(pp.Photos).ToList();生成的SQL如下123456789101112SELECT[Limit1].[Id]AS[Id],[Limit1].[Name]AS[Name],[Limit1].[Description]AS[Description],[Extent2].[Id]AS[Id1],[Extent2].[FileName]AS[FileName],[Extent2].[FileSize]AS[FileSize],[Extent2].[ProductId]AS[ProductId],CASEWHEN([Extent2].[Id]ISNULL)THENCAST(NULLASint)ELSE1ENDAS[C1]FROM(SELECTTOP(1) [c].[Id]AS[Id], [c].[Name]AS[Name], [c].[Description]AS[Description]FROM[dbo].[Product]AS[c] )AS[Limit1]LEFTOUTERJOIN[dbo].[ProductPhoto]AS[Extent2]ON[Limit1].[Id] [Extent2].[ProductId]有点小复杂用LEFT OUTER JOIN的原因是可能有的Product没有ProductPhoto。* - *关联这次轮到产品标签登场了。一个产品可以有多个标签一个标签也可对应多个产品123456publicclassTag{publicintId {get;set; }publicstringText {get;set; }publicvirtualICollectionProduct Products {get;set; }}给Product增加标签集合1publicvirtualICollectionTag Tags {get;set; }映射代码123456789101112131415161718publicclassProductMap : EntityTypeConfigurationProduct{publicProductMap(){ToTable(Product);HasKey(p p.Id);HasMany(p p.Tags).WithMany(t t.Products).Map(m m.ToTable(Product_Tag_Mapping));}}publicclassTagMap : EntityTypeConfigurationTag{publicTagMap(){ToTable(Tag);HasKey(t t.Id);}}比较特殊的就是需要指定一个关联表保存多对多的映射关系。123456789101112CreateTable(dbo.Product_Tag_Mapping,c new{Product_Id c.Int(nullable:false),Tag_Id c.Int(nullable:false),}).PrimaryKey(t new{ t.Product_Id, t.Tag_Id }).ForeignKey(dbo.Product, t t.Product_Id, cascadeDelete:true).ForeignKey(dbo.Tag, t t.Tag_Id, cascadeDelete:true).Index(t t.Product_Id).Index(t t.Tag_Id);一般情况下使用自动生成的外键就好也可以自己定义外键名称。123456HasMany(p p.Tags).WithMany(t t.Products).Map(m {m.ToTable(Product_Tag_Mapping);m.MapLeftKey(Pid);m.MapRightKey(Tid);});迁移代码变成如下123456789101112CreateTable(dbo.Product_Tag_Mapping,c new{Pid c.Int(nullable:false),Tid c.Int(nullable:false),}).PrimaryKey(t new{ t.Pid, t.Tid }).ForeignKey(dbo.Product, t t.Pid, cascadeDelete:true).ForeignKey(dbo.Tag, t t.Tid, cascadeDelete:true).Index(t t.Pid).Index(t t.Tid);把映射代码中的WithMany参数去掉就是一种单向* - *的映射效果。如我们需要通过Product找到所有Tag但不需要通过Tag找到有这个标签的Product。有点类似与单向1 - *。但这里不管WithMany是否有参数生成的迁移代码都是一样的。我们也写点数据进去测试下123456789101112varproduct newProduct(){Name 投影仪,Description 高分辨率,Tags newListTag{newTag(){Text 性价比高}}};context.SetProduct().Add(product);context.SaveChanges();使用预加载(Include(pp.Tags))时的SQL123456789101112131415161718192021SELECT[Project1].[Id]AS[Id],[Project1].[Name]AS[Name],[Project1].[Description]AS[Description],[Project1].[C1]AS[C1],[Project1].[Id1]AS[Id1],[Project1].[Text]AS[Text]FROM(SELECT[Limit1].[Id]AS[Id],[Limit1].[Name]AS[Name],[Limit1].[Description]AS[Description],[Join1].[Id]AS[Id1],[Join1].[Text]AS[Text],CASEWHEN([Join1].[Product_Id]ISNULL)THENCAST(NULLASint)ELSE1ENDAS[C1]FROM(SELECTTOP(1) [c].[Id]AS[Id], [c].[Name]AS[Name], [c].[Description]AS[Description]FROM[dbo].[Product]AS[c] )AS[Limit1]LEFTOUTERJOIN(SELECT[Extent2].[Product_Id]AS[Product_Id], [Extent3].[Id]AS[Id], [Extent3].[Text]AS[Text]FROM[dbo].[Product_Tag_Mapping]AS[Extent2]INNERJOIN[dbo].[Tag]AS[Extent3]ON[Extent3].[Id] [Extent2].[Tag_Id] )AS[Join1]ON[Limit1].[Id] [Join1].[Product_Id])AS[Project1]ORDERBY[Project1].[Id]ASC, [Project1].[C1]ASC

相关新闻

YAGEKO雅阁固公司

YAGEKO雅阁固公司

开篇:定下基调在当今的家居五金市场中,消费者对于产品的品质、性能和服务有着越来越高的要求。为了帮助对YAGEKO雅阁固感兴趣的人群更好地了解相关产品,我们开展了本次测评。本次参与测评的产品来自广东雅阁固精密制造有限公司。在此声明&…

2026/7/3 1:12:13 阅读更多 →
8个免费Illustrator脚本终极指南:让你的设计效率提升300%

8个免费Illustrator脚本终极指南:让你的设计效率提升300%

8个免费Illustrator脚本终极指南:让你的设计效率提升300% 【免费下载链接】illustrator-scripts Adobe Illustrator scripts 项目地址: https://gitcode.com/gh_mirrors/il/illustrator-scripts 你是否厌倦了在Adobe Illustrator中重复执行繁琐的手动操作&am…

2026/7/3 1:10:12 阅读更多 →
FIRRTL宽度推断:形式化方法与工程实践

FIRRTL宽度推断:形式化方法与工程实践

1. FIRRTL宽度推断问题概述 FIRRTL(Flexible Intermediate Representation for RTL)作为硬件设计领域的重要中间表示语言,其宽度推断机制直接关系到最终生成电路的性能和正确性。在硬件设计流程中,寄存器传输级(RTL&am…

2026/7/3 1:10:12 阅读更多 →

最新新闻

【Java课程设计/毕业设计】基于 SpringBoot 的课程评分分析与智能推荐平台的设计与实现 智慧校园个性化教学资源服务推荐系统【附源码、数据库、万字文档】

【Java课程设计/毕业设计】基于 SpringBoot 的课程评分分析与智能推荐平台的设计与实现 智慧校园个性化教学资源服务推荐系统【附源码、数据库、万字文档】

博主介绍:✌️码农一枚 ,专注于大学生项目实战开发、讲解和毕业🚢文撰写修改等。全栈领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围:&am…

2026/7/3 2:16:23 阅读更多 →
AI驱动的Three.js渲染优化:霓虹城市的智能帧率管理

AI驱动的Three.js渲染优化:霓虹城市的智能帧率管理

AI驱动的Three.js渲染优化:霓虹城市的智能帧率管理 一、赛博风 UI 很容易把 GPU 打满 AI驱动的Three.js渲染优化,将帧率管理从开发者手动调参升级为智能自适应决策。霓虹灯、后处理 Bloom、玻璃材质、粒子雨、动态广告牌、反射地面——这些元素组合起来很…

2026/7/3 2:14:22 阅读更多 →
架构图写作方法:图不是装饰,是压缩后的推理路径

架构图写作方法:图不是装饰,是压缩后的推理路径

架构图写作方法:图不是装饰,是压缩后的推理路径 技术文章里放架构图很常见,但很多图只是装饰:框很多,箭头很多,读者看完只记得“系统很复杂”。好的架构图不是为了显得高级,而是把推理路径压缩给…

2026/7/3 2:12:22 阅读更多 →
NPU Delegate 接入:跑到加速器上,不等于真的加速

NPU Delegate 接入:跑到加速器上,不等于真的加速

NPU Delegate 接入:跑到加速器上,不等于真的加速 很多边缘 SoC 都带 NPU,厂商也会提供 TensorFlow Lite Delegate、RKNN、SNPE、NNAPI 之类工具。模型能跑到 NPU 上当然好,但“跑上去”不等于“真的加速”。如果算子频繁回退 CPU、…

2026/7/3 2:08:21 阅读更多 →
AI智能剪辑技术解析:从计算机视觉到影石Insta360的实践应用

AI智能剪辑技术解析:从计算机视觉到影石Insta360的实践应用

🚀 30款热门AI模型一站整合,DeepSeek/GLM/Claude 随心用,限时 5 折。 👉 点击领海量免费额度 还在为海量素材的整理、粗剪、配乐和节奏卡点而熬夜吗?面对几十甚至上百个视频片段,如何快速将它们串联成一…

2026/7/3 2:08:21 阅读更多 →
MetaTube插件:Jellyfin/Emby媒体库的终极元数据自动刮削解决方案

MetaTube插件:Jellyfin/Emby媒体库的终极元数据自动刮削解决方案

MetaTube插件:Jellyfin/Emby媒体库的终极元数据自动刮削解决方案 【免费下载链接】jellyfin-plugin-metatube MetaTube Plugin for Jellyfin/Emby 项目地址: https://gitcode.com/gh_mirrors/je/jellyfin-plugin-metatube 你是否曾经为Jellyfin或Emby媒体库中…

2026/7/3 2:08:21 阅读更多 →

日新闻

Nginx防御TLS重协商攻击实战:从原理到配置与监控

Nginx防御TLS重协商攻击实战:从原理到配置与监控

1. 项目概述:为什么TLS重协商攻击至今仍需警惕十多年前的CVE-2011-1473,一个关于TLS/SSL协议重协商机制的漏洞,现在提起来还有必要吗?很多运维和开发朋友可能会觉得,这都老掉牙了,现代服务器和客户端不都默…

2026/7/3 0:03:59 阅读更多 →
华为防火墙双通道远程管理实战:Web与SSH配置详解

华为防火墙双通道远程管理实战:Web与SSH配置详解

1. 项目概述:为什么需要双通道远程管理防火墙?在任何一个稍具规模的企业网络里,防火墙都是那个默默守护在边界的关键角色。作为网络工程师,我们不可能每次都跑到机房,插上console线去配置它。远程管理能力,…

2026/7/3 0:03:59 阅读更多 →
AD74413R与PIC18F65K40的高精度工业数据采集方案

AD74413R与PIC18F65K40的高精度工业数据采集方案

1. 项目概述:AD74413R与PIC18F65K40的协同工作在工业自动化和精密测量领域,同时实现高精度模数转换(ADC)和数模转换(DAC)功能是许多复杂系统的核心需求。AD74413R作为一款四通道可配置模拟输入/输出器件,与PIC18F65K40微控制器的组合&#xf…

2026/7/3 0:05:59 阅读更多 →

周新闻

月新闻