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

  • EF7数据库提供者的自定义值生成器已关闭评论
  • 109 次浏览
  • A+
所属分类:.NET技术
摘要

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

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

SqlServer提供者:

Microsoft.EntityFrameworkCore.SqlServer
如下代码:我们可以看到提供者已经帮我们实现好了顺序Guid自定义值生成器

public class SequentialGuidValueGenerator : ValueGenerator<Guid> {     private long _counter = DateTime.UtcNow.Ticks;      public override Guid Next(EntityEntry entry)     {         var guidBytes = Guid.NewGuid().ToByteArray();         var counterBytes = BitConverter.GetBytes(Interlocked.Increment(ref _counter));          if (!BitConverter.IsLittleEndian)         {             Array.Reverse(counterBytes);         }          guidBytes[08] = counterBytes[1];         guidBytes[09] = counterBytes[0];         guidBytes[10] = counterBytes[7];         guidBytes[11] = counterBytes[6];         guidBytes[12] = counterBytes[5];         guidBytes[13] = counterBytes[4];         guidBytes[14] = counterBytes[3];         guidBytes[15] = counterBytes[2];          return new Guid(guidBytes);     }      public override bool GeneratesTemporaryValues => false; } 

有了值生成器,我们来看看数据库提供者是如何使用的。
首先它帮我们写了生成值选择器,看代码我们会发现。只有在属性为Guid类型,并且是需要生成值的状态下自动使用 SequentialGuidValueGenerator 值生成器。
如下代码所示:

 public class SqlServerValueGeneratorSelector : RelationalValueGeneratorSelector {     protected override ValueGenerator? FindForType(IProperty property, IEntityType entityType, Type clrType)         => property.ClrType.UnwrapNullableType() == typeof(Guid)             ? property.ValueGenerated == ValueGenerated.Never || property.GetDefaultValueSql() != null                 ? new TemporaryGuidValueGenerator()                 : new SequentialGuidValueGenerator()             : base.FindForType(property, entityType, clrType); } 

SqlServerValueGeneratorSelector 将附加到 AddEntityFrameworkSqlServer 中第109行。
AddEntityFrameworkSqlServer 将被附加到 ApplyServices 中第66行。
最终 ApplyServices 将为EF 核心内部API 解释服务与功能。

MySql提供者:

Pomelo.EntityFrameworkCore.MySql
首先,MySql提供者与SqlServer提供者的原理是一样的机制。不过为了适配Mysql的uuid(RFC 4122)规范,提供者写了属于Mysql的顺序Guid自定义值生成器

public class MySqlSequentialGuidValueGenerator  : ValueGenerator<Guid> {      private readonly IMySqlOptions _options;      public MySqlSequentialGuidValueGenerator(IMySqlOptions options)     {         _options = options;     }      private static readonly RandomNumberGenerator _rng = RandomNumberGenerator.Create();      public override Guid Next(EntityEntry entry)     {         return Next();     }      public Guid Next()     {         return Next(DateTimeOffset.UtcNow);     }      public Guid Next(DateTimeOffset timeNow)     {         var randomBytes = new byte[7];         _rng.GetBytes(randomBytes);         var ticks = (ulong) timeNow.Ticks;          var uuidVersion = (ushort) 4;         var uuidVariant = (ushort) 0b1000;          var ticksAndVersion = (ushort)((ticks << 48 >> 52) | (ushort)(uuidVersion << 12));         var ticksAndVariant = (byte)  ((ticks << 60 >> 60) | (byte)  (uuidVariant << 4));          if (_options.ConnectionSettings.GuidFormat == MySqlGuidFormat.LittleEndianBinary16)         {             var guidBytes = new byte[16];             var tickBytes = BitConverter.GetBytes(ticks);             if (BitConverter.IsLittleEndian)             {                 Array.Reverse(tickBytes);             }              Buffer.BlockCopy(tickBytes, 0, guidBytes, 0, 6);             guidBytes[6] = (byte)(ticksAndVersion << 8 >> 8);             guidBytes[7] = (byte)(ticksAndVersion >> 8);             guidBytes[8] = ticksAndVariant;             Buffer.BlockCopy(randomBytes, 0, guidBytes, 9, 7);              return new Guid(guidBytes);         }          var guid = new Guid((uint) (ticks >> 32), (ushort) (ticks << 32 >> 48), ticksAndVersion,             ticksAndVariant,             randomBytes[0],             randomBytes[1],             randomBytes[2],             randomBytes[3],             randomBytes[4],             randomBytes[5],             randomBytes[6]);          return guid;     }      public override bool GeneratesTemporaryValues => false; }  

与SqlServer提供者同理,值生成器附加到MysqlValueGeneratorSelector,MysqlValueGeneratorSelector又附加到 AddEntityFramwrokMySql中,AddEntityFramwrokMySql又被附加到AppServices中。

使用方式

只有在属性为Guid类型,并且是需要生成值的状态下自动使用 SequentialGuidValueGenerator 值生成器。

builder.Property(p => p.Id).ValueGeneratedOnAdd();  

总结

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