У меня есть следующий фрагмент кода
var newPost = new Post()
{
Activity = new Activity { Type = 1, ActivityTotalStatistic = new ActivityTotalStatistic() },
CreatedDate = oldPost.DateTimeCreated,
CategoryId = categoryId,
Title = oldPost.Name,
OwnerId = oldPost.UserID,
Slug = oldPost.Name,
LastUpdateDate = oldPost.DateTimeCreated,
PublishDate = oldPost.DateTimeCreated,
PostStatistic = new PostStatistic(),
PostItems = new List<PostItem>
{
new PostItem
{
Activity = new Activity { Type = 2},<-- note this line of code
CreatedDate = oldPost.DateTimeCreated,
Title = oldPost.Name,
Type = 1,
Content = oldPost.Path
}
}
};
newDb.Posts.Add(newPost);
newDb.SaveChanges();
Это схема SQL для таблицы активности и статистики.
create table ActivityTotalStatistics
(
Id int primary key identity(1,1),
NumberOfLikes int not null,
NumberOfDislikes int not null,
SumOfLikes int not null,
CommentCount int not null
)
create table Activities
(
Id int identity (1,1) primary key,
Type int not null,
ActivityTotalStatisticId int not null
foreign key references ActivityTotalStatistics(Id)
)
Как вы можете видеть, каждое действие должно иметь статистику действий, потому что внешний ключ не может принимать значения NULL, и мой код должен сломаться, потому что действие в элементе записи не имеет экземпляра статистики действий.
Но EF его не распознает. Что происходит, так это то, что элемент сообщения получает ту же статистику активности из этой строки кода.
Activity = new Activity { Type = 1, ActivityTotalStatistic = new ActivityTotalStatistic() },
Является ли это допустимым поведением Entity Framework?
ОБНОВИТЬ Модель Activity.cs
public partial class Activity
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public Activity()
{
this.ActivityLikes = new HashSet<ActivityLike>();
this.Comments = new HashSet<Comment>();
this.PostItems = new HashSet<PostItem>();
this.Posts = new HashSet<Post>();
}
public int Id { get; set; }
public int Type { get; set; }
public int ActivityTotalStatisticId { get; set; }
public virtual ActivityTotalStatistic ActivityTotalStatistic { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<ActivityLike> ActivityLikes { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<Comment> Comments { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<PostItem> PostItems { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<Post> Posts { get; set; }
}
ActivityTotalStatistic.cs
public partial class ActivityTotalStatistic
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public ActivityTotalStatistic()
{
this.Activities = new HashSet<Activity>();
}
public int Id { get; set; }
public int NumberOfLikes { get; set; }
public int NumberOfDislikes { get; set; }
public int SumOfLikes { get; set; }
public int CommentCount { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<Activity> Activities { get; set; }
}
@simonare вам нужен XML или дизайнерский снимок экрана?
@Robert Добавьте свой класс модели к вопросу.
@TanvirAjel я добавил. это база данных в первую очередь
Можете ли вы также поделиться классами Post и PostItem? Какова логическая иерархия ваших сущностей? Пост владеет активностью или активность владеет сообщениями? Я думал, что Post должен владеть Activity, но коллекция Posts и PostItems в Activity меня смутила. Принадлежит ли Activity пользователю, публикации или чему-то еще?
@neptune Activity похож на базовую таблицу. У поста есть своя активность, у поста своя активность. Таким образом, я могу повторно использовать таблицу комментариев для ActivityComments вместо двух таблиц PostComments и PostItemComments.
@Robert, вы уверены, что ActivityTotalStatistic, который добавляется в PostItem, принадлежит Post, а не новому? Я подозреваю, что EF создает для вас новую пустую статистику AcitivityTotalStatistic. Можете ли вы поделиться тем, что попадает в базу данных после выполнения кода, которым вы поделились?
@neptune добавляет одно действие, устанавливает один и тот же идентификатор действия для публикации и публикации. Если я не создам экземпляр в сообщении, он выдает исключение
Я думаю, что EF предполагает неправильное направление отношений. Он может рассматривать Activity как родитель, а Post и PostItem как его дочерние элементы. Я знаю, что это не объясняет, почему Activity повторно используется как в Post, так и в PostItem, которые вы создаете, но я думаю, что EF обычно не ожидает такого указания отношений. См. здесь в разделе Условные обозначения: docs.microsoft.com/en-us/ef/core/modeling/… Как выглядят таблицы Post и PostItem?





Is this a valid behavior of Entity Framework?
Я так не думаю - к сожалению, нет официальной документации/спецификации для этих режимов отслеживания, но похоже на побочный эффект/дефект реализации (ошибка).
Обоснование:
Это происходит Только, когда средство отслеживания изменений содержит не замужемAdded основной объект с автоматически сгенерированным PK, зависимый объект имеет как свойство явной ссылки навигации, так и свойство FK, все со значениями по умолчанию («0» и null).
Это нет происходит, если:
(1) Существует второй Added основной объект:
newDb.Set<ActivityTotalStatistic>().Add(new ActivityTotalStatistic());
SaveChanges бросает DbUpdateException:
Unable to determine the principal end of the 'Activity_ActivityTotalStatistic' relationship. Multiple added entities may have the same primary key.
Не очень удобный, но все же исключение.
(2) Зависимый объект не имеет явного свойства FK, но правильно сконфигурировал требуемое теневое свойство FK. SaveChanges бросает:
Entities in 'MyDbContext.Activities' participate in the 'Activity_ActivityTotalStatistic' relationship. 0 related 'Activity_ActivityTotalStatistic_Target' were found. 1 'Activity_ActivityTotalStatistic_Target' is expected.
Опять же, не очень удобно, но все же исключение.
(3) К свойству ссылочной навигации применен атрибут [Required]:
[Required]
public virtual ActivityTotalStatistic ActivityTotalStatistic { get; set; }
SaveChanges бросает ValidationException, содержащий ValidationError с (наконец-то) удобным для пользователя сообщением:
The ActivityTotalStatistic field is required.
Вернемся к исходному делу. Только и только в этом конкретном случае EF находит единственный ожидающий экземпляр основного объекта Added и связывает его со всеми ожидающими зависимыми объектами, имеющими ActivityTotalStatistic == null и ActivityTotalStatisticId == 0.
Все это для меня указывает на баг/непреднамеренное поведение EF6. Кажется, что лучший способ предотвратить это — украсить необходимые справочные свойства навигации атрибутом [Required], но это проблематично в сгенерированной модели объекта Database First (edmx), такой как ваша.
Тогда мы будем называть это ошибкой или нежелательным поведением.
Можете ли вы опубликовать модель EF, связанную с вашей таблицей, и конфигурацию Entity, связанную с вашей моделью?