所以我有一个真的很难找出什么时候我应该附加到一个对象上,什么时候我不应该附加到一个对象上。首先,这是我(非常简化的)对象模型的一个小图。
在我的DAL中,每次进行与数据相关的操作时,我都会创建一个新的DataContext。比如说,我想保存一个新用户。在我的业务层中,我创建了一个新用户。
var user = new User();
user.FirstName = "Bob";
user.LastName = "Smith";
user.Username = "bob.smith";
user.Password = StringUtilities.EncodePassword("MyPassword123");
user.Organization = someOrganization; // Assume that someOrganization was loaded and it's data context has been garbage collected.
现在我想保存此用户。
var userRepository = new RepositoryFactory.GetRepository<UserRepository>();
userRepository.Save(user);
NEATO!这是我的保存逻辑:
public void Save(User user)
{
if (!DataContext.Users.Contains(user))
{
user.Id = Guid.NewGuid();
user.CreatedDate = DateTime.Now;
user.Disabled = false;
//DataContext.Organizations.Attach(user.Organization);
DataContext.Users.InsertOnSubmit(user);
}
else
{
DataContext.Users.Attach(user);
}
DataContext.SubmitChanges();
// Finished here as well.
user.Detach();
}
所以,我们在这里。您会注意到我将DataContext附加到组织的位置注释掉。如果我附加到组织,我会得到以下例外:
嗯,这不起作用。让我试试,不要附加(即注释掉关于附加到组织的那一行)。NotSupportedException: 已尝试附加或添加 不是新的实体,也许是新的 已从另一个DataContext加载。 这不受支持。
DuplicateKeyException: 无法使用已经存在的密钥添加实体 正在使用中。
WHAAAAT?我只能假设这是试图插入一个显然是错误的新组织。
那么,这些交易是什么?我该怎么办?什么是正确的方法?似乎L2S让它变得比它应该更难......
编辑:我刚注意到,如果我尝试查看挂起的更改集(dataContext.GetChangeSet()),我会得到前面描述的相同的NotSupportedException !!怎么了,L2S?!
答案 0 :(得分:7)
它可能无法完全像这样工作,但这是我概念化它的方式:当你从DataContext召唤一个对象时,Linq所做的事情之一是跟踪这个对象随时间的变化,所以它知道什么如果您提交更改,请保存。如果丢失了这个原始数据上下文,从它召唤的Linq对象没有从它从数据库中召唤的时间内发生的变化的历史记录。
例如:
DbDataContext db = new DbDataContext();
User u = db.Users.Single( u => u.Id == HARD_CODED_GUID );
u.FirstName = "Foo";
db.SubmitChanges();
在这种情况下,由于User对象是从数据上下文中召唤出来的,因此它跟踪了对“u”的所有更改,并知道如何将这些更改提交给数据库。
在您的示例中,您有一个User对象,该对象已在某处保留(或从其他地方传递,并且没有它从其传唤的原始DataContext)。在这种情况下,您必须将其附加到新数据上下文。
User u; // User object passed in from somewhere else
DbDataContext db = new DbDataContext();
u.FirstName = "Foo";
DbDataContext.Users.Attach( u );
db.SubmitChanges();
由于用户和组织之间的关系只是数据模型中的GUID(OrganizationId),因此您只需附加用户对象。
我不确定你的脚手架代码,但也许是这样的:
private const Guid DEFAULT_ORG = new Guid("3cbb9255-1083-4fc4-8449-27975cb478a5");
public void Save(User user)
{
if (!DataContext.Users.Contains(user))
{
user.Id = Guid.NewGuid();
user.CreatedDate = DateTime.Now;
user.Disabled = false;
user.OrganizationId = DEFAULT_ORG; // make the foreign key connection just
// via a GUID, not by assigning an
// Organization object
DataContext.Users.InsertOnSubmit(user);
}
else
{
DataContext.Users.Attach(user);
}
DataContext.SubmitChanges();
}
答案 1 :(得分:1)
因此,当您从数据库中获取一个对象时,使用“attach”,将其分离(例如通过在其他地方的Web服务上进行编组),并希望将其放回数据库中。而不是调用.Attach(),而是调用.InsertOnSubmit()。你几乎在概念上,你只是用错误的方法做你想做的事。
答案 2 :(得分:0)
我使用了400多列的大桌子。我无法映射和测试所有这些!
从数据库中获取原始对象,并将其与修改后的对象一起附加。只要确保重新填充的对象是完全填充的,否则它将用空格覆盖DB!
或者您可以将原始GET复制到内存中并处理MOdel的正确副本(不仅仅是引用),然后传递原始GET和更改后的内容,而不是像我在示例中那样。这只是它如何运作的一个例子。
public void Save(User user)
{
if (!DataContext.Users.Contains(user))
{
user.Id = Guid.NewGuid();
user.CreatedDate = DateTime.Now;
user.Disabled = false;
user.OrganizationId = DEFAULT_ORG; // make the foreign key connection just
// via a GUID, not by assigning an
// Organization object
DataContext.Users.InsertOnSubmit(user);
}
else
{
var UserDB = DataContext.Users.FirstOrDefault(db => db.id == user.id); //Costs an extra call but its worth it if oyu have 400 columns!
DataContext.Users.Attach(user, userDB); //Just maps all the changes on the flu
}
DataContext.SubmitChanges();
}