2014-10-06 12 views
6

ho le seguenti entités/tabelle:WP8 e LINQ to SQL con uno-a-molti relazione: SubmitChanges() rimuove entità sbagliato

  • Board: Una scheda può avere molti perni
  • Pin: One pin è assegnato a una scheda. Questa entità è astratta e ha figli con diverse implementazioni. Tutte bambino appartenenti all'entità perno genitore con InheritanceMapping e verranno salvate nella tabella perno e distinto da un Discriminator column
    • TaskPin: Questo è uno implementazioni figlio di perno. Può avere molti compiti.
  • Task: Un compito viene assegnato a uno TaskPin

Ecco il codice per rendere la mia struttura più chiara:

[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() 
    { 

    } 
} 

creo un TaskPin e assegnarlo a una tavola. Quindi creo due Task e li assegno alla TaskPin. Questo funziona bene. Il problema occurrs quando provo a uno o più Tasks dalla 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); 
     } 
    } 

Se chiamo allora SubmitChanges() sul mio DataContext oggetto, si imposterà il Board property del TaskPin (ereditato da Pin) a 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(); 
    } 

mi aspetto che il Task saranno cancellati a causa DeleteOnNull è impostato su vero, ma non so il motivo per cui il Board property del perno è anche impostato su null che si tradurrà in un NullPointerExceptio o che il Pin viene eliminato anche .

Ho fatto una ricerca su google su questo argomento ma non trovo nulla che risolva il mio problema. Un'alternativa potrebbe essere quella di impedire il blocco delle proprietà della scheda e chiamare manualmente DeleteOnSubmit() per Attività.

+0

Come si utilizza il datacontext: una volta creato o ricreato ogni volta? Conoscete sql profiler? Sembra che tu abbia due transazioni e io possa essere il visualizzatore con profiler. –

+0

Il tuo 'changeSet' contiene un riferimento non nullo a' Board'? Sospetto che il tuo oggetto grafico non venga caricato a causa di altri fattori. – Mrchief

+0

@Mrchief cosa intendi con il grafo degli oggetti? Il set di modifiche contiene una voce per la TaskPin da eliminare. –

risposta

0

A me sembra che il comportamento è destinato.

Se si guarda alla Pin proprietà in Task classe, è impostato per essere eliminati anche Null (DeleteOnNull = true), che poi il Pin in Task sarà cancellato come da removeTask() in classe TaskPin.Poi guardate il metodo diremovePin() in Consiglio classe che imposta Consiglio del Pin a null. Poi guardate il proprietà Consiglio in Pin classe, DeleteOnNull è impostata su true, che cancellerà il Consiglioda Pin esempio quando è impostato su null.