asp.net 实体框架核心:使用自动生成的外键作为组合主键的一部分

7gcisfzg  于 2022-12-15  发布在  .NET
关注(0)|答案(3)|浏览(94)

我有一个泛型类TimeSeries<T>,它包含一个泛型状态变量State<T>的列表。State<T>包含某个时间的状态。

public class TimeSeries<T>
{
    [Key]
    public int ID { get; set; }
    public List<State<T>> States { get; set; }
}

public class State<T>
{
    [Key]
    public int StateID { get; set; }
    public DateTime Date { get; set; }
    public T State { get; set; }
}

此外,我有两个类IntClassFloatClass,它们都包含一个时间序列,但类型不同,为了获得TimeSeries的两个表,其中一个有一个指向IntClass的外键,另一个有一个指向FloatClass的外键,我从TimeSeries<T>派生了另外两个类,每个类包含一个外键属性。

public class IntTimeSeries : TimeSeries<int>
{
    public int IntClassID{ get; set; }
}
public class FloatTimeSeries : TimeSeries<float>
{
    public int FloatClassID{ get; set; }
}

IntClassFloatClass包含派生类的示例:

public class IntClass
{
    [Key]
    public int ID { get; set; }
    public IntTimeSeries TimeSeries { get; set; }
}
public class FloatClass
{
    [Key]
    public int ID { get; set; }
    public FloatTimeSeries TimeSeries { get; set; }
}

这种方法生成了表State<int>State<float>,它们分别具有(自动生成的)外键IntClassIDFloatClassID,如果我像这里所做的那样用[Key]注解StateID,那么它当然是主键。
然而,我想要的是主键分别是组合(Date, IntClassID)(Date, FloatClassID),我需要这样做是因为我有时需要从第二个数据库更新值,而我在那里只有阅读权限,这可能吗?

au9on6nz

au9on6nz1#

在EF-core 3和5中,您可以通过修改EF的元数据来实现这一点,在以下更高版本中可以轻松访问EF的元数据:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    var stateTypes = modelBuilder.Model.GetEntityTypes()
        .Where(e => e.ClrType.IsConstructedGenericType
            && e.ClrType.GetGenericTypeDefinition() == (typeof(State<>)))
        .ToList();
    stateTypes
        .ForEach(t => t.SetPrimaryKey(t.FindProperties(new[] { "StateID", "Date"} )));
}

这首先收集从State<>派生的实体类型,然后为这些类型设置主键属性。使用nameof当然会使这在运行时更加安全。
对于EF核心2用户,最后一条语句为:

stateTypes.ForEach(t => t.SetPrimaryKey(t.GetProperties()
        .Where(p => new[] { "StateID", "Date" }.Contains(p.Name))
            .OrderByDescending(p => p.Name)
        .ToList()));

OrderByDescending,因为在本例中,属性不是按照数组的顺序获取的)。

mklgxw1f

mklgxw1f2#

多亏了格特,我找到了解决办法:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    var stateTypes = modelBuilder.Model.GetEntityTypes()
        .Where(e => e.ClrType.IsConstructedGenericType
            && e.ClrType.GetGenericTypeDefinition() == (typeof(State<>)))
        .ToList();
    stateTypes.ForEach(t => t.FindProperty(GeneratedForeignKeyName(t)).IsNullable = false);
    stateTypes
        .ForEach(t => t.SetPrimaryKey(t.FindProperties(new[] { GeneratedForeignKeyName(t), "Date"} )));
}

private string GeneratedForeignKeyName(IMutableEntityType t)
{
    if (t.Name.Equals(/* data type namespace here */ + ".State<float>"))
    {
        return "FloatClassID";
    }
    else if (t.Name.Equals(/* data type namespace here */ + ".State<int>"))
    {
        return "IntClassID";
    }
    else throw new ArgumentException();
}
f5emj3cl

f5emj3cl3#

在.NET 7中,使用新的PrimaryKey属性可以更容易地实现这一点。将生成为字符串的外键传递给它,实体框架将完成其余的工作。

[Table("Likes")]
[PrimaryKey("LikedId", "LikedById")]
public sealed class DataUserLike
{
    // Columns
    public required DataUser Liked { get; init; }
    public required DataUser LikedBy { get; init; }
}

字符串
https://learn.microsoft.com/en-us/ef/core/modeling/keys?tabs=data-annotations#tabgroup_2

相关问题