2015-12-20 2 views
5

У меня есть следующий сценарий:EF6 уникальное ограничение на внешний ключ

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; } 
} 

Я ищу, чтобы вставить книги и связанные с ними авторы, если это необходимо. У меня есть следующий наивный код, который ломает (в значительной степени ожидаемый):

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(); 

Что я вижу в том, что иногда, то FirstOrDefault возвращается нуль, но вставка не удается из-за нарушения уникального индекса имя автора. Есть ли какие-то обманки EF, которые позволят это сделать упрощенным способом? Думаю, я мог бы использовать хранимую процедуру, но, если возможно, хотел бы сделать это на стороне клиента.

+1

Единственная причина, по которой я могу понять, что это сломается, - это если вы добавляете несколько книг одновременно и добавляется «Автор» другим процессом между тем, когда вы его просматриваете, и когда элемент добавляется в коллекцию. Самый простой способ справиться с такой проблемой параллелизма, на мой взгляд, состоял бы в том, чтобы поймать исключение, прикрепить книгу к автору, который сейчас находится в Db, а затем повторно отправить. – Claies

+0

1. Вы попробовали? Автор.Books.Add (книга); ctx.SaveChangesAsync(); 2. Добавить в книгу: public int AuthorId {get; задавать; } Публичный виртуальный Автор Автор {get; задавать; } – W92

+0

Замечания Клэйса верны ... гонка - это точно проблема, и я пытаюсь найти обходное решение. @ W92, да ... попробовал это. такое же состояние гонки возникает. Вопрос касается вставки автора. – ashic

ответ

3

Перепробовав различные вещи, я пошел со следующим:

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(); 

Пока не очень, это предотвратить состояние гонки между проверкой автора и вставкой с использованием БД нативных функций. ORM и негерметичные абстракции, eh :)

Думаю, я тоже мог бы положить в нее книжную вставку или сделать все это sproc. Если кто-то придумает собственный подход ORM, который подходит для этого сценария, я все уши :)

Смежные вопросы