使用 Linq-to-SQL 更新实体 - 附加非新实体

发布于 2024-11-05 19:25:01 字数 5117 浏览 2 评论 0原文

我正在尝试编写一个使用 Linq-to-SQL 与数据库(MS SQL Server 2008)交互的程序。添加和删​​除似乎没问题,但我无法理解更新。

该实体上有一个版本列,它是数据库上的时间戳列,用于 Linq-to-SQL 中内置的乐观锁定。我已将实体上所有字段的“更新检查”属性设置为“从不”。

我有以下 SaveTaskCommand,用于插入和更新实体,具体取决于特定任务是否已添加到数据库中。

public class SaveTaskCommand : CustomCommand
{
    private Task _task;
    private TaskDetailsViewModel _taskDetails;

    public SaveTaskCommand(Task task, TaskDetailsViewModel taskDetails)
    {
        _task = task;
        _taskDetails = taskDetails;
    }

    public override void Execute(object parameter)
    {
        TaskRepository taskRepository = new TaskRepository();
        if (!taskRepository.ContainsTask(_task))
        {
            taskRepository.AddTask(_task);
            _taskDetails.Mediator.NotifyColleagues(ViewModelMessages.TaskAdded, 
                _task);
        }
        else
        {
            taskRepository.UpdateTask(_task);
            _taskDetails.Mediator.NotifyColleagues(
                ViewModelMessages.TaskAmended, null);
        }
    }

    public override bool CanExecute(object parameter)
    {
        return _task.IsValid();
    }
}

CustomCommand 类只是一个包装 ICommand 并处理 CanExecuteChanged 事件的类,因此我不必在每个命令中重复代码。

正如您所看到的,在命令的 Execute() 方法中创建了一个 TaskRepository,该方法首先检查任务是否已在数据库中,然后选择是插入还是更新。 TaskRepository 的代码如下。

public class TaskRepository : IRepository
{
    private DataContextDataContext _dataContext;

    public TaskRepository()
    {
        _dataContext = new DataContextDataContext();
    }

    public List<Task> GetAllTasks()
    {
        return _dataContext.Tasks.ToList();
    }

    public Task GetForKeyTable(int keyTable)
    {
        return _dataContext.Tasks.Where(t => t.KeyTable == keyTable).
            FirstOrDefault();
    }

    public void AddTask(Task task)
    {
        task.Project = _dataContext.Projects.SingleOrDefault(
            p => p.KeyTable == task.KeyProject);
        _dataContext.Tasks.InsertOnSubmit(task);
        _dataContext.SubmitChanges();

    }

    public void UpdateTask(Task task)
    {
        //exception occurs here
        _dataContext.Tasks.Attach(task, GetForKeyTable(task.KeyTable)); 
        _dataContext.SubmitChanges();
    }

    public void DeleteTask(Task task)
    {
        _dataContext.Tasks.Attach(task, GetForKeyTable(task.KeyTable));
        _dataContext.Tasks.DeleteOnSubmit(task);
        _dataContext.SubmitChanges();
    }

    public bool ContainsTask(Task task)
    {
        return GetForKeyTable(task.KeyTable) != null;
    }
}

在指示的行中,我得到以下异常:

已尝试附加或 添加一个可能不是新的实体 从另一个加载 数据上下文。不支持此功能。

我不明白为什么当我传递实体的原始版本时会出现此异常。如果我将注释行更改为

_dataContext.Tasks.Attach(task, true);

任何帮助,我都会得到同样的异常,我们将不胜感激。

更新

我已经使我的存储库实现了 IDisposable,并在调用构造函数的所有地方都进行了更改,使用 using (TaskRepository taskRepository = new TaskRepository)。在 TaskRepositoryDispose() 方法中,我在数据上下文上调用了 Dispose()

我还更改了 Update() 方法以在任务对象上调用 Detach()。我的代码现在如下所示:

public class TaskRepository : IRepository, IDisposable
{
    private DataContextDataContext _dataContext;

    public TaskRepository()
    {
        _dataContext = new DataContextDataContext();
        DataLoadOptions dlo = new DataLoadOptions();
        dlo.LoadWith<Task>(t => t.Project);
        dlo.LoadWith<Task>(t => t.Priority);
        _dataContext.LoadOptions = dlo;
    }

    public List<Task> GetAllTasks()
    {
        return _dataContext.Tasks.ToList();
    }

    public Task GetForKeyTable(int keyTable)
    {
        return _dataContext.Tasks.Where(t => t.KeyTable == keyTable).FirstOrDefault();
    }

    public void AddTask(Task task)
    {
        task.Project = _dataContext.Projects.SingleOrDefault(p => p.KeyTable == task.KeyProject);
        _dataContext.Tasks.InsertOnSubmit(task);
        _dataContext.SubmitChanges();

    }

    public void UpdateTask(Task task)
    {
        task.Detach();

        _dataContext.Tasks.Attach(task, true);  //exception occurs here
        _dataContext.Refresh(RefreshMode.KeepCurrentValues, task);
        _dataContext.SubmitChanges();
    }

    public void DeleteTask(Task task)
    {
        _dataContext.Tasks.Attach(task, GetForKeyTable(task.KeyTable));
        _dataContext.Tasks.DeleteOnSubmit(task);
        _dataContext.SubmitChanges();
    }

    public bool ContainsTask(Task task)
    {
        return GetForKeyTable(task.KeyTable) != null;
    }

    #region IDisposable Members

    public void Dispose()
    {
        _dataContext.Dispose();
    }

    #endregion
}

任务上的 Detach() 方法是这样的:

public void Detach()
{
    this._Project = default(EntityRef<Project>);
    this._Priority = default(EntityRef<Priority>);
}

作为参考,我的实体如下所示:

数据库布局

我现在在指示的行处收到以下异常。

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

I am trying to write a program that uses Linq-to-SQL to interface with the database (MS SQL Server 2008). Adding and deleting seem to be ok but I can't get my head around updates.

The entity has a version column on it which is a timestamp column on the database and used for the optimistic locking that comes built in to Linq-to-SQL. I have set the Update Check property for all of the fields on the entity to be Never.

I have the following SaveTaskCommand which is used to insert and update entities, depending on whether or not the particular task has been added to the database already.

public class SaveTaskCommand : CustomCommand
{
    private Task _task;
    private TaskDetailsViewModel _taskDetails;

    public SaveTaskCommand(Task task, TaskDetailsViewModel taskDetails)
    {
        _task = task;
        _taskDetails = taskDetails;
    }

    public override void Execute(object parameter)
    {
        TaskRepository taskRepository = new TaskRepository();
        if (!taskRepository.ContainsTask(_task))
        {
            taskRepository.AddTask(_task);
            _taskDetails.Mediator.NotifyColleagues(ViewModelMessages.TaskAdded, 
                _task);
        }
        else
        {
            taskRepository.UpdateTask(_task);
            _taskDetails.Mediator.NotifyColleagues(
                ViewModelMessages.TaskAmended, null);
        }
    }

    public override bool CanExecute(object parameter)
    {
        return _task.IsValid();
    }
}

The CustomCommand class is just a class that wraps up an ICommand and deals with the CanExecuteChanged event so that I didn't have to repeat the code in each of the commands.

As you can see a TaskRepository is created in the Execute() method of the command which firstly checks if the task is already in the database and then chooses whether to insert or update. The code for the TaskRepository is below.

public class TaskRepository : IRepository
{
    private DataContextDataContext _dataContext;

    public TaskRepository()
    {
        _dataContext = new DataContextDataContext();
    }

    public List<Task> GetAllTasks()
    {
        return _dataContext.Tasks.ToList();
    }

    public Task GetForKeyTable(int keyTable)
    {
        return _dataContext.Tasks.Where(t => t.KeyTable == keyTable).
            FirstOrDefault();
    }

    public void AddTask(Task task)
    {
        task.Project = _dataContext.Projects.SingleOrDefault(
            p => p.KeyTable == task.KeyProject);
        _dataContext.Tasks.InsertOnSubmit(task);
        _dataContext.SubmitChanges();

    }

    public void UpdateTask(Task task)
    {
        //exception occurs here
        _dataContext.Tasks.Attach(task, GetForKeyTable(task.KeyTable)); 
        _dataContext.SubmitChanges();
    }

    public void DeleteTask(Task task)
    {
        _dataContext.Tasks.Attach(task, GetForKeyTable(task.KeyTable));
        _dataContext.Tasks.DeleteOnSubmit(task);
        _dataContext.SubmitChanges();
    }

    public bool ContainsTask(Task task)
    {
        return GetForKeyTable(task.KeyTable) != null;
    }
}

At the line indicated, I get the following exception:

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.

I don't understand why I get this exception when I am passing in the original version of the entity. I get the same exception if I change the commented line to

_dataContext.Tasks.Attach(task, true);

Any help would be greatly appreciated.

Update

I have made my repository implement IDisposable and changed everywhere that calls the constructor use the using (TaskRepository taskRepository = new TaskRepository). In the Dispose() method of TaskRepository, I have called Dispose() on my data context.

I have also changed the Update() method to call Detach() on my Task object. My code now looks like this:

public class TaskRepository : IRepository, IDisposable
{
    private DataContextDataContext _dataContext;

    public TaskRepository()
    {
        _dataContext = new DataContextDataContext();
        DataLoadOptions dlo = new DataLoadOptions();
        dlo.LoadWith<Task>(t => t.Project);
        dlo.LoadWith<Task>(t => t.Priority);
        _dataContext.LoadOptions = dlo;
    }

    public List<Task> GetAllTasks()
    {
        return _dataContext.Tasks.ToList();
    }

    public Task GetForKeyTable(int keyTable)
    {
        return _dataContext.Tasks.Where(t => t.KeyTable == keyTable).FirstOrDefault();
    }

    public void AddTask(Task task)
    {
        task.Project = _dataContext.Projects.SingleOrDefault(p => p.KeyTable == task.KeyProject);
        _dataContext.Tasks.InsertOnSubmit(task);
        _dataContext.SubmitChanges();

    }

    public void UpdateTask(Task task)
    {
        task.Detach();

        _dataContext.Tasks.Attach(task, true);  //exception occurs here
        _dataContext.Refresh(RefreshMode.KeepCurrentValues, task);
        _dataContext.SubmitChanges();
    }

    public void DeleteTask(Task task)
    {
        _dataContext.Tasks.Attach(task, GetForKeyTable(task.KeyTable));
        _dataContext.Tasks.DeleteOnSubmit(task);
        _dataContext.SubmitChanges();
    }

    public bool ContainsTask(Task task)
    {
        return GetForKeyTable(task.KeyTable) != null;
    }

    #region IDisposable Members

    public void Dispose()
    {
        _dataContext.Dispose();
    }

    #endregion
}

The Detach() method on Task is this:

public void Detach()
{
    this._Project = default(EntityRef<Project>);
    this._Priority = default(EntityRef<Priority>);
}

For reference, my entities look like this:

Database layout

I now get the following exception at the line indicated.

Cannot add an entity with a key that is already in use.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

み青杉依旧 2024-11-12 19:25:01

尝试在将 Task 对象附加到新的数据上下文之前将其与之前的数据上下文分离,或者在整个应用程序中使用数据上下文的单个实例...

您可以在此处找到解除附加方法的示例:http://omaralzabir.com/linq_to_sql__how_to_attach_object_to_a_ Different_data_context/

Try to deattach your Task object from previous data context before attaching it to a new one or use single instance of data context in whole appilaction...

You can find example of deattach method in here: http://omaralzabir.com/linq_to_sql__how_to_attach_object_to_a_different_data_context/

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文