2015-12-20 9 views
5

Ho il seguente scenario:EF6 vincolo univoco estera chiave

public class Book { 
     [Key] 
     public string Isbn { get; set; } 
     public string Title { get; set; } 
     public int Stock { get; set; } 
     public Author Author { get; set; } 
} 

public class Author { 
    public int AuthorId { get; set; } 
    [Index(IsUnique = true)] 
    [StringLength(50)] 
    public string Name { get; set; } 

    public ICollection<Book> Books { get; set; } 
} 

Sto cercando di inserire i libri e autori associati, se necessario. Ho il seguente codice ingenuo, che si rompe (più o meno atteso):

var author = _ctx.Authors.FirstOrDefault(x => x.Name == command.Author); 

if (author == null) 
    author = new Author { Name = command.Author }; 

var book = new Book 
{ 
    Isbn = command.Id, 
    Title = command.Title, 
    Stock = command.Count, 
    Author = author 
}; 

_ctx.Books.Add(book); 

await _ctx.SaveChangesAsync(); 

Quello che sto vedendo è che a volte, il FirstOrDefault sta tornando nulla, ma l'inserto è riuscita in seguito a violazione della indice univoco il nome dell'autore. C'è qualche trucco di EF che permetterà che questo accada in modo semplicistico? Suppongo che potrei usare un proc memorizzato, ma vorrei farlo lato client se possibile.

+1

L'unica ragione per cui posso vedere che questo si interromperebbe è se si aggiungono più libri contemporaneamente e si aggiunge "Autore" da un altro processo tra quando lo si osserva e quando l'elemento viene aggiunto alla raccolta. Il modo più semplice per gestire questo tipo di problema di concorrenza, a mio avviso, sarebbe quello di rilevare l'eccezione, allegare il libro all'autore che si trova ora nel Db e quindi inviarlo di nuovo. – Claies

+0

1. hai provato? Autore.Books.Add (libro); ctx.SaveChangesAsync(); 2. Aggiungi al libro: public int AuthorId {get; impostato; } public virtual Autore Autore {get; impostato; } – W92

+0

L'osservazione di Claies è corretta ... la gara è esattamente il problema, e sto cercando di trovare una soluzione alternativa. @ W92, sì ... ci ho provato. si presenta la stessa condizione di gara. Il problema è sull'inserto dell'autore. – ashic

risposta

3

Dopo aver provato varie cose, sono andato con il seguente:

var author = _ctx.Authors.SqlQuery(
    "with data as (select @author as [name]) " + 
    "merge Authors a " + 
    "using data s on s.[name] = a.[name] " + 
    "when not matched by target " + 
    "then insert([name]) values(s.[name]); select * from Authors where [name][email protected]", new SqlParameter("author", command.Author)).Single(); 


var book = new Book 
{ 
    Isbn = command.Id, 
    Title = command.Title, 
    Stock = command.Count, 
    Author = author 
}; 

_ctx.Books.Add(book); 

await _ctx.SaveChangesAsync(); 

Anche se non abbastanza, questo impedisce la condizione di competizione tra il controllo autore e l'inserimento utilizzando le funzionalità native db. ORM e astrazioni che perdono, eh :)

Immagino di poter inserire il foglietto anche lì, o di fare tutto il resto. Se qualcuno si avvicina con un approccio nativo ORM che si adatti a questo scenario, io sono tutto orecchie :)