Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/263.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 实体框架核心:使用自动生成的外键作为复合主键的一部分_C#_Asp.net_Entity Framework_Entity Framework Core - Fatal编程技术网

C# 实体框架核心:使用自动生成的外键作为复合主键的一部分

C# 实体框架核心:使用自动生成的外键作为复合主键的一部分,c#,asp.net,entity-framework,entity-framework-core,C#,Asp.net,Entity Framework,Entity Framework Core,我有一个泛型类TimeSeries,它包含一个泛型状态变量列表。状态包含特定时间的状态 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 { ge

我有一个泛型类TimeSeries,它包含一个泛型状态变量列表。状态包含特定时间的状态

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; }
}
IntClass和FloatClass包含派生类的实例:

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和State表。它们分别具有自动生成的外键IntClassID和FloatClassID。如果我像这里一样用[Key]注释StateID,那么那当然是主键


然而,我想要的是主键分别是复合日期、IntClassID和日期、FloatClassID。我之所以需要它,是因为我有时需要更新第二个数据库中的值,而我只有读取权限。这可能吗?

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

在ModelCreatingModelBuilder上受保护的覆盖无效modelBuilder { var stateTypes=modelBuilder.Model.GetEntityTypes .其中e=>e.ClrType.IsConstructedGenericType &&e.ClrType.GetGenericTypeDefinition==typeofState 托利斯特先生; 州类型 .ForEacht=>t.SetPrimaryKeyt.FindPropertiesnew[]{StateID,Date}; } 首先收集从状态派生的实体类型,然后设置这些类型的主键属性。使用nameof当然会使运行时更加安全

对于EF core 2用户,最后一句话是:

stateTypes.ForEacht=>t.SetPrimaryKeyt.GetProperties .Wherep=>new[]{StateID,Date}.Containsp.Name .OrderByDescendingp=>p.Name 托利斯特先生;
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();
}

这是哪个EF版本?该项目目前使用的是核心版本2.2,但我可以免费升级。谢谢。我将升级版本并试用。刚刚添加了一个EF core 2改编版本以使其更广泛适用。我已经完成了升级并插入了您的代码。我插入了一个将t映射到外键名的函数,而不是StateID。到目前为止,这是可行的。现在,当我在数据库上调用EnsureCreated时,它告诉我外键也必须是不可为null的。您知道我如何确保生成的密钥的安全性吗?完整异常消息为System.InvalidOperationException:实体类型“State”上的键不能包含属性“FloatClassID”,因为该属性可为空/可选。声明密钥的所有属性都必须标记为不可为空/必需。我在代码中没有发现这个错误,所以我想知道它是从哪里来的。也可以在元数据中更改这一点,但我认为您最好确保所有可能属于PKs的属性都不可为null。例如,如果属性本身是int类型?正如我在问题中所写的,显然可以为null的PK是自动生成的,因此,我不能只更改int?根据你的回答,我找到了一个解决办法。我将把它作为一个单独的评论发布。
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();
}