如何配置包含外键的复合主键(EF Fluent API)
How to configure a composite primary key containing a foreign key (EF Fluent API)
我的实体Curve和Point是一对多的关系,定义如下:
public class Curve
{
public Curve()
{
Points = new HashSet<Point>();
}
public int CurveId { get; set; }
public string Name { get; set; }
public virtual ICollection<Point> Points { get; set; }
}
并且:
public class Point
{
public int Point_Num { get; set; }
public int CurveId { get; set; }
public double XCoord { get; set; }
public double YCoord { get; set; }
public virtual Curve Curve { get; set; }
}
在我的上下文 class 中,我按如下方式配置键和导航属性(注意 Point 实体有一个复合主键):
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Curve>()
.HasKey(c => c.CurveId);
modelBuilder.Entity<Point>()
.HasKey(p => new { p.Point_Num, p.CurveId });
modelBuilder.Entity<Point>()
.HasRequired(p => p.Curve)
.WithMany(c => c.Points)
.HasForeignKey(p => p.CurveId);
}
在代码的其他地方,我填充了数据库:
var curve = new Curve
{
Name = "curve 1"
};
var points = new List<Point>();
points.Add(new Point
{
XCoord = 1d,
YCoord = 1d,
});
points.Add(new Point
{
XCoord = 2d,
YCoord = 2d,
});
foreach (var point in points)
{
curve.Points.Add(point);
}
using (var dbCtxt = new MyDbContext())
{
try
{
dbCtxt.Curves.Add(curve);
dbCtxt.SaveChanges();
}
catch (Exception e)
{
Console.WriteLine(e.StackTrace);
}
}
调用 SaveChanges() 时抛出错误 "An error occurred while updating the entries."
如果我将 Point_Num 定义为 Point 实体的主键,则所有实体都可以正常更新。问题似乎出在复合主键 (Point_Num, CurveId) 包含外键 (CurveId)
我无法理解这个问题,显然我缺少一些东西。任何帮助将不胜感激!
所以我已经完成了我首先应该做的事情,即在 SQL 中创建我的表并使用实体数据模型向导在 C# 中生成我的实体(使用 "Code first from database" )
这样做,我注意到 EF 在复合主键上生成了具有以下 "DatabaseGeneratedOption.None" 数据注释的 Point 实体:
public partial class Points
{
[Key]
[Column(Order = 0)]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int Point_Num { get; set; }
[Key]
[Column(Order = 1)]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int Curve_Id { get; set; }
...
public virtual Curve Curve { get; set; }
}
我使用 Fluent API:
实现了这个
modelBuilder.Entity<Point>().Property(p => p.PointId)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
在填充数据库的代码中,我创建了 2 个不同的 "Curve" 实体:
var curve1 = new Curve
{
Name = "curve 1"
};
var curve2 = new Curve
{
Name = "curve 2"
};
然后我创建 "Point" 个指定 PointId 和 CurveId 的实体。例如。假设我想为每条曲线添加一个点:
var points = new List<Point>();
points.Add(new Point
{
PointId = 1,
CurveId = 1,
XCoord = 1.2d,
YCoord = 3.1d,
});
points.Add(new Point
{
PointId = 1,
CurveId = 2,
XCoord = 0.5d,
YCoord = 0.75d,
});
在上下文中添加实体时,添加曲线1和曲线2是不够的,我还需要添加以前创建的点列表,EF不会自动上传它们:
myContext.Points.AddRange(points);
myContext.Curves.Add(curve1);
myContext.Curves.Add(curve2);
myContext.SaveChanges();
这根本不是一个优雅的解决方案。我希望有一种更直接的方法可以做到这一点,但这解决了我的问题!数据库更新正确,所有键和外键设置正确...
我的实体Curve和Point是一对多的关系,定义如下:
public class Curve
{
public Curve()
{
Points = new HashSet<Point>();
}
public int CurveId { get; set; }
public string Name { get; set; }
public virtual ICollection<Point> Points { get; set; }
}
并且:
public class Point
{
public int Point_Num { get; set; }
public int CurveId { get; set; }
public double XCoord { get; set; }
public double YCoord { get; set; }
public virtual Curve Curve { get; set; }
}
在我的上下文 class 中,我按如下方式配置键和导航属性(注意 Point 实体有一个复合主键):
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Curve>()
.HasKey(c => c.CurveId);
modelBuilder.Entity<Point>()
.HasKey(p => new { p.Point_Num, p.CurveId });
modelBuilder.Entity<Point>()
.HasRequired(p => p.Curve)
.WithMany(c => c.Points)
.HasForeignKey(p => p.CurveId);
}
在代码的其他地方,我填充了数据库:
var curve = new Curve
{
Name = "curve 1"
};
var points = new List<Point>();
points.Add(new Point
{
XCoord = 1d,
YCoord = 1d,
});
points.Add(new Point
{
XCoord = 2d,
YCoord = 2d,
});
foreach (var point in points)
{
curve.Points.Add(point);
}
using (var dbCtxt = new MyDbContext())
{
try
{
dbCtxt.Curves.Add(curve);
dbCtxt.SaveChanges();
}
catch (Exception e)
{
Console.WriteLine(e.StackTrace);
}
}
调用 SaveChanges() 时抛出错误 "An error occurred while updating the entries."
如果我将 Point_Num 定义为 Point 实体的主键,则所有实体都可以正常更新。问题似乎出在复合主键 (Point_Num, CurveId) 包含外键 (CurveId)
我无法理解这个问题,显然我缺少一些东西。任何帮助将不胜感激!
所以我已经完成了我首先应该做的事情,即在 SQL 中创建我的表并使用实体数据模型向导在 C# 中生成我的实体(使用 "Code first from database" )
这样做,我注意到 EF 在复合主键上生成了具有以下 "DatabaseGeneratedOption.None" 数据注释的 Point 实体:
public partial class Points
{
[Key]
[Column(Order = 0)]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int Point_Num { get; set; }
[Key]
[Column(Order = 1)]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int Curve_Id { get; set; }
...
public virtual Curve Curve { get; set; }
}
我使用 Fluent API:
实现了这个modelBuilder.Entity<Point>().Property(p => p.PointId)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
在填充数据库的代码中,我创建了 2 个不同的 "Curve" 实体:
var curve1 = new Curve
{
Name = "curve 1"
};
var curve2 = new Curve
{
Name = "curve 2"
};
然后我创建 "Point" 个指定 PointId 和 CurveId 的实体。例如。假设我想为每条曲线添加一个点:
var points = new List<Point>();
points.Add(new Point
{
PointId = 1,
CurveId = 1,
XCoord = 1.2d,
YCoord = 3.1d,
});
points.Add(new Point
{
PointId = 1,
CurveId = 2,
XCoord = 0.5d,
YCoord = 0.75d,
});
在上下文中添加实体时,添加曲线1和曲线2是不够的,我还需要添加以前创建的点列表,EF不会自动上传它们:
myContext.Points.AddRange(points);
myContext.Curves.Add(curve1);
myContext.Curves.Add(curve2);
myContext.SaveChanges();
这根本不是一个优雅的解决方案。我希望有一种更直接的方法可以做到这一点,但这解决了我的问题!数据库更新正确,所有键和外键设置正确...