考虑 EF Core 实体的通用类层次结构:
public abstract class Parent<TUserKey>
where TUserKey : IEquatable<TUserKey> // same signature as IdentityUser<TKey>
{
protected Parent() { } // non-public ctor for use by EF
protected Parent(TUserKey? userId) { UserId = userId; }
public long Id { get; }
public TUserKey? UserId { get; } // nullable because optional relationship
// other properties...
}
public sealed class Child : Parent<long>
{
private Child() : base() { } // non-public ctor for use by EF
public Child(long? userId) : base(userId) { } // <----- PROBLEM HERE
// other properties...
}
这不会编译:
无法从“长”转换?到“长”
想法:
where TUserKey : struct, IEquatable<TUserKey>
声明父类。然而,这是不合适的,因为某些子类使用 string
作为类型。public Child(long userId) : base(userId) { }
。然而,许多调用站点都会传递 long?
,所以我需要在任何地方都使用 userId!
。我怀疑我被(2)困住了,但也许我忽略了一些东西。有没有更好的方法,无需进行重大重写?
(顺便说一句:
TUserKey : IEquatable<TUserKey>
的目的是匹配IdentityUser<TKey>
的签名。)
正如@LasseV.Karlsen 的评论中所解释的,可为空的开放通用参数是问题的根源。
这段代码实际上包含在我们主系统使用的帮助程序库中,因此它需要灵活。一些消费者使用值类型作为 PK(例如 int、long、Guid),而其他消费者则使用引用类型(例如字符串)。
因此,另一种方法是将层次结构一分为二:一个用于值,一个用于引用类型。
基类:
public abstract class Parent
{
protected Parent() { }
public long Id { get; }
// other properties...
}
public abstract class ParentValue<TUserKey> : Parent
where TUserKey : struct, IEquatable<TUserKey>
{
protected ParentValue() { }
protected ParentValue(TUserKey? userId) { UserId = userId; }
public TUserKey? UserId { get; }
}
public abstract class ParentReference<TUserKey> : Parent
where TUserKey : class, IEquatable<TUserKey>
{
protected ParentReference() { }
protected ParentReference(TUserKey? userId) { UserId = userId; }
public TUserKey? UserId { get; }
}
消费者将定义一个子类。对于值类型:
public sealed class Child : ParentValue<long>
{
private Child() : base() { }
public Child(long? userId) : base(userId) { }
}
或参考类型:
public sealed class Child : ParentReference<string>
{
private Child() : base() { }
public Child(string? userId) : base(userId) { }
}