如果没有数据上下文,如何保存Linq对象?

Sha*_*ica 1 c# linq linq-to-sql

我有一个Linq对象,我想对它进行更改并保存它,如下所示:

public void DoSomething(MyClass obj) {
  obj.MyProperty = "Changed!";
  MyDataContext dc = new MyDataContext();
  dc.GetTable<MyClass>().Attach(dc, true); // throws exception
  dc.SubmitChanges();
}
Run Code Online (Sandbox Code Playgroud)

例外是:

System.InvalidOperationException: An entity can only be attached as modified without original state if it declares a version member or does not have an update check policy.
Run Code Online (Sandbox Code Playgroud)

看起来我有几个选择:

  1. 在我需要以这种方式使用的每个Linq类和表(100+)上放置一个版本成员.
  2. 找到最初创建对象的数据上下文,并使用它来提交更改.
  3. 在每个类中实现OnLoaded并保存此对象的副本,我可以将其传递给Attach()作为基线对象.
  4. 要进行并发检查; 在附加之前加载DB版本并将其用作基线对象(不是!!!)

选项(2)似乎是最优雅的方法,特别是如果我能找到一种在创建对象时存储对数据上下文的引用的方法.但是 - 怎么样?

还有其他想法吗?

编辑

我试图遵循Jason Punyon的建议并在桌面上创建一个并发字段作为测试用例.我在dbml文件中的字段上设置了所有正确的属性(Time Stamp = true等),现在我有一个并发字段......以及一个不同的错误:

System.NotSupportedException: An attempt has been made to Attach or Add an entity that is not new, perhaps having been loaded from another DataContext.  This is not supported.
Run Code Online (Sandbox Code Playgroud)

那么,如果不是一个现有的实体,我应该附加什么呢?如果我想要一个新记录,我会做一个InsertOnSubmit()!那么你应该如何使用Attach()?

编辑 - 完全披露

好的,我可以看到是时候全面披露为什么所有标准模式都不适合我.

我一直试图通过隐藏"消费者"开发人员的DataContext来变得聪明并使我的界面更清晰.我通过创建基类来完成

public class LinqedTable<T> where T : LinqedTable<T> {
  ...
}
Run Code Online (Sandbox Code Playgroud)

...我的每一个表都生成了其生成版本的"另一半",如下所示:

public partial class MyClass : LinqedTable<MyClass> {
}
Run Code Online (Sandbox Code Playgroud)

现在LinqedTable有一堆实用方法,尤其是:

public static T Get(long ID) {
  // code to load the record with the given ID
  // so you can write things like:
  //   MyClass obj = MyClass.Get(myID);
  // instead of:
  //   MyClass obj = myDataContext.GetTable<MyClass>().Where(o => o.ID == myID).SingleOrDefault();
}
public static Table<T> GetTable() {
  // so you can write queries like:
  //   var q = MyClass.GetTable();
  // instead of:
  //   var q = myDataContext.GetTable<MyClass>();
}
Run Code Online (Sandbox Code Playgroud)

当然,正如您可以想象的那样,这意味着LinqedTable必须以某种方式能够访问DataContext.直到最近,我通过在静态上下文中缓存 DataContext来实现这一目标.是的,"直到最近",因为"近期"是当我发现你不是真的要挂到一个DataContext比工作单元更长,否则小鬼的种种奔涌出来的木制品.学过的知识.

所以现在我知道我不能长时间坚持这个数据上下文...这就是为什么我开始尝试按需创建一个DataContext,只缓存在当前LinqedTable实例上.这导致了新创建的DataContext与我的对象无关的问题,因为它"知道"它对创建它的DataContext不忠.

有没有办法在创建或加载时将DataContext信息推送到LinqedTable?

这真的是一个装腔作势者.我绝对不希望在我已经投入了所有这些功能方便妥协LinqedTable的基类,我需要能够放手的DataContext在必要时,虽然它仍然需要挂到它.

还有其他想法吗?

Cyl*_*Cat 5

使用LINQ to SQL进行更新是有趣的.

如果数据上下文消失了(在大多数情况下应该是这样),那么您将需要获取新的数据上下文,并运行查询以检索要更新的对象.在LINQ to SQL中,你必须检索一个对象来删除它,这是一个绝对的规则,而且你应该检索一个对象来更新它.有一些解决方法,但它们很丑陋,通常还有很多方法可以帮助你解决问题.所以,再次获取记录并完成它.

获得重新获取的对象后,使用包含更改的现有对象的内容对其进行更新.然后对新数据上下文执行SubmitChanges().而已!LINQ to SQL将通过将记录中的每个值与原始(在重新获取的)记录中进行比较来生成相当严厉的乐观并发版本.如果在获取数据时更改了任何值,LINQ to SQL将引发并发异常.(因此,您无需为版本控制或时间戳更改所有表.)

如果您对生成的更新语句有任何疑问,则必须打破SQL事件探查器并观察更新是否发送到数据库.这是一个好主意,直​​到您对生成的SQL有信心.

关于事务的最后一个注释 - 如果没有环境事务,数据上下文将为每个SubmitChanges()调用生成一个事务.如果您要更新多个项目并希望将它们作为一个事务运行,请确保为所有项目使用相同的数据上下文,并等待调用SubmitChanges(),直到您更新了所有对象内容.

如果这种交易方法不可行,那么查找TransactionScope对象.这将是你的朋友.