.NET 7多数据库适配下实体强类型ID的实现方案问询
解决方案思路
1. 领域层定义强类型ID
在MyLib中创建通用强类型ID基类,再针对每个实体实现专属强类型ID,确保领域模型只依赖统一的强类型ID,不耦合任何数据库特定类型。
示例代码
// 通用强类型ID基类 public abstract class StronglyTypedId<TValue> : IEquatable<StronglyTypedId<TValue>> where TValue : notnull { public TValue Value { get; } protected StronglyTypedId(TValue value) { Value = value ?? throw new ArgumentNullException(nameof(value)); } public bool Equals(StronglyTypedId<TValue>? other) { if (other is null) return false; return EqualityComparer<TValue>.Default.Equals(Value, other.Value); } public override bool Equals(object? obj) { return obj is StronglyTypedId<TValue> other && Equals(other); } public override int GetHashCode() { return Value.GetHashCode(); } public static bool operator ==(StronglyTypedId<TValue>? left, StronglyTypedId<TValue>? right) { return Equals(left, right); } public static bool operator !=(StronglyTypedId<TValue>? left, StronglyTypedId<TValue>? right) { return !Equals(left, right); } public override string ToString() { return Value.ToString() ?? string.Empty; } } // User实体专属强类型ID public sealed class UserId : StronglyTypedId<object> { private UserId(object value) : base(value) { } // 供各数据库子库调用的创建方法 public static UserId FromGuid(Guid guid) => new(guid); public static UserId FromString(string id) => new(id); public static UserId FromLiteDbObjectId(ObjectId objectId) => new(objectId); // 供各子库调用的类型转换方法 public Guid AsGuid() => (Guid)Value; public string AsString() => Value.ToString()!; public ObjectId AsLiteDbObjectId() => (ObjectId)Value; }
修改User实体
public class User { public UserId Id { get; private set; } public string Name { get; set; } public string Address { get; set; } // 构造函数确保ID正确初始化 public User(UserId id, string name, string address) { Id = id; Name = name; Address = address; } }
2. 各数据库子库实现类型映射
每个子库负责将强类型ID转换为对应数据库所需的类型,通过ORM的映射/转换机制实现,隔离数据库特定逻辑。
2.1 MyLib.EfCore 适配
使用EF Core值转换器实现UserId与Guid的双向转换:
public static class EfCoreExtensions { public static IServiceCollection WithEfCore(this IServiceCollection services, string connectionString) { services.AddDbContext<MyLibEfCoreDbContext>(options => options.UseSqlServer(connectionString)); services.AddScoped<IUserRepository, EfCoreUserRepository>(); return services; } } public class MyLibEfCoreDbContext : DbContext { public DbSet<User> Users { get; set; } protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<User>(entity => { entity.Property(u => u.Id) .HasConversion( id => id.AsGuid(), guid => UserId.FromGuid(guid)) .IsRequired(); }); } }
2.2 MyLib.RavenDb 适配
通过RavenDB的文档约定注册ID转换逻辑:
public static class RavenDbExtensions { public static IServiceCollection WithRavenDb(this IServiceCollection services, string databaseUrl, string databaseName) { services.AddSingleton<IAsyncDocumentSession>(sp => { var store = new DocumentStore { Urls = new[] { databaseUrl }, Database = databaseName }; store.Initialize(); // 配置User的ID属性识别与转换 store.Conventions.FindIdentityProperty = prop => prop.Name == nameof(User.Id); store.Conventions.IdentityConvertors.Register<UserId>( id => id.AsString(), str => UserId.FromString(str)); return store.OpenAsyncSession(); }); services.AddScoped<IUserRepository, RavenDbUserRepository>(); return services; } }
2.3 MyLib.LiteDb 适配
使用LiteDB的BsonMapper注册类型转换:
public static class LiteDbExtensions { public static IServiceCollection WithLiteDb(this IServiceCollection services, string connectionString) { var mapper = BsonMapper.Global; mapper.RegisterType( serialize: id => id.AsLiteDbObjectId(), deserialize: bson => UserId.FromLiteDbObjectId(bson.AsObjectId)); services.AddSingleton<ILiteDatabase>(sp => new LiteDatabase(connectionString, mapper)); services.AddScoped<IUserRepository, LiteDbUserRepository>(); return services; } }
3. 保持原有链式配置风格
主库UseMyLib方法注册领域服务,子库WithXxx方法注册对应数据库的仓储与映射,保持原有调用习惯:
// 应用程序配置示例 builder.Services .UseMyLib() // 注册领域服务等核心依赖 .WithRavenDb("http://localhost:8080", "MyLibDb"); // 或.WithEfCore("YourConnectionString") // 或.WithLiteDb("Filename=MyLib.db;Connection=Shared");
方案优势
- 领域模型完全独立,不依赖任何数据库类型,符合DDD设计原则。
- 数据库特定转换逻辑被隔离在对应子库,实现关注点分离。
- 避免了实体中冗余的多类型ID属性,保持领域模型简洁。
内容的提问来源于stack exchange,提问作者abk
相关产品推荐
相关产品推荐

