2012-06-25 4 views
5

Ho cercato una risposta a questo per 2 giorni. Non sono nemmeno sicuro di cosa sia esattamente sbagliato, ma penso di aver individuato il possibile colpevole.Errori di convalida del database. Consenti valore nullable in una tabella ma non nella tabella correlata (normalizzata)

Il mio programma è un tracker/gestore di database che terrà traccia delle apparecchiature presso la mia azienda. L'utente può richiedere, creare e modificare l'attrezzatura. Al momento, l'interrogazione del database funziona, ma quando provo ad aggiungere o aggiornare il database il programma genera errori di convalida (ne parlerò più in basso).

Ho una classe di apparecchiature che ha molti attributi nullable (poiché l'utente potrebbe non conoscere molte di queste proprietà quando crea una nuova voce, abbiamo lasciato quasi tutti gli attributi nullable) ed è connesso a (utilizzando chiavi esterne e caricamento lazy)/"virtuale") a poche altre tabelle create per aiutare a normalizzare il database.

Ecco il codice abbreviati per aiutare:

Attrezzature Modello:

public class Equipment 
{ 
    public int EquipmentId { get; set; } //required 

    public string Company { get; set; } //required 

    public bool Verified { get; set; } //required 

    (...other non-required attributes...) 


    //The following two attributes are where my problems are I believe 
    [ForeignKey("PAU")] 
    public int PAUId { get; set; } //required and Foreign Key  

    //This is not required (but is FK to the related table) if the user doesn't know 
    [ForeignKey("Division")] 
    public Nullable<int> DivisionId { get; set; } 


    //These are the lazy loading connections to the other tables in the database 

    public virtual Division Division { get; set; } 

    public virtual PAU PAU { get; set; } 
} 

Divisione Modello: Questa tabella contiene solo le 5 divisioni differenti presso la nostra azienda e dei relativi ID.

public class Division 
{ 
    public Division() 
    { 
     this.Equipments = new HashSet<Equipment>(); 
    } 

    //These are non-nullable/required fields 
    public int DivisionId { get; set; } 
    public string DivisionName { get; set; } 

    public virtual ICollection<Equipment> Equipments { get; set; } 
} 

PAU Modello: Questo modello contiene i PAU # s e la relativa descrizione e ID.

public class PAU 
{ 
    public PAU() 
    { 
     this.Equipments = new HashSet<Equipment>(); 
    } 

    //These are non-nullable/required fields 
    public int PAUId { get; set; } 
    public string PAUNumber { get; set; } 
    public string PAUDescription { get; set; } 

    public virtual ICollection<Equipment> Equipments { get; set; } 
} 

controller Equipaggiamento:

public class EquipmentController : Controller 
{ 
    TLCP_DEVEntities4 db = new TLCP_DEVEntities4(); 

    (...) 

    public ActionResult Edit(int id)   
    { 
     ViewData["divisions"] = new SelectList(db.Equipments. 
           OrderBy(x => x.Division.DivisionName). 
           Select(x => x.Division).ToList(). 
           Distinct(), "DivisionId", "DivisionName"); 

     ViewData["PAUNumbers"] = new SelectList(db.Equipments. 
           OrderBy(x => x.PAU.PAUNumber). 
           Select(x => x.PAU).ToList(). 
           Distinct(), "PAUId", "PAUNumber"); 

     return View(db.Equipments.Find(id)); 
    } 

    [HttpPost] 
    public ActionResult Edit(Equipment equipment) 
    { 
     if (ModelState.IsValid) 
     { 
      db.Entry(equipment).State = EntityState.Modified;      
      db.SaveChanges();  //fails here          
      return RedirectToAction("Maintenance"); 
     } 

     ViewData["divisions"] = new SelectList(db.Equipments. 
           OrderBy(x => x.Division.DivisionName). 
           Select(x => x.Division).ToList(). 
           Distinct(), "DivisionId", "DivisionName"); 

     ViewData["PAUNumbers"] = new SelectList(db.Equipments. 
           OrderBy(x => x.PAU.PAUNumber). 
           Select(x => x.PAU).ToList(). 
           Distinct(), "PAUId", "PAUNumber"); 

     return View(equipment); 
    } 

    public ActionResult AddEquipment() 
    { 
     ViewData["divisions"] = new SelectList(db.Equipments. 
           OrderBy(x => x.Division.DivisionName). 
           Select(x => x.Division).ToList(). 
           Distinct(), "DivisionId", "DivisionName"); 

     ViewData["PAUNumbers"] = new SelectList(db.Equipments. 
           OrderBy(x => x.PAU.PAUNumber). 
           Select(x => x.PAU).ToList(). 
           Distinct(), "PAUId", "PAUNumber"); 

     return View(); 
    } 

    public ActionResult AddEquipment(Equipment equipment) 
    { 
     try 
     { 
      db.Equipments.Add(equipment); 
      db.SaveChanges(); 

      return RedirectToAction("Maintenance"); 
     } 
     catch (Exception ex) 
     { 

      ViewData["divisions"] = new SelectList(db.Equipments. 
            OrderBy(x => x.Division.DivisionName). 
            Select(x => x.Division).ToList(). 
            Distinct(), "DivisionId", "DivisionName"); 

      ViewData["PAUNumbers"] = new SelectList(db.Equipments. 
           OrderBy(x => x.PAU.PAUNumber). 
           Select(x => x.PAU).ToList(). 
           Distinct(), "PAUId", "PAUNumber"); 

      return View(); 
     } 
    } 

    (...) 
} 

Nelle viste ho elenchi a discesa che per questi attributi che sono popolate con le possibili opzioni già presenti nel database (memorizzato in Viewdata sopra). Ho provato un paio di modi diversi di memorizzare i dati in mio modello, ma in questo modo sembra funzionare al meglio:

<%: Html.DropDownListFor(model => model.DivisionId, 
     ViewData["divisions"] as SelectList, "")%> 

Ok, quindi dopo un sacco di debug, il problema sembra essere perché le proprietà caricate pigri hanno attributi null, che non sono ammessi nelle loro tabelle. I miei esempi riguarderanno più la creazione di nuovi oggetti di equipaggiamento, anche se credo che il problema sia lo stesso per modificarli.

Esempio di divisione: Quindi DivisionId nella classe Equipment può essere nullo (l'utente non sa o l'apparecchiatura non appartiene a una divisione specifica) e la tabella/classe Equipment non si lamenta di ciò. Ma quando provo a SaveChanges(), il programma si lamenta perché l'oggetto Virtual Division è nullo (ha il DivisionId predefinito di 0 e un Nome divisione nullo). Poiché Equipment.DivisionId è null, non voglio aggiornare la tabella Division per questo oggetto Equipment. C'è un modo per aggirare questo? Non dovrei essere carico pigro?

PAU Esempio: Poiché PAUId è un attributo obbligatorio per tutti gli oggetti del materiale, ma come nella Divisione Esempio PAU.PAUNumber e PAU.PAUDescription sono nulli (e PAU.PAUId è il default 0) causando un errore. Questo è proprio come il primo, ma con l'attributo richiesto (quindi non sono sicuro che debba essere gestito diversamente).

Quindi non sono davvero sicuro su come risolvere questo problema.

  • è lazy loading la questione dal momento voglio solo collegare il mio oggetto Attrezzature a quegli altri tavoli se il/chiave esterna ID non è nullo?

  • C'è qualche metodo che posso chiamare che aggiorni i valori dell'oggetto di equipaggiamento dalle altre tabelle in base agli Id? È un metodo che devo creare da solo?

  • Se mi liberassi del caricamento lento, potresti spiegare come dovrei scrivere le mie query in modo da poter accedere ai dati normalizzati (come DivisionName)?

Mi scuso per il post lunghissimo. Non volevo perdere nulla che potesse essere importante.

Modifica

Ho una soluzione ruvida al PAU esempio (ma non credo che funziona per la Divisione esempio a causa di consentire valori null all'interno di apparecchiature, ma non le singole tabelle).

public ActionResult AddEquipment(Equipment equipment) 
    { 
     try 
     { 
      equipment.PAU.PAUId = equipment.PAUId; 
      equipment.PAU.PAUNumber = db.PAUs.Find(equipment.PAUId).PAUNumber; 
      equipment.PAU.PAUDescription = db.PAUs.Find(equipment.PAUId).PAUDescription; 


     (...) 

    } 

Tuttavia non è molto elegante. Ho provato a guardare un tutorial sulla riflessione, ma non aveva molto senso. Se quella fosse una soluzione più elegante, qualcuno potrebbe provare a spiegarlo?

Sto ancora cercando una soluzione per l'esempio di divisione (poiché Equipment.DivisionId è nullable ma Division.DivisionId non è annullabile questo non funziona), quindi qualsiasi aiuto su questa o su una soluzione più elegante per sopra sarebbe molto apprezzato!

+0

E qui è l'errore che ottengo (non è molto utile e quando ho cercato su questo non ho trovato una risposta che lo ha risolto): {System.Data.Entity.Validation.DbEntityValidationException: Convalida fallita per uno o più entità. Vedi la proprietà 'EntityValidationErrors' per maggiori dettagli. a System.Data.Entity.Internal.InternalContext.SaveChanges() a System.Data.Entity.Internal.LazyInternalContext.SaveChanges() a System.Data.Entity.DbContext.SaveChanges() a DLMP_Asset_Tracking.Controllers.EquipmentController .AddEquipment (Equipment equipment) –

+0

Cosa c'era nella proprietà 'EntityValidationErrors'? –

+0

Mostra che PAUId = 0 (il valore predefinito, giusto?) E PAUDescription e PAUNumber sono nulli (lo stesso con Divisione e le altre 4 proprietà che ho lasciato fuori dalla domanda a causa della ridondanza). L'array ValidationErrors mostra che sono necessarie le proprietà nulle (PAUNumber e PAUDescription), che sono dal modo in cui ho impostato le tabelle del database. Questo risponde alla tua domanda, George? –

risposta

0

C'era una parte un'altra parte del mio codice che si comportava in modo strano causando l'errore. Apprezzo tutto il tuo aiuto! Grazie!

Problemi correlati