翼度科技»论坛 编程开发 .net 查看内容

EF7数据库提供者的自定义值生成器

8

主题

8

帖子

24

积分

新手上路

Rank: 1

积分
24
本文将讲解提供者使用值生成器的原理。因代码太多,本文只摘要重要代码,并且删除了代码中的注释。
SqlServer提供者:

Microsoft.EntityFrameworkCore.SqlServer
如下代码:我们可以看到提供者已经帮我们实现好了顺序Guid自定义值生成器
  1. public class SequentialGuidValueGenerator : ValueGenerator<Guid>
  2. {
  3.     private long _counter = DateTime.UtcNow.Ticks;
  4.     public override Guid Next(EntityEntry entry)
  5.     {
  6.         var guidBytes = Guid.NewGuid().ToByteArray();
  7.         var counterBytes = BitConverter.GetBytes(Interlocked.Increment(ref _counter));
  8.         if (!BitConverter.IsLittleEndian)
  9.         {
  10.             Array.Reverse(counterBytes);
  11.         }
  12.         guidBytes[08] = counterBytes[1];
  13.         guidBytes[09] = counterBytes[0];
  14.         guidBytes[10] = counterBytes[7];
  15.         guidBytes[11] = counterBytes[6];
  16.         guidBytes[12] = counterBytes[5];
  17.         guidBytes[13] = counterBytes[4];
  18.         guidBytes[14] = counterBytes[3];
  19.         guidBytes[15] = counterBytes[2];
  20.         return new Guid(guidBytes);
  21.     }
  22.     public override bool GeneratesTemporaryValues => false;
  23. }
复制代码
有了值生成器,我们来看看数据库提供者是如何使用的?首先它帮我们写了生成值选择器,看代码我们会发现。
只有属性为Guid类型,并且是需要生成值的状态下自动使用 SequentialGuidValueGenerator 值生成器。
如下代码所示:
  1. public class SqlServerValueGeneratorSelector : RelationalValueGeneratorSelector
  2. {
  3.     protected override ValueGenerator? FindForType(IProperty property, IEntityType entityType, Type clrType)
  4.         => property.ClrType.UnwrapNullableType() == typeof(Guid)
  5.             ? property.ValueGenerated == ValueGenerated.Never || property.GetDefaultValueSql() != null
  6.                 ? new TemporaryGuidValueGenerator()
  7.                 : new SequentialGuidValueGenerator()
  8.             : base.FindForType(property, entityType, clrType);
  9. }
复制代码
SqlServerValueGeneratorSelector 将附加到 AddEntityFrameworkSqlServer 中第109行。
AddEntityFrameworkSqlServer 将被附加到 ApplyServices 中第66行。
最终 ApplyServices 将为EF 核心内部API 解释服务与功能。
MySql提供者:

Pomelo.EntityFrameworkCore.MySql
首先,MySql提供者与SqlServer提供者的原理是一样的机制。不过为了适配Mysql的uuid(RFC 4122)规范,提供者写了属于Mysql的顺序Guid自定义值生成器
  1. public class MySqlSequentialGuidValueGenerator  : ValueGenerator<Guid>
  2. {
  3.     private readonly IMySqlOptions _options;
  4.     public MySqlSequentialGuidValueGenerator(IMySqlOptions options)
  5.     {
  6.         _options = options;
  7.     }
  8.     private static readonly RandomNumberGenerator _rng = RandomNumberGenerator.Create();
  9.     public override Guid Next(EntityEntry entry)
  10.     {
  11.         return Next();
  12.     }
  13.     public Guid Next()
  14.     {
  15.         return Next(DateTimeOffset.UtcNow);
  16.     }
  17.     public Guid Next(DateTimeOffset timeNow)
  18.     {
  19.         var randomBytes = new byte[7];
  20.         _rng.GetBytes(randomBytes);
  21.         var ticks = (ulong) timeNow.Ticks;
  22.         var uuidVersion = (ushort) 4;
  23.         var uuidVariant = (ushort) 0b1000;
  24.         var ticksAndVersion = (ushort)((ticks << 48 >> 52) | (ushort)(uuidVersion << 12));
  25.         var ticksAndVariant = (byte)  ((ticks << 60 >> 60) | (byte)  (uuidVariant << 4));
  26.         if (_options.ConnectionSettings.GuidFormat == MySqlGuidFormat.LittleEndianBinary16)
  27.         {
  28.             var guidBytes = new byte[16];
  29.             var tickBytes = BitConverter.GetBytes(ticks);
  30.             if (BitConverter.IsLittleEndian)
  31.             {
  32.                 Array.Reverse(tickBytes);
  33.             }
  34.             Buffer.BlockCopy(tickBytes, 0, guidBytes, 0, 6);
  35.             guidBytes[6] = (byte)(ticksAndVersion << 8 >> 8);
  36.             guidBytes[7] = (byte)(ticksAndVersion >> 8);
  37.             guidBytes[8] = ticksAndVariant;
  38.             Buffer.BlockCopy(randomBytes, 0, guidBytes, 9, 7);
  39.             return new Guid(guidBytes);
  40.         }
  41.         var guid = new Guid((uint) (ticks >> 32), (ushort) (ticks << 32 >> 48), ticksAndVersion,
  42.             ticksAndVariant,
  43.             randomBytes[0],
  44.             randomBytes[1],
  45.             randomBytes[2],
  46.             randomBytes[3],
  47.             randomBytes[4],
  48.             randomBytes[5],
  49.             randomBytes[6]);
  50.         return guid;
  51.     }
  52.     public override bool GeneratesTemporaryValues => false;
  53. }
复制代码
与SqlServer提供者同理,值生成器附加到MysqlValueGeneratorSelector,MysqlValueGeneratorSelector又附加到 AddEntityFramwrokMySql中,AddEntityFramwrokMySql又被附加到AppServices中。
使用方式

只有在属性为Guid类型,并且是需要生成值的状态下自动使用 SequentialGuidValueGenerator 值生成器。
  1. builder.Property(p => p.Id).ValueGeneratedOnAdd();
复制代码
总结

我们了解到了EF的数据库提供者使用值生成器的原理,知道了每个数据库的Guid算法不一样。还了解到我们使用EF时,不用过多的关心数据库数据类型值的生成方式,这些提供者都帮我们做到了。

来源:https://www.cnblogs.com/YataoFeng/archive/2023/03/07/17187772.html
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作!

举报 回复 使用道具