簡體   English   中英

Entity Framework Core:使用自動生成的外鍵作為復合主鍵的一部分

[英]Entity Framework Core: use auto-generated foreign key as part of composite primary key

我有一個通用的 class TimeSeries<T> ,其中包含通用 state 變量State<T>的列表。 State<T>在某個時間包含 state。

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) 我需要這個,因為有時我需要從我只有讀取權限的第二個數據庫更新值。 這可能嗎?

在 EF-core 3 和 5 中,您可以通過修改 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 core 2 用戶,最后一條語句是:

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

OrderByDescending因為在這種情況下,屬性不是按數組的順序獲取的)。

感謝 Gert,我找到了解決方案:

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();
}

在 .NET 7 中,使用新的 PrimaryKey 屬性現在更容易。 將生成為字符串的外鍵傳遞給它,實體框架將執行 rest。

[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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM