我正在开发一个C#控制台应用程序,从公会战争2 API下载数据,并使用Entity Framework 6将其输入到我的数据库中。我试图使用多线程来加速将大量数据输入到我的数据库的过程。
问题在于,当代码运行到我的AddRecipes方法中的DBContext.SaveChanges()调用时,会返回以下错误:
违反了“PK_dbo.Items”主键约束。无法向对象“dbo.Items”中插入重复键值(0)。
以下是与我的问题相关的代码部分:
这是我的DBContext类:
这里还有我的表格类(我知道它们的名称很令人困惑,我正在重新编写它们):
这里有解释Items/Recipes类返回内容的链接: Items Recipes 我注意到,删除外键约束和
问题在于,当代码运行到我的AddRecipes方法中的DBContext.SaveChanges()调用时,会返回以下错误:
违反了“PK_dbo.Items”主键约束。无法向对象“dbo.Items”中插入重复键值(0)。
以下是与我的问题相关的代码部分:
class Program
{
private static ManualResetEvent resetEvent;
private static int nIncompleteThreads = 0;
//Call this function to add to the dbo.Items table
private static void AddItems(object response)
{
string strResponse = (string)response;
using (GWDBContext ctx = new GWDBContext())
{
IEnumerable<Items> itemResponse = JsonConvert.DeserializeObject<IEnumerable<Items>>(strResponse);
ctx.Items.AddRange(itemResponse);
ctx.SaveChanges();
}
if (Interlocked.Decrement(ref nIncompleteThreads) == 0)
{
resetEvent.Set();
}
}
//Call this function to add to the dbo.Recipes table
private static void AddRecipes(object response)
{
string strResponse = (string)response;
using (GWDBContext ctx = new GWDBContext())
{
IEnumerable<Recipes> recipeResponse = JsonConvert.DeserializeObject<IEnumerable<Recipes>>(strResponse);
ctx.Recipes.AddRange(recipeResponse);
foreach(Recipes recipe in recipeResponse)
{
ctx.Ingredients.AddRange(recipe.ingredients);
}
ctx.SaveChanges(); //This is where the error is thrown
}
if (Interlocked.Decrement(ref nIncompleteThreads) == 0)
{
resetEvent.Set();
}
}
static void GetResponse(string strLink, string type)
{
//This method calls the GW2 API through HTTPWebRequest
//and store the responses in a List<string> responseList variable.
GWHelper.GetAllResponses(strLink);
resetEvent = new ManualResetEvent(false);
nIncompleteThreads = GWHelper.responseList.Count();
//ThreadPool.QueueUserWorkItem creates threads for multi-threading
switch (type)
{
case "I":
{
foreach (string strResponse in GWHelper.responseList)
{
ThreadPool.QueueUserWorkItem(new WaitCallback(AddItems), strResponse);
}
break;
}
case "R":
{
foreach (string strResponse in GWHelper.responseList)
{
ThreadPool.QueueUserWorkItem(new WaitCallback(AddRecipes), strResponse);
}
break;
}
}
//Waiting then resetting event and clearing the responseList
resetEvent.WaitOne();
GWHelper.responseList.Clear();
resetEvent.Dispose();
}
static void Main(string[] args)
{
string strItemsLink = "items";
string strRecipesLink = "recipes";
GetResponse(strItemsLink, "I");
GetResponse(strRecipesLink, "R");
Console.WriteLine("Press any key to continue...");
Console.ReadLine();
}
这是我的DBContext类:
public class GWDBContext : DbContext
{
public GWDBContext() : base("name=XenoGWDBConnectionString") { }
public DbSet<Items> Items { get; set; }
public DbSet<Recipes> Recipes { get; set; }
public DbSet<Ingredient> Ingredients { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
}
}
这里还有我的表格类(我知道它们的名称很令人困惑,我正在重新编写它们):
public class Items
{
public Items()
{
Recipes = new HashSet<Recipes>();
Ingredients = new HashSet<Ingredient>();
}
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)] //This attribute makes sure that the id column is not an identity column since the api is sending that).
public int id { get; set; }
.../...
public virtual ICollection<Recipes> Recipes { get; set; }
public virtual ICollection<Ingredient> Ingredients { get; set; }
}
public class Recipes
{
public Recipes()
{
disciplines = new List<string>();
ingredients = new HashSet<Ingredient>();
}
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)] //This attribute makes sure that the id column is not an identity column since the api is sending that).
public int id { get; set; }
public string type { get; set; }
[ForeignKey("Items")] //This attribute points the output_item_id column to the Items table.
.../...
private List<string> _disciplines { get; set; }
public List<string> disciplines
{
get { return _disciplines; }
set { _disciplines = value; }
}
[Required]
public string DisciplineAsString
{
//get; set;
get { return string.Join(",", _disciplines); }
set { _disciplines = value.Split(',').ToList(); }
}
public string chat_link { get; set; }
public virtual ICollection<Ingredient> ingredients { get; set; }
public virtual Items Items { get; set; }
}
public class Ingredient
{
public Ingredient()
{
Recipe = new HashSet<Recipes>();
}
[Key]
public int ingredientID { get; set; }
[ForeignKey("Items")] //This attribute points the item_id column to the Items table.
public int item_id { get; set; }
public int count { get; set; }
public virtual ICollection<Recipes> Recipe { get; set; }
public virtual Items Items { get; set; }
}
这里有解释Items/Recipes类返回内容的链接: Items Recipes 我注意到,删除外键约束和
public virtual Items Items { get; set; }
代码后,数据将保存正确。我认为我的错误与在Recipes类中拥有public virtual Items Items
有关。但从我的理解来看,我需要在类中拥有虚拟变量,以便Entity Framework知道类之间的关系。那么,为什么在我的类中拥有该虚拟变量会导致主键冲突?
Items
对象所导致的,但是我需要查看recipeResponse
的确切内容才能验证这一点。 - Gert Arnold