#c# #entity-framework #design-patterns #ef-code-first
#c# #entity-framework #шаблоны проектирования #ef-code-first
Вопрос:
Использование следующих классов по соглашению EF позволит правильно создать таблицы и ассоциации PK / FK. Однако, когда я добавляю данные в коллекцию photos и сохраняю объекты, данные в таблице Photos не сохраняются в таблице базы данных.
Я избегаю использования любых типов коллекций, которые наследуются от ICollection для общедоступного свойства, и вместо этого использовал частное резервное поле, поскольку я хочу контролировать доступ к методам добавления / удаления. Есть ли что-нибудь дополнительное, что мне нужно добавить в OnModelCreating, чтобы сообщить EF, что в IEnumerable Photos есть данные, и сохранить их?
(Данные из класса Album могут быть сохранены правильно)
public class Album
{
private readonly ICollection<Photo> _photos = new List<Photo>();
public Guid Id {get; set;}
public string Name {get; set;}
public virtual IEnumerable<Photo> Photos
{
get{ return _photos;}
}
public void AddPhoto(byte[] bytes, string name)
{
//some biz rules
Photo p = new Photo();
p.Bytes = bytes;
p.Name = name;
_photos.Add(p);
}
}
public class Photo
{
public Guid Id {get; set;}
public string Name {get; set;}
public byte[] Bytes {get; set;}
}
public class AlbumDbContext : DbContext
{
public AlbumDbContext()
{
this.Database.CreateIfNotExists();
}
public DbSet<Album> Albums { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
}
}
Ответ №1:
Я даже удивляюсь, что таблицы и отношения созданы правильно, особенно когда ни одна из ваших сущностей не определила ключ, а контекст не определяет set для Photos
. В любом случае ICollection<T>
это необходимо. Ваш класс не является допустимой сущностью, и даже если вы каким-то образом заставите его работать, я ожидаю, что у вас возникнут проблемы с его использованием — например, вы можете забыть о отложенной загрузке.
Также обработка их таким образом не имеет смысла, потому что любой может преобразовать ваш enumerable обратно в collection. Вы должны создать перечислимую копию коллекции, чтобы заставить ее работать должным образом.
Единственный способ, который, возможно, может сработать, это:
public class Album
{
public class AlbumConfiguration : EntityTypeConfiguration<Album>
{
public AlbumConfiguration()
{
// Inner class will see private members of the outer class
HasMany(a => a._photos)...
}
}
private readonly ICollection<Photo> _photos = new List<Photo>();
public string Name {get; set;}
public virtual IEnumerable<Photo> Photos
{
get{ return _photos;}
}
public void AddPhoto(byte[] bytes, string name)
{
//some biz rules
Photo p = new Photo();
p.Bytes = bytes;
p.Name = name;
_photos.Add(p);
}
}
И ваш контекст будет выглядеть следующим образом:
public class AlbumDbContext : DbContext
{
public DbSet<Album> Albums { get; set; }
public DbSet<Photo> Photos { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new Album.AlbumConfiguration());
}
}
Это решение довольно уродливое, потому что оно делает вашу сущность зависимой от entity framework.
Комментарии:
1. Да, у фактического кода есть свойства идентификатора. Просто обновил код для полноты.
2. Можете ли вы объяснить немного больше о преобразовании перечисляемого обратно в коллекцию? Как это возможно? Единственный способ добавить фотографии — это использовать метод AddPhoto. У клиента не должно быть способа изменить коллекцию …?
3. Вы можете просто вызвать
(List<Photo>)album.Photos
4. Спасибо, что указали на это. Понятия не имел, что это возможно! Как можно было бы защитить коллекцию?
5. @Diego — Мы хорошо защищаем от клиентов, использующих API. Они просто не могут просто добавлять объекты без действующих бизнес-правил. Я понимаю, что возврат только для чтения в получателе может ограничить доступ?