【Entity Framework】你要知道EF中功能序列与值转换

【Entity Framework】你要知道EF中功能序列与值转换

文章目录

  • 【Entity Framework】你要知道EF中功能序列与值转换
    • 一、序列
      • 1.1 基本用法
      • 1.2 配置序列设置
    • 二、值转换
      • 2.1 配置值转换器
      • 2.2 批量配置值转换器
      • 2.3 预定义的转换
      • 2.4 ValueConverter类
      • 2.5 内置转换器
    • 三、应用
      • 3.1 简单值对象
      • 3.2 复合值对象
      • 3.3 基元的集合
      • 3.4 值对象的集合

在这里插入图片描述

一、序列

序列在数据库中生成唯一的顺序数值。 序列不与特定表关联,可以设置多个表以从同一序列提取值。

1.1 基本用法

可以在模型中设置序列,然后使用它为属性生成值:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{modelBuilder.HasSequence<int>("OrderNumbers");modelBuilder.Entity<Order>().Property(o => o.OrderNo).HasDefaultValueSql("NEXT VALUE FOR OrderNumbers");
}

请注意,从序列生成值的特定SQL是特定于数据库的;上面的示例适用于 SQL Server 但在其他数据库上将失败。 有关详细信息,请查阅数据库的文档。

1.2 配置序列设置

还可以配置序列的其他方面,例如其架构、起始值、增量等:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{modelBuilder.HasSequence<int>("OrderNumbers", schema: "shared").StartsAt(1000).IncrementsBy(5);
}

二、值转换

值转换器可在从数据库读取或向其中写入属性值时转换属性值。此转换可以是从同一类型的一个值转换为另一个值(如:加密字符串),也可以是从一种类型的值转换为另一种类型的值(如:数据库中枚举值和字符串的相互转换)。

值转换器的指定涉及 ModelClrTypeProviderClrType。 模型类型是实体类型中的属性的 .NET 类型。 提供程序类型是数据库提供程序理解的 .NET 类型。 例如,若要在数据库中将枚举保存为字符串,模型类型是枚举的类型,而提供程序类型是 String。 这两种类型可以相同。

使用两个 Func 表达式树定义转换:一个从 ModelClrType 转换为 ProviderClrType,另一个从 ProviderClrType 转换为 ModelClrType。 使用表达式树的目的是使它们可被编译到数据库访问委托中,以便进行高效转换。 表达式树可能包含对复杂转换的转换方法的简单调用。

2.1 配置值转换器

值转换在DbContext.OnModelCreating中配置。如,将一个枚举和实体类型定义为:

public class Rider
{public int Id { get; set; }public EquineBeast Mount { get; set; }
}public enum EquineBeast
{Donkey,Mule,Horse,Unicorn
}

可在OnModelCreating中转换进行如下配置:在数据库中将枚举值存储为字符串。可以通过执行从ModelClrTypeProviderClrType的转换,另一个执行反向的转换:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{modelBuilder.Entity<Rider>().Property(e => e.Mount).HasConversion(v => v.ToString(),v => (EquineBeast)Enum.Parse(typeof(EquineBeast), v));
}

绝不会向值转换器传递 null 值。 数据库列中的 null 在实体实例中始终为 null,反之亦然。 这使实现转换更容易,并允许在可为 null 和不可为 null 的属性之间共享转换。

2.2 批量配置值转换器

为使用相关CLR类型的每个属性配置相同的值转换器很常见。可以使用预约定模型配置为整个模型执行一次此操作,而无需手动为每个属性执行此操作。要执行此操作,请将值转换器定义为类:

public class CurrencyConverter : ValueConverter<Currency, decimal>
{public CurrencyConverter(): base(v => v.Amount,v => new Currency(v)){}
}

然后按以下所示在上下文类型中重写ConfigureConventions并配置转换器:

protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder)
{configurationBuilder.Properties<Currency>().HaveConversion<CurrencyConverter>();
}

2.3 预定义的转换

EF Core含有许多预定义转换,不需要手动编写转换函数。而是根据模型中的属性类型和请求的数据库提供程序类型选取要使用的转换。

如:下面的示例中使用了从枚举到字符串的转换,但当提供程序类型配置为string时,EF Core实际上会使用HasConversion的泛型类型自动执行此转换:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{modelBuilder.Entity<Rider>().Property(e => e.Mount).HasConversion<string>();
}

可通过显式地指定数据库列类型实现相同的操作。 例如,如果实体类型的定义如下:

  • 数据注释
public class Rider2
{public int Id { get; set; }[Column(TypeName = "nvarchar(24)")]public EquineBeast Mount { get; set; }
}
  • Flument API
modelBuilder.Entity<Rider2>().Property(e => e.Mount).HasColumnType("nvarchar(24)");

然后,枚举值会被保存为数据库中的字符串,

2.4 ValueConverter类

上面示例调用HasConversion会创建一个ValueConverter<TModel,TProvider>实例并在属性上设置它。可改为显示地创建ValueConverter。示例如:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{var converter = new ValueConverter<EquineBeast, string>(v => v.ToString(),v => (EquineBeast)Enum.Parse(typeof(EquineBeast), v));modelBuilder.Entity<Rider>().Property(e => e.Mount).HasConversion(converter);
}

多个属性使用同一个转换时,这非常有用。

2.5 内置转换器

如上所述,EF Core附带了一组预定义ValueConverter<TModel,TProvider>类,这些类位于Microsoft.EntityFrameworkCore.Storage.ValueConversion命名空间中。 在许多情况下,EF 将根据模型中属性的类型和在数据库中请求的类型,选择适当的内置转换器,正如上面的枚举转换示例所示。 例如,对 bool 属性使用 .HasConversion<int>() 会使 EF Core 将布尔值转换为数值零和一:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{modelBuilder.Entity<User>().Property(e => e.IsActive).HasConversion<int>();
}

这种做法与创建一个内置BoolToZeroOneConverter<TProvider>的实例并进行显式设置在功能上的效果一样:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{var converter = new BoolToZeroOneConverter<int>();modelBuilder.Entity<User>().Property(e => e.IsActive).HasConversion(converter);
}

三、应用

3.1 简单值对象

此示例使用简单类型来包装基元类型。 希望模型中的类型比基元类型更具体(因而更具类型安全性)时,这很有用。 在此示例中,该类型为 Dollars,它包装小数基元:

public readonly struct Dollars
{public Dollars(decimal amount) => Amount = amount;  public decimal Amount { get; }public override string ToString() => $"${Amount}";
}

这可用于实体类型中:

public class Order
{public int Id { get; set; }public Dollars Price { get; set; }
}

还可在存储到数据库中时被转换为基本 decimal

modelBuilder.Entity<Order>().Property(e => e.Price).HasConversion(v => v.Amount,v => new Dollars(v));

3.2 复合值对象

在上一个示例中,值对象类型仅包含一个属性。更常见的是:值对象类型组成共同构成一个域概念的多个属性。如:一个通用的Money类型包含金额和货币。

public readonly struct Money
{[JsonConstructor]public Money(decimal amount, Currency currency){Amount = amount;Currency = currency;}public override string ToString()=> (Currency == Currency.UsDollars ? "$" : "£") + Amount;public decimal Amount { get; }public Currency Currency { get; }
}
public enum Currency
{UsDollars,PoundsSterling
}

可以像以前一样在实体类型中使用此值对象:

public class Order
{public int Id { get; set; }public Money Price { get; set; }
}

值转换器目前只能执行值与一个数据库列之间的转换。此限制意味着对象的所有属性值都必须被编码为一个列值。对此,通常的处理方法是:在对象进入数据库中时序列化该对象,再在它退出数据库时反序列化。如,使用System.Text.Json:

modelBuilder.Entity<Order>().Property(e => e.Price).HasConversion(v => JsonSerializer.Serialize(v, (JsonSerializerOptions)null),v => JsonSerializer.Deserialize<Money>(v, (JsonSerializerOptions)null));

3.3 基元的集合

序列化还可用于存储基元值的集合。 如:

public class Post
{public int Id { get; set; }public string Title { get; set; }public string Contents { get; set; }public ICollection<string> Tags { get; set; }
}

再次使用System.Text.Json:

modelBuilder.Entity<Post>().Property(e => e.Tags).HasConversion(v => JsonSerializer.Serialize(v, (JsonSerializerOptions)null),v => JsonSerializer.Deserialize<List<string>>(v, (JsonSerializerOptions)null),new ValueComparer<ICollection<string>>((c1, c2) => c1.SequenceEqual(c2),c => c.Aggregate(0, (a, v) => HashCode.Combine(a, v.GetHashCode())),c => (ICollection<string>)c.ToList()));

ICollection<string> 表示可变引用类型。 也就是说,需要使用 ValueComparer,这样 EF Core 才能正确地跟踪和监测更改。

3.4 值对象的集合

结合前两个示例,我们可以创建一个值对象集合。 例如,假设有一个 AnnualFinance 类型,它为博客一年的财务状况建模:

public readonly struct AnnualFinance
{[JsonConstructor]public AnnualFinance(int year, Money income, Money expenses){Year = year;Income = income;Expenses = expenses;}public int Year { get; }public Money Income { get; }public Money Expenses { get; }public Money Revenue => new Money(Income.Amount - Expenses.Amount, Income.Currency);
}

此类型构成几个我们先前创建的 Money 类型:

public readonly struct Money
{[JsonConstructor]public Money(decimal amount, Currency currency){Amount = amount;Currency = currency;}public override string ToString()=> (Currency == Currency.UsDollars ? "$" : "£") + Amount;public decimal Amount { get; }public Currency Currency { get; }
}
public enum Currency
{UsDollars,PoundsSterling
}

然后,我们可以向实体类型添加一个 AnnualFinance 集合:

public class Blog
{public int Id { get; set; }public string Name { get; set; }public IList<AnnualFinance> Finances { get; set; }
}

接下来再次使用序列化来进行存储:

modelBuilder.Entity<Blog>().Property(e => e.Finances).HasConversion(v => JsonSerializer.Serialize(v, (JsonSerializerOptions)null),v => JsonSerializer.Deserialize<List<AnnualFinance>>(v, (JsonSerializerOptions)null),new ValueComparer<IList<AnnualFinance>>((c1, c2) => c1.SequenceEqual(c2),c => c.Aggregate(0, (a, v) => HashCode.Combine(a, v.GetHashCode())),c => (IList<AnnualFinance>)c.ToList()));

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

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

相关文章

白盒测试详解

&#x1f345; 视频学习&#xff1a;文末有免费的配套视频可观看 &#x1f345; 关注公众号&#xff1a;互联网杂货铺&#xff0c;回复1 &#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 概念与定义 白盒测试&#xff1a;侧重于系统或部件内部机…

攻防世界1

阅读须知&#xff1a; 探索者安全团队技术文章仅供参考,未经授权请勿利用文章中的技术资料对任何计算机系统进行入侵操作,由于传播、利用本公众号所提供的技术和信息而造成的任何直接或者间接的后果及损失&#xff0c;均由使用者 本人负责&#xff0c;作者不为此承担任何责任,如…

为了执行SQL语句,MySQL的架构是怎样设计的

1. 把MySQL当个黑盒子一样执行SQL语句 上一讲我们已经说到&#xff0c;我们的系统采用数据库连接池的方式去并发访问数据库&#xff0c;然后数据库自己其实也会维护一个连 接池&#xff0c;其中管理了各种系统跟这台数据库服务器建立的所有连接 我们先看下图回顾一下 当我们的…

苹果备忘录误删一段内容怎么恢复?iPhone备忘录恢复的4种方法!收藏!

在使用苹果设备时&#xff0c;备忘录是许多用户常用的工具之一。iPhone备忘录是一款功能强大、易于使用的应用&#xff0c;它能帮助你更好地管理生活和工作。无论你是记录待办事项、设置提醒还是与他人分享信息&#xff0c;备忘录都能满足你的需求。 然而&#xff0c;如果不小…

Mac下载的软件显示文件已损坏,如何解决文件已损坏问题

当在Mac上下载的软件显示文件已损坏时&#xff0c;这可能是因为多种原因导致的&#xff0c;包括网络问题、下载中断、软件未完整下载、文件传输错误等。解决这个问题需要采取一些步骤来排除可能的原因&#xff0c;并尝试修复文件。下面将详细介绍一些常见的解决方法&#xff1a…

单链表链表专题

1 链表的概念 概念&#xff1a;链表是⼀种物理存储结构上⾮连续、⾮顺序的存储结构&#xff0c;数据元素的逻辑顺序是通过链表中的指针链接次序实现的。 链表的结构跟⽕⻋⻋厢相似&#xff0c;淡季时⻋次的⻋厢会相应减少&#xff0c;旺季时⻋次的⻋厢会额外增加⼏节。只 需要…

Laravel 11入门:使用ServBay打造高效开发环境

Laravel 11发布&#xff0c;改进了不少功能。 它引入了更加流畅的应用结构、每秒限速、健康路由等特性。 此外&#xff0c;Laravel还推出了第一方可扩展的WebSocket服务器Laravel Reverb&#xff0c;为你的应用提供强大的实时功能。 在今天的指南中&#xff0c;我将设置一个…

【C语言__动态内存管理__复习篇6】

目录 前言 一、动态内存管理 二、动态内存函数 2.1 malloc 2.2 free 2.3 calloc 2.4 realloc 三、动态内存常见的6个使用错误 3.1 接收malloc/calloc返回的参数后未及时检查是否为NULL 3.2 越界访问动态内存空间 3.3 对非动态开辟的内存使用free释放 3.4 使用free只释放了…

「 典型安全漏洞系列 」14.NoSQL注入漏洞详解

NoSQL注入是一个漏洞&#xff0c;攻击者能够干扰应用程序对NoSQL数据库进行的查询&#xff0c;本文我们将研究如何测试一般的NoSQL漏洞&#xff0c;然后重点研究如何利用MongoDB中的漏洞&#xff08;MongoDB是最流行的NoSQL数据库&#xff09;。 1. 什么是NoSQL注入 NoSQL注入…

[lesson33]C++中的字符串类

C中的字符串类 历史遗留问题 C语言不支持真正意义上的字符串C语言用字符数组和一组函数实现字符串操作C语言不支持自定义类型&#xff0c;因此无法获得字符串类型 解决方案 从C到C的进化过程引入自定义类型在C中可以通过类完成字符串类型的定义 标准库中的字符串类 C语言直…

古月·ROS2入门21讲——学习笔记(一)核心概念部分1-14讲

讲解视频地址&#xff1a;1.ROS和ROS2是什么_哔哩哔哩_bilibili 笔记分为上篇核心概念部分和下篇常用工具部分 下篇&#xff1a;古月ROS2入门21讲——学习笔记&#xff08;二&#xff09;常用工具部分15-21讲-CSDN博客 目录 第一讲&#xff1a;ROS/ROS2是什么 1. ROS的诞生…

Java实现单点登录(SSO)详解:从理论到实践

✨✨谢谢大家捧场&#xff0c;祝屏幕前的小伙伴们每天都有好运相伴左右&#xff0c;一定要天天开心哦&#xff01;✨✨ &#x1f388;&#x1f388;作者主页&#xff1a; 喔的嘛呀&#x1f388;&#x1f388; ✨✨ 帅哥美女们&#xff0c;我们共同加油&#xff01;一起进步&am…