You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 22:35:35