我遇到了几个错误:
无法添加具有已在使用的密钥的实体
已尝试附加或添加非新实体,可能已从其他datacontext加载
在案例1中,这源于尝试为实体设置密钥而不是实体。在案例2中,我没有附加实体,但我这样做:
MyParent.Child = EntityFromOtherDataContext;
我一直在使用使用datacontext包装所有内容的模式。在我的例子中,我在Web表单场景中使用它,显然将datacontext对象移动到类宽成员变量解决了这个问题。
我的问题因此有两个方面:
如何摆脱这些错误,不必以奇怪的方式构建我的程序,或者在保持局部包装模式的同时传递datacontext?我假设我可以再次打击数据库,但这似乎非常低效。
大多数人是否建议将datacontext移动到类范围范围是否适用于网页?
答案 0 :(得分:3)
Linq to SQL不适用于断开连接的方案。您可以将实体复制到具有与实体类似结构的DTO,然后传递它。然后在将属性附加到新数据上下文时将属性复制回实体。您还可以在附加到新数据上下文之前反序列化/重新序列化实体以具有干净状态。第一种解决方法显然违反了DRY原则,而第二种解决方法只是丑陋。如果您不想使用任何这些解决方案,剩下的唯一选择是通过点击数据库来检索您要通过其PK修改的实体。这意味着每次更新前都需要额外的查询。或者使用另一个ORM,如果这是你的选择。具有自我跟踪实体的实体框架4(包含在.NET 4中)是我目前在Web表单项目中使用的,到目前为止一切都很好。
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));
}
}