我正在开发一个C#控制台应用程序,该应用程序可以从GuildWars2API下载数据,并将其与实体框架6一起输入到我的数据库中。我正在尝试使用多线程,以便加快将大量数据输入数据库的过程。
问题是,当代码在我的DBContext.SaveChanges()方法中运行到我的AddRecipes调用时,会返回以下错误:
违反主键约束'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; }
}下面的链接解释了Item/Recipes类返回的内容:
我注意到,删除外键约束和public virtual Items Items { get; set; }代码后,数据将被正确保存。我相信我的错误与Recipes类中的public virtual Items Items有关。但根据我的理解,我需要在类中有这个虚拟变量,这样实体框架就可以知道类之间的关系。那么,为什么我的类中有这个虚拟变量会导致引发主键冲突呢?
发布于 2017-07-25 15:25:05
您只需要菜谱中的项目列表。如果您需要搜索哪些菜谱中有特定项,则可以通过搜索菜谱的外键(项目的主键)来实现。
代码中存在一个根本的命名缺陷。您有一个类累赘,然后是一个名为累赘的列表。物品也是一样。
然后,您将有一个用于累进的外键[ForeignKey("Items")]。这是什么东西?列表或对象项。很容易出错。
将类重命名为Recipe和Item
public class Recipe
{
public Recipe()
public class Item
{
public Item()此外,正如注释中提到的那样,使用重复的Id of 0,听起来似乎没有设置Id。
查看到累赘的链接:
{
.../...
"ingredients": [
{ "item_id": 19684, "count": 50 },
{ "item_id": 19721, "count": 1 },
{ "item_id": 46747, "count": 10 }
],
"id": 7319,
.../...
}食谱不应包含项目清单,而应包含配料清单。班级结构应是:
Recipe has:
public virtual ICollection<Ingredient> Ingredients { get; set; }
Ingredient has:
public virtual ICollection<Item> Items { get; set; }Item类没有成分或Recipe列表,这些列表是通过使用匹配项目主键的外键查询配料上的数据库项来检索的,或者在配方外键上的数据库成分与配料的主键匹配-然后您可以做一个连接来查找这些成分的任何项。
因此,进行以下更改:
在项目类中不需要提到食谱或配料。
public Item() // remove pluralisation
{
// Remove these from the constructor,
// Recipes = new HashSet<Recipes>();
// Ingredients = new HashSet<Ingredient>();
}
// remove these from the class.
// public virtual ICollection<Recipes> Recipes { get; set; }
// public virtual ICollection<Ingredient> Ingredients { get; set; }--一种成分有许多项目--因此,它是一组项目
public Ingredient()
{
// You don't need a collection of Recipes -
// you need a collection of Items.
// Recipe = new HashSet<Recipes>();
}
.../...
[ForeignKey("Item")] // change this
public Item Item // include an Item object - the PK of the
// Item is the FK of the Ingredient
.../...
// Remove Recipes
// public virtual ICollection<Recipes> Recipe { get; set; }
public virtual ICollection<Item> Items { get; set; }我更喜欢对变量项使用对象名称。
--食谱有很多种成分--因此,一个成分的集合,
public Recipes()
{
disciplines = new List<string>();
ingredients = new HashSet<Ingredient>();
}
.../...
// [ForeignKey("Items")] remove this
[ForeignKey("Ingredient")]
public Ingredient Ingredient // include as Ingredient object
// the PK of the Ingredient is the FK for the Recipe
.../...
public virtual ICollection<Ingredient> Ingredients { get; set; }
// The Recipe does not have an Item, the Ingredient has
// has a collection of <Item> Items
// public virtual Items Items { get; set; }另外,我也不知道为什么要使用Hashset。如果你没有什么特别的理由使用的话,我会把它们列在清单上。
如果这不能修复您的代码,我将检查它的其余部分。
https://stackoverflow.com/questions/45260221
复制相似问题