WP8和Linq to SQL与一对多关系:SubmitChanges()删除错误的实体

chr*_*rkl 6 c# submitchanges linq-to-sql windows-phone windows-phone-8

我有以下entites /表:

  • Board:一块板可以有很多引脚
  • Pin:一个引脚分配给一个板.该实体是抽象的,并且具有具有不同实现的子项.所有属于父pin实体的子节点InheritanceMapping将被保存到pin表中并用a区分Discriminator column
    • 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;
    }
}
Run Code Online (Sandbox Code Playgroud)
[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()
    {

    }
}
Run Code Online (Sandbox Code Playgroud)
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;
    }
}
Run Code Online (Sandbox Code Playgroud)
[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()
    {

    }
}
Run Code Online (Sandbox Code Playgroud)

我创建了一个TaskPin并将其分配给一个板.然后我创建两个任务并将它们分配给TaskPin.这确实很好.当我尝试将一个或多个问题occurrs Tasks来自TaskPin:

    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);
        }
    }
Run Code Online (Sandbox Code Playgroud)

如果我然后调用SubmitChanges()我的DataContext对象,它会将Board propertyTaskPin(继承自Pin)设置为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();
    }
Run Code Online (Sandbox Code Playgroud)

我希望Task将被删除因为DeleteOnNull设置为true但我不知道为什么Board propertyPin也设置为null导致一个NullPointerExceptio或者Pin也被删除.

我对这个主题进行了谷歌搜索,但我找不到任何解决我问题的方法.另一种方法是阻止Board属性的归零并DeleteOnSubmit()手动调用Task.

小智 0

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

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