问题
I've created two lists that corresponds to two of my classes:
//Photo
var photos = new List<Photo>{
new Photo {
Title = "Title1",
Description = "Description1",
CreatedDate = DateTime.Today
}
};
//Comment
var comments = new List<Comment>
{
new Comment{
PhotoId = 1,
UserName = "User1",
Subject = "Comment1",
}
};
When I tried to add these two lists to my context object and then SaveChanges, I got the error I mentioned:
photos.ForEach(s => context.Photos.Add(s));
comments.ForEach(s => context.Comments.Add(s));
context.SaveChanges();
But when I saved the changes individually, I didn't get the exception.
photos.ForEach(s => context.Photos.Add(s));
context.SaveChanges();
comments.ForEach(s => context.Comments.Add(s));
context.SaveChanges();
Why is this? Should I save changes after each modification of my DB?
回答1:
To avoid issues like this, set up the relationships between your entities and use those relationships rather than trying to set FK IDs manually. ORMs like EF are designed to work this detail out for you, so leverage that ability.
If a photo has comments, where a comment has a PhotoID column then set up the entities something like this:
public class Photo
{
public int PhotoID {get; set;}
public string Title {get; set;}
public string Description {get; set;}
private List<Comment> _comments = new List<Comment>();
public virtual List<Comment> Comments
{
get {return _comments;}
set {_comments = value;}
}
}
public class Comment
{
public string UserName {get; set;}
public string Subject {get; set;}
}
Note that I haven't added PhotoID to comment. If you want to search for comments for instance and navigate back to a Photo then you can optionally add:
public virtual Photo Photo {get; set;}
However if you don't need that navigation, then I'd avoid adding it to keep things simple.
To set up these navigation properties so EF can manage them, I use EntityTypeConfiguration mappings. There are also methods using data annotations, but I find ETConfigs more reliable.
public class PhotoConfiguration : EntityTypeConfiguration<Photo>
{
public PhotoConfiguration()
{
ToTable("Photos");
HasKey(x => x.PhotoID)
.Property(x => x.PhotoID)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity); // This informs EF that the database will be assigning the PK.
HasMany(x => x.Comments)
.WithRequired()
.Map(x => x.MapKey("PhotoID")); // This tells EF to join comments to a Photo /w the PhotoID on the Comment table. HasMany says a Photo has many comments, and WithRequired() points to a non-nullable FK on Comment, but no navigation property coming back to Photo.
}
}
This example is for EF6, for EFCore you can replace the .Map() call /w
.HasForeignKey("PhotoID");
which will accomplish the same thing with a shadow property. If you do want a reference to Photo on each Comment, then you can add the virtual property in Comment, then replace the .WithRequired() with .WithRequired(x => x.Photo) and this will set up the relationship.
public class CommentConfiguration : EntityTypeConfiguration<Comment>
{
public CommentConfiguration()
{
ToTable("Comments");
HasKey(x => x.CommentID)
.Property(x => x.CommentD)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
}
}
Now when you want to save a photo and a comment:
var photo = new Photo
{
Title = "Title1",
Description = "Description1",
CreatedDate = DateTime.Today,
Comments = new[]
{
New Comment { UserName = "User1", Subject = "Comment1" }
}.ToList()
};
context.Photos.Add(photo);
context.SaveChanges();
Note that we don't save Comments separately. EF manages all of the PK/FK behind the scenes. After the SaveChanges we can retrieve the PKs of our entities from the entity properties.
来源:https://stackoverflow.com/questions/50538181/an-error-occurred-while-updating-the-entries-in-ef-when-i-try-to-savechanges