linq-to-sql“已尝试附加或添加不是新的实体”?

时间:2010-05-12 15:42:11

标签: asp.net linq-to-sql

我遇到了几个错误:

  1. 无法添加具有已在使用的密钥的实体

  2. 已尝试附加或添加非新实体,可能已从其他datacontext加载

  3. 在案例1中,这源于尝试为实体设置密钥而不是实体。在案例2中,我没有附加实体,但我这样做:

    MyParent.Child = EntityFromOtherDataContext;

    我一直在使用使用datacontext包装所有内容的模式。在我的例子中,我在Web表单场景中使用它,显然将datacontext对象移动到类宽成员变量解决了这个问题。

    我的问题因此有两个方面:

    1. 如何摆脱这些错误,不必以奇怪的方式构建我的程序,或者在保持局部包装模式的同时传递datacontext?我假设我可以再次打击数据库,但这似乎非常低效。

    2. 大多数人是否建议将datacontext移动到类范围范围是否适用于网页?

3 个答案:

答案 0 :(得分:3)

  1. Linq to SQL不适用于断开连接的方案。您可以将实体复制到具有与实体类似结构的DTO,然后传递它。然后在将属性附加到新数据上下文时将属性复制回实体。您还可以在附加到新数据上下文之前反序列化/重新序列化实体以具有干净状态。第一种解决方法显然违反了DRY原则,而第二种解决方法只是丑陋。如果您不想使用任何这些解决方案,剩下的唯一选择是通过点击数据库来检索您要通过其PK修改的实体。这意味着每次更新前都需要额外的查询。或者使用另一个ORM,如果这是你的选择。具有自我跟踪实体的实体框架4(包含在.NET 4中)是我目前在Web表单项目中使用的,到目前为止一切都很好。

  2. DataContext不是线程安全的,只能在方法级别与using一起使用,就像您已经做的那样。您可以考虑向静态数据上下文添加锁,但这意味着不能同时访问数据库。另外,你会在上下文中积累内存中的实体,这些实体将成为潜在的问题。

答案 1 :(得分:2)

对于那些追随我的人,我会提供自己的看法:

“尝试添加或附加非新实体”的错误源于此操作:

Child.Parent = ParentEntityFromOtherDataContext

我们可以使用当前的datacontext重新加载对象,以避免这种问题:

Child.Parent = dc.Entries.Select(t => t).Where(t => t.ID == parentEntry.ID).SingleOrDefault();

或者可以做到这一点

MySubroutine(DataContext previousDataContext)
{
 work...
}

或者在Web表单场景中,我倾向于使DataContext成为类成员,如下所示:

DataContext _dc = new DataContext();

是的,datacontext假设代表一个工作单元。但是,它是一个轻量级的对象,在Web表单场景中页面相当短暂,模式可以从(使用dc = new dc())更改为仅使用成员变量_dc。我倾向于最后一个解决方案,因为它会减少数据库的数量,并且需要更少的代码。

但是,有没有解决这个问题的方法呢?我正在考虑缓存一些陈旧数据。

答案 2 :(得分:1)

我通常做的就是这个

public abstract class BaseRepository : IDisposable
{
    public BaseRepository():
        this(new MyDataContext( ConfigurationManager.ConnectionStrings["myConnection"].ConnectionString))
    {
    }

    public BaseRepository(MyDataContext dataContext)
    {
        this.DataContext = dataContext;
    }

    public MyDataContext DataContext {get; set;}

    public void Dispose()
    {
        this.DataContext.Dispose();
    }
}

然后想象我有以下存储库

public class EmployeeRepository : BaseRepository
{
     public EmployeeRepository():base()
     {
     }

     public EmployeeRepository(MyDataContext dataContext):base(dataContext)
     {
     }

     public Employee SelectById(Guid id)
     {
          return this.DataContext.Employees.FirstOrDefault(e=>e.Id==id);
     }

     public void Update(Employee employee)
     {
         Employee original = this.Select(employee.Id);
         if(original!=null)
         {
             original.Name = employee.Name;
             //others
             this.DataContext.SubmitChanges();
         }
     }
}

在我的控制器中(我使用的是asp.net mvc)

public ActionResult Update(Employee employee)
{
    using(EmployeeRepository employeeRepository = new EmployeeRepository())
    {
        if(ModelState.IsValid)
        {
            employeeRepository.Update(employee);
        }
    }

    //other treatment
}

因此,datacontext已正确处理,我可以在我的员工存储库的同一个实例中使用它

现在想象一下,对于一个特定的动作,我希望加载员工的公司(为了以后在我的视图中显示),我可以这样做:

public ActionResult Select(Guid id)
{
    using(EmployeeRepository employeeRepository = new EmployeeRepository())
    {
        //Specifying special load options for this specific action:
        DataLoadOptions options = new DataLaodOptions(); 
        options.LoadWith<Employee>(e=>e.Company);
        employeeRepository.DataContext.LoadOptions = options;

        return View(employeeRepository.SelectById(id));
    }
}