C#系列-C#EF框架实现雪花主键(20)

 C#中,使用Entity Framework (EF) 框架并不直接涉及到主键的生成策略。主键的生成通常是在数据库层面或者在应用程序层面处理的。雪花主键(Snowflake ID)是一种在分布式系统中生成唯一ID的算法,它通常是由Twitter的雪花算法演变而来。

雪花主键的特点是在不依赖数据库的情况下,能够在分布式系统中生成全局唯一且有序的64位整数ID。这种ID通常用于大数据场景,因为它们可以有效地利用缓存、分片、以及分布式系统中的其他优化策略。

下面是一个简单的C#实现雪花主键生成器的例子:

csharp代码

public class SnowflakeIdGenerator

{

private const long epoch = 1288834974657L; // 起始时间戳(毫秒)

private const long workerIdBits = 5L; // 机器id所占的位数

private const long datacenterIdBits = 5L; // 数据中心id所占的位数

private const long maxWorkerId = -1L ^ (-1L << workerIdBits); // 支持的最大机器id数量

private const long maxDatacenterId = -1L ^ (-1L << datacenterIdBits); // 支持的最大数据中心id数量

private const long sequenceBits = 12L; // 序列所占的位数

private const long workerIdShift = sequenceBits; // 机器ID左移位数

private const long datacenterIdShift = sequenceBits + workerIdBits; // 数据中心ID左移位数

private const long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits; // 时间戳左移位数

private const long sequenceMask = -1L ^ (-1L << sequenceBits); // 序列掩码

private long workerId; // 机器ID

private long datacenterId; // 数据中心ID

private long sequence = 0L; // 序列

private long lastTimestamp = -1L; // 上次生成ID的时间戳

public SnowflakeIdGenerator(long workerId, long datacenterId)

{

if (workerId > maxWorkerId || workerId < 0)

{

throw new ArgumentException(string.Format("worker Id can't be greater than {0} or less than 0", maxWorkerId));

}

if (datacenterId > maxDatacenterId || datacenterId < 0)

{

throw new ArgumentException(string.Format("datacenter Id can't be greater than {0} or less than 0", maxDatacenterId));

}

this.workerId = workerId;

this.datacenterId = datacenterId;

}

public long NextId()

{

long timestamp = TimeGen();

if (timestamp < lastTimestamp)

{

throw new Exception(string.Format("Clock moved backwards. Refusing to generate id for {0} milliseconds", lastTimestamp - timestamp));

}

if (lastTimestamp == timestamp)

{

sequence = (sequence + 1) & sequenceMask;

if (sequence == 0)

{

timestamp = TilNextMillis(lastTimestamp);

}

}

else

{

sequence = 0L;

}

lastTimestamp = timestamp;

return ((timestamp - epoch) << timestampLeftShift) |

(datacenterId << datacenterIdShift) |

(workerId << workerIdShift) |

sequence;

}

protected long TilNextMillis(long lastTimestamp)

{

long timestamp = TimeGen();

while (timestamp <= lastTimestamp)

{

timestamp = TimeGen();

}

return timestamp;

}

protected long TimeGen()

{

return (long)(DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0)).TotalMilliseconds;

}

}

在这个例子中,SnowflakeIdGenerator 类封装了生成雪花主键的逻辑。构造函数接受机器IDworkerId)和数据中心IDdatacenterId)作为参数,这些ID在分布式系统中应该是唯一的。NextId 方法是生成新ID的入口点。它首先检查当前时间戳是否小于上一次生成ID的时间戳,如果是,则抛出一个异常。然后,根据时间戳是否相同来递增序列或等待下一个毫秒。最后,通过位运算组合时间戳、数据中心ID、机器ID和序列来生成最终的雪花主键。

Entity Framework (EF) 中使用雪花主键生成器,你需要将生成的雪花ID作为实体的主键值。这通常涉及到几个步骤:

  1. 定义实体类,并在其中指定主键属性。
  2. 在DbContext中配置实体,以便EF知道如何使用雪花主键生成器。
  3. 在应用程序启动时初始化雪花主键生成器,并将其注入到需要它的服务中。

下面是一个简单的示例,展示如何在EF Core中使用雪花主键生成器:

首先,定义你的实体类,并指定主键属性:

csharp代码

public class MyEntity

{

public long SnowflakeId { get; set; } // 这是雪花主键

// 其他属性...

}

然后,在DbContext中配置实体,使用HasDefaultValueSql方法来指定在插入新记录时如何生成雪花ID

csharp代码

public class MyDbContext : DbContext

{

public DbSet<MyEntity> MyEntities { get; set; }

protected override void OnModelCreating(ModelBuilder modelBuilder)

{

modelBuilder.Entity()

.HasKey(e => e.SnowflakeId) // 设置SnowflakeId为主键

.ForSqlServerHasDefaultValueSql("DEFAULT(NEXT VALUE FOR SnowflakeSequence)"); // 使用SQL Server的序列

// 其他配置...

}

}

请注意,上面的代码使用了ForSqlServerHasDefaultValueSql方法,这是针对SQL Server数据库的。如果你使用的是其他数据库提供程序,你需要使用相应的方法来配置默认值。

接下来,你需要创建一个雪花ID序列,以便数据库可以生成ID。这通常在数据库迁移脚本中完成。以下是一个SQL Server的示例:

sql代码

CREATE SEQUENCE SnowflakeSequence

AS BIGINT

START WITH 1

INCREMENT BY 1;

然后,在应用程序启动时(例如在Startup.csProgram.cs中),你需要初始化雪花主键生成器,并将其注入到EF Core的上下文中。这可以通过依赖注入来实现:

csharp代码

public void ConfigureServices(IServiceCollection services)

{

// ... 其他服务配置 ...

// 配置雪花主键生成器

services.AddSingleton(serviceProvider =>

{

// 获取配置的服务,例如应用程序设置或配置文件中的workerId和datacenterId

var workerId = // 从配置中获取workerId;

var datacenterId = // 从配置中获取datacenterId;

return new SnowflakeIdGenerator(workerId, datacenterId);

});

// 添加DbContext,并配置依赖注入

services.AddDbContext(options =>

{

// 从依赖注入容器中获取雪花主键生成器

var snowflakeIdGenerator = serviceProvider.GetService();

// 配置DbContext使用雪花主键生成器

options.UseSqlServer(yourConnectionString, sqlServerOptionsAction: sqlOptions =>

{

// 配置其他SQL Server选项...

})

.EnableSensitiveDataLogging(true) // 根据需要启用或禁用

.UseInternalServiceProvider(serviceProvider); // 使用内部服务提供者来解析依赖项

});

// ... 其他服务配置 ...

}

在这个例子中,ISnowflakeIdGenerator是一个接口,它定义了生成雪花ID的方法。你可以根据你的需要实现这个接口,并在依赖注入容器中注册实现。然后,在配置DbContext时,你可以从服务提供者中获取这个实现,并使用它来配置如何生成主键。

请注意,这个方法将雪花ID的生成逻辑从EF Core中分离出来,允许你更容易地测试和替换主键生成策略。此外,这种方法还允许你在不使用EF Core的情况下生成雪花ID,例如在需要手动插入记录到数据库的场景中。

你可能感兴趣的:(C#系列,c#,开发语言)