2015-11-19 4 views
3

Дополнение (1): Один из ответов сказал, что он должен работать. Поэтому я добавил полное исключение в конце этого сообщенияУдалить из коллекции в Entity Framework 6

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

MSDN Code First to a new DataBase

Что я вижу в том, что если я создаю блог, я могу добавить некоторые сообщения, добавить блог в базу данных и вызвать SaveChanges. Структура сущности признает, что сообщения должны находиться в другой таблице с внешним ключом в таблице блогов.

Это именно то, как можно было бы создать базу данных, если бы Entity Framework не было вокруг.

Получение всех сообщений из блога и добавление сообщения может быть сделано без каких-либо знаний об отдельной почтовой таблице и внешнем ключе в таблице Blog.

Однако, когда я пытаюсь удалить сообщение из блога, я получаю ошибки в отношении внешних ключей.

Примечание: Следующая не об эффективности

public class Blog 
{ 
    public int Id { get; set; } 
    public string Name { get; set; } 

    public virtual ICollection<Post> Posts { get; set; } 
} 

public class Post 
{ 
    public int Id { get; set; } 
    public string Title { get; set; } 
    public string Content { get; set; } 

    public int BlogId { get; set; } 
    public virtual Blog Blog { get; set; } 
} 

public class BloggingContext : DbContext 
{ 
    public BloggingContext(string nameOrConnectionString) 
     : base(nameOrConnectionString){} 

    public DbSet<Blog> Blogs { get; set; } 
    public DbSet<Post> Posts { get; set; } 
} 

Использование будет выглядеть следующим образом:

class Program 
{ 
    static void Main(string[] args) 
    { 
     const string dbName = "MyTestDb"; 
     Database.SetInitializer(new DropCreateDatabaseAlways <BloggingContext>()); 
     using (var context = new BloggingContext(this.DbName)) 
     { 
      // create a blog: 
      var blog = new Blog() 
      { 
       Name = "First Blog", 
       Posts = new List<Post>() 
       { 
        new Post() { Title = "My 1st Post", Content = "Hello World!" }, 
        new Post() { Title = "My 2nd Post", Content = "All animals are equal but pigs are more equal"}, 
        new Post() { Title = "My 3rd Post", Content = "Shall I compare thee to a summer's day" }, 
       }, 
      }; 
      context.Blogs.Add(blog); 
      context.SaveChanges(); 

Хорошая вещь в том, что клиент не должен знать как фактически организована база данных. Клиент не должен знать, что Блог и Почта находятся в разных таблицах. Для клиента в блоге есть коллекция сообщений.

Аналогичным образом, если клиент запрашивает блог и получает доступ к сообщениям в блоге, Entity Framework знает, где получить сообщения. Клиент не должен знать, что сообщения сохраняются в другой таблице с внешним ключом.

blog = context.Blogs.First(); 
var lastPost = blog.Posts.Last(); 

Entity Framework даже при этом умный, что он не выбирает элементы, которые не нужны. Если бы я не хотел бы использовать коллекцию Post, то сообщения не будут извлечены из базы данных

Поэтому я ожидал, что следующий будет работать:

blog.Posts.Remove(lastPost); 
context.AddOrUpdate(blog); 
context.SaveChanges(); 

я ожидал, что структура субъекта знал бы, что последнее сообщение было удалено, и, таким образом, он должен будет удалить элемент из таблицы Posts. Но это не делает этого. Я получаю исключение: «Отношения не могут быть изменены, потому что одно или несколько свойств внешнего ключа не являются нулевыми. Bla bla», что означает, что сообщение должно быть удалено из таблицы Posts.

Вопрос: Правильно ли, что клиент не должен знать модель базы данных Entity Framework, за исключением случаев, когда данные должны быть удалены?

Дополнение: Конечно, я могу удалить элемент из DbSet, но мой вопрос: если Entity Framework достаточно умен, мне не нужно добавлять сообщение в DbSet, почему я должен его удалить?

Кто-то спросил текст исключения

System.InvalidOperationException был необработанное

  • _HResult = -2146233079
  • _message = Сбой операции: Отношения не могут быть изменены из-за одно или несколько свойств внешнего ключа не имеют значения NULL. Когда происходит изменение отношения, соответствующее свойство внешнего ключа устанавливается равным нулевому значению. Если внешний ключ не поддерживает нулевые значения, необходимо определить новое отношение, для свойства внешнего ключа должно быть назначено другое ненулевое значение, или не связанный с ним объект должен быть удален.

  • HResult = -2146233079

  • IsTransient = ложь
  • Message = Сбой операции: Отношения не могут быть изменены, так как один или несколько внешних ключей свойств не является обнуляемым. Когда происходит изменение отношения, соответствующее свойство внешнего ключа устанавливается равным нулевому значению. Если внешний ключ не поддерживает нулевые значения, необходимо определить новое отношение, для свойства внешнего ключа должно быть назначено другое ненулевое значение, или не связанный с ним объект должен быть удален.
  • Источник = EntityFramework

StackTrace: на System.Data.Entity.Core.Objects.ObjectContext.PrepareToSaveChanges (варианты SaveOptions) на System.Data.Entity.Core.Objects.ObjectContext.SaveChangesInternal (SaveOptions опции, Boolean executeInExistingTransaction) на System.Data.Entity.Core.Objects.ObjectContext.SaveChanges (варианты SaveOptions) на System.Data.Entity.Internal.InternalContext.SaveChanges() в System.Data.Entity.Internal.LazyInternalContext .SaveChanges() в System.Data.Entity.DbContext.SaveChanges() в Try InMemoryDbSet.TestDirectDbContext.Test2() в c: \ Users \ Harald \ Documents \ Visual Studio 2013 \ Projects \ EntityFramework \ TryInMemoryDbSet \ TryInMemoryDbSet \ TestDirectDbContext.cs: строка 75 в TryInMemoryDbSet.Program.Main (String [] args) в c : \ Users \ Harald \ Documents \ Visual Studio 2013 \ Projects \ EntityFramework \ TryInMemoryDbSet \ TryInMemoryDbSet \ Program.cs: линия 19 InnerException:

+0

Вы пробовали 'blog.Posts.Remove (lastPost)', а затем вызываете 'context.SaveChanges()' вместо? – rexcfnghk

+0

oops, ошибка ввода. Я исправлю это в своем вопросе –

+0

Можете ли вы также опубликовать трассировку стека здесь? По моему опыту, он должен работать так, как ожидалось. Также вы попытались удалить вызов AddOrUpdate()? EF достаточно умен, чтобы определить объект как «EntityState.Modified», если ваша сущность отслеживается «DbContext» – rexcfnghk

ответ

1

Вопрос: Верно ли, что клиент не должен знать модель базы данных Entity Framework, созданную, кроме случаев, когда данные должны быть удалены ?

Дополнение: Конечно, я могу удалить элемент из DbSet, но мой вопроса: Если Entity Framework достаточно умен я не должен добавить пост в DbSet, почему я должен удалить Это?

Anwser:

Вы должны удалить его из DbSet, потому что, когда вы удалите его из коллекции, вы на самом деле удаления «отношения» между ними. Однако отношение (в вашем случае) является обязательным, свойство Post BlogId NOT NULL, это означает, что все сообщения должны принадлежать блогу. Когда вы удаляете из коллекции, EF пытается установить FK как нулевое значение, поэтому вы получаете исключение.

Это сгенерированный SQL, который EF выполняется при удалении Post из Blog

UPDATE [dbo].[Posts] 
SET [BlogId] = NULL 
WHERE ([Id] = @0) 

Это будет работать, если BlogId был обнуляемым.

+0

Я понимаю, что вы говорите, и я согласен, что это решение для его решения. Но я не понимаю, что если я добавлю сообщение в существующий блог, мне не нужно добавлять его в DbSet . Это сделано для меня. Если я удалю его, я должен помнить, чтобы удалить его из DbSet также –

+1

. Ваше второе утверждение неверно. Вам не нужно удалять его из коллекции и DbSet, вам нужно удалить его только из DbSet (сбор не требуется). Единственный способ удалить объект - удалить его из DbSet. Когда вы удаляете его из коллекции, вы удаляете только его отношения. EF признает, что что-то нужно вставить, и делает это автоматически. Однако он не удаляет что-то автоматически, возможно, из соображений безопасности. –

0
blog.Posts.Remove(lastPost); 

lastPost Удалить из blog.Posts коллекции, а не из базы данных.

Как я представляю для публикации, необходим блог, сообщение не может существовать с PostId, оцененным нулем.

По крайней мере, два решения:.

  • context.Set() Удалить (lastPost), или
  • использовать BlogId в составном PK на пост. Поэтому, когда установлено значение null, PostId запускает удаление в Db.
+1

. Вы в какой-то мере правы. Удаление lastPost из коллекции сообщений в блоге не означает, что lastPost больше не должен существовать. Но мне показалось странным, что даже если бы я не указал DbSet , Entity Framework по-прежнему достаточно умен, чтобы создать таблицу с сообщениями и внешними ключами в «Блоги». Тем не менее, он недостаточно умен, чтобы удалить его. Если бы я не добавил его, зачем мне его удалять. Я подожду, может, кто-то придет с решением, если нет, мне придется использовать ваш метод. –

+0

Я только что наткнулся на статью, которая предлагает альтернативное решение: http://www.kianryan.co. uk/2013/03/orphaned-child/Решение 3 в этой статье помогло мне немного решить эту проблему. Он не идеален, но он работает. Грустно это не работает из коробки, как NHibernate. –

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