我在使用codefirst實體框架時遇到了很多麻煩。在實體框架中添加+修改對象時出現奇怪的困難
我有兩個表的數據庫 - Users
和Countries
。 Users
有一個字段,其外鍵爲CountryId
。當用戶首次添加到數據庫時,FK更新正常。但是,如果我嘗試更新附加到新DbContext的用戶的FK,則不會發生更改。
我可以在我最初創建在用戶的上下文更新FK。另外,我可以在我從數據庫中檢索用戶上下文更新FK。然而,在我剛剛Attach
用戶的上下文中,FK字段不更新,即使是正常的nvarchar
或int
領域做更新。
這裏是我的DbContext:
class DataContext : DbContext
{
public DbSet<User> Users { get; set; }
public DbSet<Country> Countries { get; set; }
}
這裏是我的模型:
public class User
{
public int UserId { get; private set; }
public string Name { get; set; }
public virtual Country Country { get; set; }
}
public class Country
{
public int CountryId { get; private set; }
public string Name { get; set; }
}
這種方法演示了此問題。我將3個用戶添加到我的數據庫中,然後嘗試以3種不同的方式修改它們 - 在用戶添加的原始上下文中,在Attach
的新上下文中,以及在用戶從數據庫中恢復的新上下文中修改:
static void Main(string[] args)
{
Country[] countries;
using (var dc = new DataContext())
{
countries = dc.Countries.ToArray();
}
var u = new User();
// FIRST ROW
using (var dc = new DataContext())
{
u.Name = "First";
u.Country = countries[0];
dc.Users.Attach(u);
dc.Entry(u).State = System.Data.EntityState.Added;
dc.SaveChanges();
u.Name = "FirstChanged";
u.Country = countries[1];
// defaults to 'added' once u is tracked.
dc.Entry(countries[1]).State = EntityState.Unchanged;
dc.SaveChanges();
}
// SECOND ROW
u = new User();
using (var dc = new DataContext())
{
u.Name = "Second";
u.Country = countries[0];
dc.Users.Attach(u);
dc.Entry(u).State = System.Data.EntityState.Added;
dc.SaveChanges();
}
using (var dc = new DataContext())
{
u.Name = "SecondChanged";
u.Country = countries[1];
dc.Users.Attach(u);
dc.Entry(u).State = System.Data.EntityState.Modified;
dc.SaveChanges();
}
// THIRD ROW
u = new User();
using (var dc = new DataContext())
{
u.Name = "Third";
u.Country = countries[0];
dc.Users.Attach(u);
dc.Entry(u).State = System.Data.EntityState.Added;
dc.SaveChanges();
}
using (var dc = new DataContext())
{
u = dc.Users.Find(u.UserId);
u.Name = "ThirdChanged";
u.Country = countries[1];
dc.Entry(countries[1]).State = EntityState.Modified;
dc.SaveChanges();
}
}
這個應用產生的數據庫,結果如下:
UserId Name Country_CountryId
------------------------------------------------------------------------
1 FirstChanged 2
2 SecondChanged 1
3 ThirdChanged 2
正如你所看到的,在所有情況下Name
更新成功。在第一種情況和第三種情況下,CountryId
已更新。但是,在第二種情況下,CountryId不會更新。不幸的是,這種情況與我最爲相關,因爲我希望能夠在DataContext之外持續使用模型進行編輯。
我不明白爲什麼會這樣 - 如果Name
場沒有更新我能理解,但由於其只是CountryId
我不明白爲什麼。在這兩種情況下,Country
對象都被跟蹤並標記爲Unchanged
。我不明白爲什麼只有第二種情況不起作用。
請幫我弄清楚這裏有什麼。我能想到的唯一解決方案就是在需要修改時使用第三種情況,這樣當我保存時,我可以將數據從持久對象複製到從數據庫中檢索到的新對象中,但是顯然這是愚蠢的解決方案。
下面,我包括演示此問題的一個控制檯應用程序。要運行它,你將需要的EntityFramework使用的NuGet(或類似)和一個連接字符串這樣的檢索:
<add name="DataContext" connectionString="DataSource=.\DataContext.sdf" providerName="System.Data.SqlServerCe.4.0" />
代碼:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.Entity;
using System.Data;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
Country[] countries;
using (var dc = new DataContext())
{
countries = dc.Countries.ToArray();
}
var u = new User();
// FIRST ROW
using (var dc = new DataContext())
{
u.Name = "First";
u.Country = countries[0];
dc.Users.Attach(u);
dc.Entry(u).State = System.Data.EntityState.Added;
dc.SaveChanges();
u.Name = "FirstChanged";
u.Country = countries[1];
// defaults to 'added' once u is tracked.
dc.Entry(countries[1]).State = EntityState.Unchanged;
dc.SaveChanges();
}
// SECOND ROW
u = new User();
using (var dc = new DataContext())
{
u.Name = "Second";
u.Country = countries[0];
dc.Users.Attach(u);
dc.Entry(u).State = System.Data.EntityState.Added;
dc.SaveChanges();
}
using (var dc = new DataContext())
{
u.Name = "SecondChanged";
u.Country = countries[1];
dc.Users.Attach(u);
dc.Entry(u).State = System.Data.EntityState.Modified;
dc.SaveChanges();
}
// THIRD ROW
u = new User();
using (var dc = new DataContext())
{
u.Name = "Third";
u.Country = countries[0];
dc.Users.Attach(u);
dc.Entry(u).State = System.Data.EntityState.Added;
dc.SaveChanges();
}
using (var dc = new DataContext())
{
u = dc.Users.Find(u.UserId);
u.Name = "ThirdChanged";
u.Country = countries[1];
dc.Entry(countries[1]).State = EntityState.Modified;
dc.SaveChanges();
}
}
}
class DataContext : DbContext
{
public DbSet<User> Users { get; set; }
public DbSet<Country> Countries { get; set; }
public DataContext()
{
Database.SetInitializer<DataContext>(new ModelInitializer());
Database.Initialize(false);
}
}
class ModelInitializer : DropCreateDatabaseAlways<DataContext>
{
protected override void Seed(DataContext context)
{
var jsds = new List<Country>
{
new Country("United Kingdom"),
new Country("United States Of America")
};
jsds.ForEach(jsd => context.Countries.Add(jsd));
}
}
public class User
{
public int UserId { get; private set; }
public string Name { get; set; }
public virtual Country Country { get; set; }
}
public class Country
{
public int CountryId { get; private set; }
public string Name { get; set; }
public Country() { }
public Country(string name)
{
Name = name;
}
}
}
感謝您看這個問題 - 任何幫助,將不勝感激。我在試圖修復它的第二天,我慢慢地快要瘋了......
我只想說,你對許多EF相關問題的答案對我來說是無價的。謝謝。 – 2012-04-17 12:31:56
感謝您的回答。我嘗試了你發佈的代碼,但遇到了一些麻煩(我得到一個錯誤,說刷新ObjectStateManager,但這樣做意味着u.Name沒有更新,儘管FK沒有更新),所以我將我的用戶類更改爲使用外鍵關聯。然後,我改變了我的代碼來指定這樣的國家:'u.CountryId = countries [0] .CountryId;'。它是否正確?它似乎工作,但在某些情況下,「Country」屬性爲空,而不是反映用戶的「CountryId」屬性。 – Oliver 2012-04-17 13:15:20
是的,這是使用FK關聯的正確方法。該名稱未更新,因爲代碼缺少將用戶的狀態更改爲已修改的行。我會更新代碼。 – 2012-04-17 13:51:58