简体   繁体   English

实体框架M:1关系导致主键重复

[英]Entity Framework M:1 relationship resulting in primay key duplication

I'm somewhat new to EF 6.0 so I'm pretty sure I'm doing something wrong here. 我是EF 6.0的新手,所以我很确定自己在这里做错了。 there are two questions related to the problem 有两个与此问题有关的问题

  1. what am I doing wrong here 我在这里做错了什么
  2. what's the best practice to achieve this 什么是实现这一目标的最佳实践

I'm using a code first model, and used the edmx designer to design the model and relationships, the system needs to pull information periodically from a webservice and save it to a local database (SQL Lite) in a desktop application 我正在使用代码优先模型,并使用edmx设计器设计模型和关系,系统需要定期从Web服务提取信息并将其保存到桌面应用程序中的本地数据库(SQL Lite)中

在此处输入图片说明

so I get an order list from the API, when I populate and try to save Ticket, I get a duplicate key exception when trying to insert TicketSeatType - 因此我从API获取订单列表,当我填充并尝试保存Ticket时,尝试插入TicketSeatType时出现重复的键异常-

how do I insert the ticket to dbContext, so that It doesn't try and re-insert insert TicketSeatType and TicketPriceType, I have tried setting the child object states to unchanged but it seems to be inserting 我如何将票证插入dbContext,以便它不会尝试重新插入Insert TicketSeatType和TicketPriceType,我尝试将子对象状态设置为不变,但似乎正在插入

secondly, what would be the best practice to achieve this using EF ? 其次,使用EF实现这一目标的最佳实践是什么? it just looks very inefficient loading each object into memory and comparing if it exists or not 只是将每个对象加载到内存中并比较它是否存在似乎效率很低

since I need to update the listing periodically, I have to check against each object in the database if it exists, then update, else insert 因为我需要定期更新列表,所以我必须检查数据库中的每个对象是否存在,然后更新,否则插入

code: 码:

//read session from db
                if (logger.IsDebugEnabled) logger.Debug("reading session from db");
                dbSession = dbContext.SessionSet.Where(x => x.Id == sessionId).FirstOrDefault();

                //populate orders
                List<Order> orders = (from e in ordersList
                                      select new Order {
                                          Id = e.OrderId,
                                          CallCentreNotes = e.CallCentreNotes,
                                          DoorEntryCount = e.DoorEntryCount,
                                          DoorEntryTime = e.DoorEntryTime,
                                          OrderDate = e.OrderDate,
                                          SpecialInstructions = e.SpecialInstructions,
                                          TotalValue = e.TotalValue,
                                          //populate parent refernece
                                          Session = dbSession
                                      }).ToList();

                //check and save order
                foreach (var o in orders) {
                    dbOrder = dbContext.OrderSet.Where(x => x.Id == o.Id).FirstOrDefault();
                    if (dbOrder != null) {
                        dbContext.Entry(dbOrder).CurrentValues.SetValues(o);
                        dbContext.Entry(dbOrder).State = EntityState.Modified;
                    }
                    else {
                        dbContext.OrderSet.Add(o);
                        dbContext.Entry(o.Session).State = EntityState.Unchanged;
                    }
                }
                dbContext.SaveChanges();

                //check and add ticket seat type
                foreach (var o in ordersList) {
                    foreach (var t in o.Tickets) {
                        var ticketSeatType = new TicketSeatType {
                            Id = t.TicketSeatType.TicketSeatTypeId,
                            Description = t.TicketSeatType.Description
                        };
                        dbTicketSeatType = dbContext.TicketSeatTypeSet.Where(x => x.Id == ticketSeatType.Id).FirstOrDefault();
                        if (dbTicketSeatType != null) {
                            dbContext.Entry(dbTicketSeatType).CurrentValues.SetValues(ticketSeatType);
                            dbContext.Entry(dbTicketSeatType).State = EntityState.Modified;
                        }
                        else {
                            if (!dbContext.ChangeTracker.Entries<TicketSeatType>().Any(x => x.Entity.Id == ticketSeatType.Id)) {
                                dbContext.TicketSeatTypeSet.Add(ticketSeatType);
                            }
                        }
                    }
                }
                dbContext.SaveChanges();

                //check and add ticket price type
                foreach (var o in ordersList) {
                    foreach (var t in o.Tickets) {
                        var ticketPriceType = new TicketPriceType {
                            Id = t.TicketPriceType.TicketPriceTypeId,
                            SeatCount = t.TicketPriceType.SeatCount,
                            Description = t.TicketPriceType.Description
                        };
                        dbTicketPriceType = dbContext.TicketPriceTypeSet.Where(x => x.Id == ticketPriceType.Id).FirstOrDefault();
                        if (dbTicketPriceType != null) {
                            dbContext.Entry(dbTicketPriceType).CurrentValues.SetValues(ticketPriceType);
                            dbContext.Entry(dbTicketPriceType).State = EntityState.Modified;
                        }
                        else {
                            if (!dbContext.ChangeTracker.Entries<TicketPriceType>().Any(x => x.Entity.Id == ticketPriceType.Id)) {
                                dbContext.TicketPriceTypeSet.Add(ticketPriceType);
                            }
                        }
                    }
                }
                dbContext.SaveChanges();

                //check and add tickets
                foreach (var o in ordersList) {
                    dbOrder = dbContext.OrderSet.Where(x => x.Id == o.OrderId).FirstOrDefault();
                    foreach (var t in o.Tickets) {
                        var ticket = new Ticket {
                            Id = t.TicketId,
                            Quantity = t.Quantity,
                            TicketPrice = t.TicketPrice,
                            TicketPriceType = new TicketPriceType {
                                Id = t.TicketPriceType.TicketPriceTypeId,
                                Description = t.TicketPriceType.Description,
                                SeatCount = t.TicketPriceType.SeatCount,
                            },
                            TicketSeatType = new TicketSeatType {
                                Id = t.TicketSeatType.TicketSeatTypeId,
                                Description = t.TicketSeatType.Description
                            },
                            Order = dbOrder
                        };
                        //check from db 
                        dbTicket = dbContext.TicketSet.Where(x => x.Id == t.TicketId).FirstOrDefault();
                        dbTicketSeatType = dbContext.TicketSeatTypeSet.Where(x => x.Id == t.TicketSeatType.TicketSeatTypeId).FirstOrDefault();
                        dbTicketPriceType = dbContext.TicketPriceTypeSet.Where(x => x.Id == t.TicketPriceType.TicketPriceTypeId).FirstOrDefault();                            

                        if (dbTicket != null) {
                            dbContext.Entry(dbTicket).CurrentValues.SetValues(t);
                            dbContext.Entry(dbTicket).State = EntityState.Modified;
                            dbContext.Entry(dbTicket.Order).State = EntityState.Unchanged;
                            dbContext.Entry(dbTicketSeatType).State = EntityState.Unchanged;
                            dbContext.Entry(dbTicketPriceType).State = EntityState.Unchanged;

                        }
                        else {
                            dbContext.TicketSet.Add(ticket);
                            dbContext.Entry(ticket.Order).State = EntityState.Unchanged;
                            dbContext.Entry(ticket.TicketSeatType).State = EntityState.Unchanged;
                            dbContext.Entry(ticket.TicketPriceType).State = EntityState.Unchanged;
                        }
                    }
                }
                dbContext.SaveChanges();

UPDATE: 更新:

Found the answer, it has to do with how EF tracks references to objects, in the above code, I was creating new entity types from the list for TicketPriceType and TicketSeatType: 找到了答案,这与EF如何跟踪对对象的引用有关,在上面的代码中,我从TicketPriceType和TicketSeatType的列表中创建了新的实体类型:

 foreach (var o in ordersList) {
                    dbOrder = dbContext.OrderSet.Where(x => x.Id == o.OrderId).FirstOrDefault();
                    foreach (var t in o.Tickets) {
                        var ticket = new Ticket {
                            Id = t.TicketId,
                            Quantity = t.Quantity,
                            TicketPrice = t.TicketPrice,
                            TicketPriceType = new TicketPriceType {
                                Id = t.TicketPriceType.TicketPriceTypeId,
                                Description = t.TicketPriceType.Description,
                                SeatCount = t.TicketPriceType.SeatCount,
                            },
                            TicketSeatType = new TicketSeatType {
                                Id = t.TicketSeatType.TicketSeatTypeId,
                                Description = t.TicketSeatType.Description
                            },
                            Order = dbOrder
                        };
....

in this case the EF wouldn't know which objects they were and try to insert them. 在这种情况下,EF不会知道它们是哪个对象,而是尝试插入它们。

the solution is to read the entities from database and allocate those, so it's referencing the same entities and doesn't add new ones 解决方案是从数据库中读取实体并进行分配,因此它引用的是相同实体,并且不添加新实体

foreach (var t in o.Tickets) {

                            //check from db 
                            dbTicket = dbContext.TicketSet.Where(x => x.Id == t.TicketId).FirstOrDefault();
                            dbTicketSeatType = dbContext.TicketSeatTypeSet.Where(x => x.Id == t.TicketSeatType.TicketSeatTypeId).FirstOrDefault();
                            dbTicketPriceType = dbContext.TicketPriceTypeSet.Where(x => x.Id == t.TicketPriceType.TicketPriceTypeId).FirstOrDefault();

                            var ticket = new Ticket {
                                Id = t.TicketId,
                                Quantity = t.Quantity,
                                TicketPrice = t.TicketPrice,
                                TicketPriceType = dbTicketPriceType,
                                TicketSeatType = dbTicketSeatType,
                                Order = dbOrder
                            };
...}

Don't you think that you are trying to write very similar codes for defining the state of each entity? 您是否不认为您正在尝试编写非常相似的代码来定义每个实体的状态? We can handle all of these operations with a single command. 我们可以用一个命令处理所有这些操作。

You can easily achieve this with the newly released EntityGraphOperations for Entity Framework Code First. 您可以使用新发布的用于实体框架代码优先的EntityGraphOperations轻松实现此目的。 I am the author of this product. 我是这个产品的作者。 And I have published it in the github , code-project ( includes a step-by-step demonstration and a sample project is ready for downloading) and nuget . 而且我已经在github代码项目包括分步演示和示例项目准备下载)nuget中发布了它 With the help of InsertOrUpdateGraph method, it will automatically set your entities as Added or Modified . 借助InsertOrUpdateGraph方法,它将自动将您的实体设置为已AddedModified And with the help of DeleteMissingEntities method, you can delete those entities which exists in the database, but not in the current collection. 借助DeleteMissingEntities方法,您可以删除数据库中存在的实体,但不能删除当前集合中的实体。

// This will set the state of the main entity and all of it's navigational 
// properties as `Added` or `Modified`.
context.InsertOrUpdateGraph(ticket); 

By the way, I feel the need to mention that this wouldn't be the most efficient way of course. 顺便说一句,我觉得有必要提到,这当然不是最有效的方法。 The general idea is to get the desired entity from the database and define the state of the entity. 总体思路是从数据库中获取所需的实体并定义实体的状态。 It would be as efficient as possible. 这将是尽可能高效的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM