LINQ to SQL - 在不创建新DataContext的情况下保存实体?

时间:2010-04-20 02:52:18

标签: c# .net linq linq-to-sql

我收到此错误

  

无法使用已在使用的密钥添加实体

当我尝试保存项目时

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(Item item)
{
  Global.DataContext.Items.Attach(item);
  Global.DataContext.SubmitChanges();

  return View(item);
}

那是因为我无法将项目附加到全局DataContext。

是否可以在不创建新DataContext的情况下保存项目,而无需手动分配项目的每个字段?

(我对LINQ来说很新)

编辑:由于下面的评论,我意识到静态DataContext会导致问题,现在就像这样

public static AbcDataContext DataContext
{
  get
  {
    if (!HttpContext.Current.Items.Contains("DataContext"))
      HttpContext.Current.Items["DataContext"] = new AbcDataContext(ConnectionString);
    return (AbcDataContext)HttpContext.Current.Items["DataContext"];
  }
}

(雷克斯可能不同意这一点,但我现在无法改变整个代码 - 可能会更晚)

5 个答案:

答案 0 :(得分:4)

没有全局/静态的DataContext,这就是为痛苦做好准备。 DataContext应该表示单个逻辑事务(“get in,do x / y / z and get out”)。它们制造起来便宜且易于处理;绝对没有理由尽量减少它们,更不用说保持全局/静态了。

答案 1 :(得分:3)

假设您的Item类的主键是ItemId。

假设您尝试更新的实例的ItemID为5。

DataContext已经看到了ItemID 5的原始状态,所以它不会让你使用Attach()。 http://msdn.microsoft.com/en-us/library/bb300517.aspx

  

在这个版本的Attach中,实体   被假定为其原始值   州。在调用此方法后,您   然后可以更新其字段   从中发送附加数据的示例   客户。

在LinqToSql中有三种常规方法可以执行更新。

如果此Edit方法的参数最初是从DataContext加载的,那么您需要做的就是:

public ActionResult Edit(Item item) 
{
  Global.DataContext.SubmitChanges(); 
  return View(item); 
} 

DataContext跟踪对其加载的对象的更改。作为令人讨厌的副作用,DataContext加载的任何修改对象也将被更新。这是不使用单个应用级DataContext的一个重要原因。

如果此Edit方法的参数在代码中被新增,由不同的DataContext加载或传递给您的代码(换句话说,实例没有附加的DataContext),那么您可以执行以下任一操作:

public ActionResult Edit(Item item) 
{
  using(MyDataContext dc = new MyDataContext())
  {
//this new DataContext has never heard of my item, so I may Attach.
    dc.Items.Attach(item);
//this loads the database record in behind your changes
// to allow optimistic concurrency to work.
//if you turn off the optimistic concurrency in your item class
// then you won't have to do this
    dc.Refresh(item, KeepCurrentValues);
    dc.SubmitChanges(); 
  }
  return View(item); 
} 

public ActionResult Edit(Item item) 
{
  original = Global.DataContext.Items.Single(x => x.ItemID = item.ItemID)
  //play the changes against the original object.
  original.Property1 = item.Property1;
  original.Property2 = item.Property2;
  Global.DataContext.SubmitChanges(); 
  return View(item); 
} 

回答你的问题后,请允许我回应其他人对使用静态DataContext所表达的担忧。这是一种糟糕的做法,违背了Microsoft对DataContext类的预期用法。 http://msdn.microsoft.com/en-us/library/system.data.linq.datacontext.aspx

  

通常,DataContext实例是   旨在持续一个“单位   工作“但你的应用程序定义   那个词。 DataContext是   轻便且不贵   创建。典型的LINQ to SQL   应用程序创建DataContext   方法范围或实例   昙花一现的成员   代表一组相关的逻辑   数据库操作。

答案 2 :(得分:1)

静态全局DataContext?如果我对您的问题的理解是正确的,这将导致每个人连接到您的应用程序共享相同的数据上下文,这将导致许多安全/同步问题。避免它。

答案 3 :(得分:1)

DataContext讨论。注意我没有评论你的代码。

DataContexts实现IDisposable,因此您应该在不再需要时处理数据上下文。您的网站在开发中运行良好,但在生产中您将获得固定。你可能在你的代码变得过于根深蒂固之前做到这一点并且改变它将是一个很大的麻烦。充其量你只会养成坏习惯。

更好的替代方法是拥有自己的控制器基类来管理你的生命周期。

public class MyBaseController : System.Web.Mvc.Controller
{
    private AbcDataContext abcDataContext;

    protected AbcDataContext DataContext
    {
        get 
        {   // lazy-create of DataContext
            if (abcDataContext == null)
                abcDataContext = new AbcDataContext(ConnectionString);

            return abcDataContext;
        }
    }

    protected override void Dispose(bool disposing)
    {
        base.Dispose(disposing);
        if (disposing)
        {
            if( abcDataContext != null )
                abcDataContext.Dispose();
        }
    }
}

允许你做

public class MyController : MyBaseController
{
    [AcceptVerbs(HttpVerbs.Post)]
    public ActionResult Edit(Item item)
    {
        DataContext.Items.Attach(item);
        DataContext.SubmitChanges();

        return View(item);
    }
}

虽然这有效,但我个人觉得它会变得烦人和棘手。


最佳:如果您要按照预期的方式关注MVC,则应该完全填充模型,而不是依赖于延迟加载实体。实现这一目标的最佳方法是尽快删除DataContext。

通常,我们通过以下模式在代码级别强制执行此操作:

using( var dc = new AbcDataContext(ConnectionString))
{
    var itemUpdater = new ItemUpdater(dc);
    item = itemUpdater.Update(item);
}
return View(item);

如果您的视图尝试通过延迟加载获取任何其他数据,那么您将获得ObjectDisposedException

答案 4 :(得分:0)

根据this discussion about the same problem,如果您删除设计器中的 Item 类并将表格拖到设计器中,它似乎是一个类型映射错误再次。