Oli*_*ver 1 c# entity-framework ef-code-first
我在使用codefirst Entity Framework时遇到了很多麻烦.
我有一个包含两个表的数据库 - Users和Countries.Users有一个具有外键的字段CountryId.首次将用户添加到数据库时,FK会更新.但是,如果我尝试更新我附加到新DbContext的用户的FK,则不会发生任何更改.
我可以在最初创建用户的上下文中更新FK.此外,我可以在从数据库中检索用户的上下文中更新FK.但是,在我只是Attach用户的上下文中,即使普通nvarchar或int字段更新,FK字段也不会更新.
这是我的DbContext:
class DataContext : DbContext
{
public DbSet<User> Users { get; set; }
public DbSet<Country> Countries { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
这是我的模型:
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; }
}
Run Code Online (Sandbox Code Playgroud)
这种方法证明了这个问题.我向我的数据库中添加了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();
}
}
Run Code Online (Sandbox Code Playgroud)
此应用程序在数据库中生成以下结果:
UserId Name Country_CountryId
------------------------------------------------------------------------
1 FirstChanged 2
2 SecondChanged 1
3 ThirdChanged 2
Run Code Online (Sandbox Code Playgroud)
如您所见,在所有情况下都已Name成功更新.在第一种情况和第三种情况下,CountryId更新.但是,在第二种情况下,CountryId不会更新.不幸的是,这种情况对我来说最相关,因为我希望能够持久化模型以供DataContext之外的用户进行编辑.
我不明白为什么会发生这种情况 - 如果该Name领域没有更新,我可以理解,但正如我无法理解CountryId为什么.在这两种情况下,Country对象都被跟踪并标记为Unchanged.我不明白为什么只有第二种情况不起作用.
请帮我弄清楚这里有什么.我能想到的唯一解决方案是在我需要修改时使用第三种情况,这样当我保存时,我将数据从持久化对象复制到从数据库中检索到的新对象,但是显然这是愚蠢的解决方案.
下面,我提供了一个演示此问题的控制台应用程序.要运行它,您需要使用NuGet(或类似的)和这样的连接字符串检索EntityFramework:
<add name="DataContext" connectionString="DataSource=.\DataContext.sdf" providerName="System.Data.SqlServerCe.4.0" />
Run Code Online (Sandbox Code Playgroud)
码:
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;
}
}
}
Run Code Online (Sandbox Code Playgroud)
感谢您查看此问题 - 任何帮助将不胜感激.我正在尝试修复它的第二天,我慢慢疯了......
这是基于外键关联与独立关联之间差异的棘手问题.如果您使用外键关联(您将CountryId在您的User实体中拥有属性),您的第二种情况也会起作用,但您正在使用独立关联,其中关联本身具有自己的状态,必须将其设置Added为使其起作用.DbContext 不提供API来更改关联的状态,因此您必须使用ObjectContext.
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);
ObjectContext oc = ((IObjectContextAdapter)dc).ObjectContext;
oc.ObjectStateManager.ChangeObjectState(u, EntityState.Modified);
oc.ObjectStateManager.ChangeRelationshipState(u, countries[1], x => x.Country, EntityState.Added);
dc.SaveChanges();
}
Run Code Online (Sandbox Code Playgroud)
当您使用独立关联时,您会发现许多并发症.