我有简单的模型:
public class Post
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public long Id { set; get; }
//non-important properties stripped, to focus on problem
public virtual Resource Resource { set; get; }
public virtual ICollection<Tag> Tags { set; get; }
}
public class Resource
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id { set; get; }
[Url]
public string Url { set; get; }
}
和DbContext(我在这个项目中使用ASP.NET身份,如果这是相关的):
public class DbContext : IdentityDbContext<User>
{
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
var postEntity = modelBuilder.Entity<Post>();
postEntity.Reference(p => p.Resource).InverseCollection(); //no navigation property on Resource side
postEntity.Collection(p => p.Tags).InverseReference(tag => tag.Post);
postEntity.ToTable("Posts");
var resourceEntity = modelBuilder.Entity<Resource>();
resourceEntity.ToTable("Resources");
var tagEntity = modelBuilder.Entity<Tag>();
tagEntity.Reference(t => t.Post).InverseCollection(p => p.Tags).Required(false);
tagEntity.ToTable("Tags");
}
}
迁移(SQL Server)后,数据库表看起来像预期的 - Post表有ResourceId的外键。
当我附上post.Resource(已经创建的资源)时,创建Post的工作正常。
我想替换post.Resource 时出现问题。 通过替换,我的意思是选择一个已经存在的资源并将其分配给发布。
var resource2 = Database.Resources.First(r=>r.Url == "xyz");
我试过了:
post.Resource = resource2; Database.Entry(post).State = EntityState.Modified;
Database.Entry(post).Property(p => p.Resource).CurrentValue = resource2;
post.Resource = null;
它们的不同组合,但它们都不起作用。在调用await SaveChangesAsync();
并查找数据库后 - 没有任何更改。如何正确执行替换(更换外键)?
// 2015年9月14日更新 问题是由额外选择引起的,为更新一对多关系而执行。完整代码:
var Database new DbContext();
var resource2 = Database.Resources.First(r=>r.Url == "xyz");
var oldAssignedTags = Database.Posts.Include(p=>p.Tags).FirstOrDefault(p => p.Id == post.Id).Tags;
var tags = newTags as List<Tag> ?? newTags.ToList();
//TagComparer is irrelevant here
var toRemove = oldAssignedTags.Except(tags, TagComparer);
var toAdd = tags.Except(oldAssignedTags, TagComparer);
foreach (var tag in toRemove)
{
Database.Entry(tag).State = EntityState.Deleted; //Database.Tags.Remove(tag);
}
foreach (var tag in toAdd)
{
tag.Post = post;
post.Tags.Add(tag);
}
post.Resource = resource2;
await Database.SaveChangesAsync();
答案 0 :(得分:1)
我认为这可能与 Eager Loading 有关,但无论有没有AspNet.Identity
我都无法重现您的问题。运行以下代码会导致资源始终更新。使用EntityFramework 7.0.0-beta7
。
<强>代码强>
static void Main(string[] args)
{
Init();
WithEagerLoading();
CleanUp();
Init();
WithoutEagerLoading();
CleanUp();
}
private static void WithoutEagerLoading()
{
var db = new MyContext();
var post = db.Posts.First(); // no eager loading of child
post.Resource = db.Resources.First(p => p.Url == "http://trend.atqu.in");
db.SaveChanges();
Console.WriteLine($"2nd Resource.Id: {new MyContext().Posts.Include(p => p.Resource).First().Resource.Id}");
}
private static void WithEagerLoading()
{
var db = new MyContext();
var post = db.Posts
.Include(p => p.Resource) // eager loading
.First();
post.Resource = db.Resources.First(p => p.Url == "http://trend.atqu.in");
db.SaveChanges();
Console.WriteLine($"2nd Resource.Id: {new MyContext().Posts.Include(p => p.Resource).First().Resource.Id}");
}
private static void CleanUp()
{
var db = new MyContext();
db.Posts.RemoveRange(db.Posts);
db.Resources.RemoveRange(db.Resources);
db.SaveChanges();
}
private static void Init()
{
var db = new MyContext();
var resource = new Resource { Url = "http://atqu.in" };
var resource2 = new Resource { Url = "http://trend.atqu.in" };
var post = new Post { Resource = resource };
db.Add(post);
db.Add(resource);
db.Add(resource2);
db.SaveChanges();
db = new MyContext();
post = db.Posts.Include(p => p.Resource).First();
resource = db.Resources.First(p => p.Url == "http://trend.atqu.in");
Console.WriteLine($"1st Resource.Id: {post.Resource.Id}");
}
<强>结果强>
1st Resource.Id: 0f4d222b-4184-4a4e-01d1-08d2bc9cea9b
2nd Resource.Id: 00ccae9c-f0da-43e6-01d2-08d2bc9cea9b
1st Resource.Id: 749f08f0-2426-4043-01d3-08d2bc9cea9b
2nd Resource.Id: 2e83b512-e8bd-4583-01d4-08d2bc9cea9b
编辑16/9
编辑问题代码中的问题是因为您在检索Database
后实例化了post
。 post
的{{1}}实例未附加Database
,因此当您将Resource
附加到SaveChangesAsync
并致电post
时,它无效,因为当时Database
已经注意到你正在拯救的post
。这就是为什么你再次选择post
post(在实例化之后)导致修复它的原因 - 因为然后附加了post
的实例。如果您不想再次选择DbContext
,则应使用相同的post
实例执行上述最初检索{{1}}的工作。