EF7中導航屬性的更新未保存

asp.net-core entity-framework-core

我有簡單的模型:

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 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; }
}

遷移(SQL Server)後,數據庫表看起來像預期的 - Post表具有ResourceId的外鍵。

當我附上post.Resource(已經創建的資源)時,創建Post的工作正常。

當我想替換post.Resource時,我的問題就出現了通過替換,我的意思是選擇一個已經存在的資源並將其分配給post。

var resource2 = Database.Resources.First(r=>r.Url == "xyz");

我努力了:

  1. post.Resource = resource2; Database.Entry(post).State = EntityState.Modified;

  2. Database.Entry(post).Property(p => p.Resource).CurrentValue = resource2;

  3. post.Resource = null;

它們的組合也不同 ,但它們都不起作用。在調用await SaveChangesAsync();並查找數據庫 - 沒有變化。如何正確執行替換(更改外鍵)?

//更新14.09.2015 問題是由附加選擇引起的,執行更新一對多關係。完整代碼:

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; }
}

一般承認的答案

我認為這可能與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}");
}

結果

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}");
}

編輯16/9

編輯問題代碼中的問題是因為您在檢索post實例化Databasepost沒有附加到該Database實例,因此當您將Resource附加到它並調用SaveChangesAsync它什麼都不做,因為那時的post與您要保存的Database有關。這就是為什麼你再次選擇post (在實例化之後)的解決方法導致它被修復 - 因為然後附加了post的實例。如果您不想再次選擇post ,則應使用相同的DbContext實例來完成DbContext用於最初檢索post




許可下: CC-BY-SA with attribution
不隸屬於 Stack Overflow
這個KB合法嗎? 是的,了解原因
許可下: CC-BY-SA with attribution
不隸屬於 Stack Overflow
這個KB合法嗎? 是的,了解原因