c# - WP8 和 Linq to SQL 具有一对多关系 : SubmitChanges() removes wrong entity

标签 c# windows-phone-8 linq-to-sql windows-phone submitchanges

我有以下实体/表:

  • Board: 一 block 板可以有很多pin
  • Pin:一个引脚分配给一个板。该实体是抽象的,并且确实有具有不同实现的子实体。属于具有 InheritanceMapping 的父 pin 实体的所有子实体将被保存到 pin 表中并由 Discriminator 列 区分
    • TaskPin:这是 pin 的一个子实现。它可以有很多任务。
  • Task:一个任务分配给一个TaskPin

这里有一些代码可以让我的结构更清晰:

[Table]
public class Board : ModelBase
{
    private int _boardId;

    [Column(IsPrimaryKey = true, IsDbGenerated = true, DbType = "INT NOT NULL Identity"
                         ,CanBeNull = false, AutoSync = AutoSync.OnInsert)]
    public int BoardId
    {
        get { return _boardId; }
        set { SetProperty(ref _boardId, value); }
    }

    private EntitySet<Pin> _pins;

    [Association(Storage = "_pins", OtherKey = "_boardId"
    ,ThisKey = "BoardId", DeleteRule = "CASCADE")]
    public EntitySet<Pin> Pins
    {
        get { return _pins; }
        set { _pins.Assign(value); }
    }

    public Board()
    {
        _pins = new EntitySet<Pin>(new Action<Pin>(this.addPin)
            ,new Action<Pin>(this.removePin));
    }

    private void addPin(Pin pin)
    {
        NotifyPropertyChanging("Pin");
        pin.Board = this;
    }

    private void removePin(Pin pin)
    {
        NotifyPropertyChanging("Pin");
        pin.Board = null;
    }
}

[Table]
[InheritanceMapping(Code = PinType.TaskPin, Type = typeof(TaskPin)
             ,IsDefault = true)]
public abstract class Pin : ModelBase
{
    private int _pinId;

    [Column(IsPrimaryKey = true, IsDbGenerated = true
         ,DbType = "INT NOT NULL Identity", AutoSync = AutoSync.OnInsert)]
    public int PinId
    {
        get { return _pinId; }
        set { SetProperty(ref _pinId, value); }
    }

    [Column]
    internal int _boardId;

    private EntityRef<Board> _board;

    [Association(Storage = "_board", ThisKey = "_boardId"
        ,OtherKey = "BoardId", IsForeignKey = true, DeleteOnNull = true)]
    public Board Board
    {
        get { return _board.Entity; }
        set
        {
            if (SetProperty(ref _board, value) != null)
            {
                _boardId = value.BoardId;
            }
        }
    }

    [Column(IsDiscriminator = true)]
    public PinType Type { get; set; }


    public Pin()
    {

    }
}

public class TaskPin : Pin
{
    private EntitySet<Task> _tasks;

    [Association(Storage = "_tasks", OtherKey = "_pinId"
        ,ThisKey = "PinId", DeleteRule = "CASCADE")]
    public EntitySet<Task> Tasks
    {
        get { return _tasks; }
        set { _tasks.Assign(value); }
    }

    public TaskPin()
    {
        _tasks = new EntitySet<Task>(new Action<Task>(this.addTask)
               ,new Action<Task>(this.removeTask));
    }

    private void addTask(Task task)
    {
        NotifyPropertyChanging("Task");
        task.Pin = this;
    }

    private void removeTask(Task task)
    {
        NotifyPropertyChanging("Task");
        task.Pin = null;
    }
}

[Table]
public class Task : ModelBase
{
    private int _taskId;

    [Column(IsPrimaryKey = true, IsDbGenerated = true
                       ,DbType = "INT NOT NULL Identity"
                       ,CanBeNull = false, AutoSync = AutoSync.OnInsert)]
    public int TaskId
    {
        get { return _taskId; }
        set { SetProperty(ref _taskId, value); }
    }

    [Column]
    internal int _pinId;

    private EntityRef<Pin> _pin;

    [Association(Storage = "_pin", ThisKey = "_pinId"
                         ,OtherKey = "PinId"
                         ,IsForeignKey = true
                         ,DeleteOnNull=true)]
    public Pin Pin
    {
        get { return _pin.Entity; }
        set
        {
            if (SetProperty(ref _pin, value) != null)
            {
                _pinId = value.PinId;
            }
        }
    }

    public Task()
    {

    }
}

我创建了一个 TaskPin 并将其分配给一个板。然后我创建两个任务并将它们分配给 TaskPin。这确实很好用。当我尝试从 TaskPin 执行一个或多个 Tasks 时出现问题:

    private void OnDeleteTasks(object sender, EventArgs e)
    {
        TaskPin taskPin = pin as TaskPin;
        var completedTasks = taskPin.Tasks
                            .Where(x => x.IsDone == true)
                            .ToList();

        foreach (var task in completedTasks)
        {
            taskPin.Tasks.Remove(task);
        }
    }

如果我在 DataContext 对象上调用 SubmitChanges(),它将设置 TaskPin(继承自 Pin)的 Board 属性null

    public void Save(Pin pin)
    {
        // This is empty so no modified members are identified => Correct
        var modifiedMembers = db.Pins.GetModifiedMembers(pin);

        // Contains just one entry for the deleted Task entity => Correct
        var changeSet = db.GetChangeSet();

        // This call will immediately set Board property of Pin to null => Wrong!
        db.SubmitChanges();
    }

我预计 Task 会被删除,因为 DeleteOnNull 设置为 true 但我不知道为什么 Board property 的 Pin也设置为 null,这将导致 NullPointerExceptio 或 Pin 也被删除。

我对这个主题进行了谷歌搜索,但没有找到任何可以解决我的问题的东西。一种替代方法是防止 Board 属性归零并手动为任务调用 DeleteOnSubmit()

最佳答案

在我看来,这种行为是有意为之的。

如果您查看 Task 类中的 Pin 属性,它被设置为在 Null (DeleteOnNull = true) 时删除,然后Task 中的 Pin 将按照 TaskPin 类中的 removeTask() 删除。然后查看Board 类中的removePin() 方法,它将PinBoard 设置为null。然后查看Pin 类中的Board 属性,DeleteOnNull 设置为true 这将从Board 中删除strong>Pin 设置为 null 时的实例。

关于c# - WP8 和 Linq to SQL 具有一对多关系 : SubmitChanges() removes wrong entity,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26225103/

相关文章:

c# - WPF MVVM-以编程方式触发datagrid排序方向的箭头

c# - 带有存储过程和用户​​定义表类型参数的 LINQ to SQL

c# - DefaultIfEmpty 返回 null

c# - 比 String.Replace() 更快

c# - .NET,每分钟(每分钟)发生一次事件。计时器是最好的选择吗?

windows-phone-8 - 在 Windows Phone 8 应用程序中下载并解压缩 zip 文件

javascript - Azure 移动服务 - 服务器端脚本 - 使用 === 与零比较

c# - 无法启动 Windows Phone 模拟器

c# - 方法 x 没有支持的 SQL 转换

c# - 跨多个线程的 Linq-to-SQL DataContext