这些模型可以在EF7中表示吗?

本文关键字:表示 EF7 模型 | 更新日期: 2023-09-27 17:50:27

我试图在我自己的项目中使用另一个程序集中的一些类作为实体,我可以坚持使用EF7,而不是编写一系列非常相似的类,这些类对数据库更友好。

简化版本如下:

interface IMediaFile
{
    string Uri { get; }
    string Title { get; set; }
}
class CMediaFile : IMediaFile
{
    public CMediaFile() { }
    public string Uri { get; set; }
    public string Title { get; set; }
}
//The following types are in my project and have full control over.
interface IPlaylistEntry
{
    IMediaFile MediaFile { get; }
}
class CPlaylistEntry<T> : IPlaylistEntry where T : IMediaFile
{
    public CPlaylistEntry() { }
    public T MediaFile { get; set; }
}

IMediaFile有多种实现,我只展示一个。我的PlaylistEntry类需要一个通用参数来为那些不同的实现启用不同的特征,我只是使用IPlaylistEntry。

所以我开始像这样建模:

var mediaFile = _modelBuilder.Entity<CMediaFile>();
mediaFile.Key(e => e.Uri);
mediaFile.Index(e => e.Uri);
mediaFile.Property(e => e.Title).MaxLength(256).Required();
var mediaFilePlaylistEntry = _modelBuilder.Entity<CPlaylistEntry<CMediaFile>>();
mediaFilePlaylistEntry.Key(e => e.MediaFile);
mediaFilePlaylistEntry.Reference(e => e.MediaFile).InverseReference();

作为一个简单的测试,我忽略了CPlaylistEntry<>,只执行:

dbContext.Set<CMediaFile>().Add(new CMediaFile() { Uri = "irrelevant", Title = "" });
dbContext.SaveChanges()

这一扔:

NotSupportedException:实体类型"CPlaylistEntry"上的"MediaFile"没有值集,并且没有值生成器可用于类型"CMediaFile"的属性。要么在添加实体之前为属性设置一个值,要么为类型为"CMediaFile"的属性配置一个值生成器

我甚至不理解这个例外,我不明白为什么CPlaylistEntry出现时,我只是试图存储一个CMediaFile实体。我猜这与我的模型定义有关-特别是将CPlaylistEntry的主键定义为不是简单类型,而是复杂类型-另一个实体。然而,我希望EF能够足够聪明地计算出这一切归结为字符串Uri,因为该复杂类型已经声明了自己的主键,并且我已经将该属性声明为该类型的外键。

是否有可能在EF中对这些类建模而不从根本上重新设计它们以更接近相应的数据库表?我过去曾使用过EF6数据库优先,所以这是我第一次尝试代码优先模式,我真的希望我能隔离数据库可能看起来像我的模型定义的混乱,并保持我在。net中与之交互的"干净"类。

如果需要对这些类型和它们之间的关系进行更多的解释,请直接问——我尽量保持简短。

这些模型可以在EF7中表示吗?

目前是否支持(不确定最终是否支持)。|我试着用轻微的变化重新创建你的模型,当试图创建数据库时,我得到:

系统。NotSupportedException:属性"PlaylistEntry"1MediaFile不能被映射,因为它的类型是"MediaFile"不受支持的。

更新1

我认为你把MediaFile作为一个键的事实正在制造问题。我对你的模型做了一些改动。我希望这不会对你造成负面影响:

public interface IPlaylistEntry<T>
    where T : IMediaFile
{
    T MediaFile { get; set; }
}
public class PlaylistEntry<T> : IPlaylistEntry<T>
    where T : IMediaFile
{
    public int Id { get; set; }
    public string PlaylistInfo { get; set; } //added for testing purposes
    public T MediaFile { get; set; }
}

映射:

protected override void OnModelCreating(ModelBuilder builder)
  {
     builder.ForSqlServer().UseIdentity();
     builder.Entity<MediaFile>().ForRelational().Table("MediaFiles");
     builder.Entity<MediaFile>().Key(e => e.Uri);
     builder.Entity<MediaFile>().Index(e => e.Uri);
     builder.Entity<MediaFile>().Property(e => e.Title).MaxLength(256).Required();
     builder.Entity<PlaylistEntry<MediaFile>>().ForRelational().Table("MediaFileEntries");
     builder.Entity<PlaylistEntry<MediaFile>>().Key(e => e.Id);
     builder.Entity<PlaylistEntry<MediaFile>>().Reference(e => e.MediaFile).InverseReference();
 }

用法:

  var mediaFile = new MediaFile() {Uri = "irrelevant", Title = ""};
  context.Set<MediaFile>().Add(mediaFile);
  context.SaveChanges();
  context.Set<PlaylistEntry<MediaFile>>().Add(new PlaylistEntry<MediaFile>
  {
      MediaFile = mediaFile,
      PlaylistInfo = "test"
  });
  context.SaveChanges();

这将工作并将正确的数据保存到数据库中。

可以使用以下命令检索数据:

  var playlistEntryFromDb = context.Set<PlaylistEntry<MediaFile>>()
         .Include(plemf => plemf.MediaFile).ToList();

更新2

因为你不想要一个身份作为键,你可以添加一个Uri属性到你的playlistentry类,它将用于playlistentry和MediaFile之间的关系。

public class PlaylistEntry<T> : IPlaylistEntry<T>
    where T : IMediaFile
{
    public string Uri { get; set; }
    public string PlaylistInfo { get; set; }
    public T MediaFile { get; set; }
}

本例中的映射如下:

    protected override void OnModelCreating(ModelBuilder builder)
    {
        builder.Entity<MediaFile>().ForRelational().Table("MediaFiles");
        builder.Entity<MediaFile>().Key(e => e.Uri);
        builder.Entity<MediaFile>().Index(e => e.Uri);
        builder.Entity<MediaFile>().Property(e => e.Title).MaxLength(256).Required();
        builder.Entity<PlaylistEntry<MediaFile>>().ForRelational().Table("MediaFileEntries");
        builder.Entity<PlaylistEntry<MediaFile>>().Key(e => e.Uri);
        builder.Entity<PlaylistEntry<MediaFile>>().Reference(e => e.MediaFile).InverseReference().ForeignKey<PlaylistEntry<MediaFile>>(e => e.Uri);
    }

插入数据的用法保持不变:

 var mediaFile = new MediaFile() { Uri = "irrelevant", Title = "" };
 context.Set<MediaFile>().Add(mediaFile);
 context.SaveChanges();
 context.Set<PlaylistEntry<MediaFile>>().Add(new PlaylistEntry<MediaFile>
 {
     MediaFile = mediaFile,
     PlaylistInfo = "test"
 });
 context.SaveChanges();

上面的代码将把"不相干"放在PlaylistEntry Uri属性中,因为它被用作外键。

和检索数据:

    var mediaFiles = context.Set<PlaylistEntry<MediaFile>>().Include(x => x.MediaFile).ToList();

连接将发生在两个表的Uri字段上