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

Can these models be represented in EF7?

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

简化版本如下所示:

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 class 采用通用参数来为这些不同的实现启用不同的特性,我只使用 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: The 'MediaFile' on entity type 'CPlaylistEntry' does not have a value set and no value generator is available for properties of type 'CMediaFile'. Either set a value for the property before adding the entity or configure a value generator for properties of type 'CMediaFile'`

我什至不明白这个异常,我不明白为什么在我只尝试存储 CMediaFile 实体时出现 CPlaylistEntry。我猜这与我的模型定义有关——特别是将 CPlaylistEntry 的主键定义为不是简单类型,而是复杂类型——另一个实体。但是,我希望 EF 足够聪明,可以将其归结为一个字符串 Uri,因为该复杂类型已经声明了自己的主键,并且我已将 属性 声明为其外键类型。

是否可以在 EF 中对这些 classes 建模,而无需从根本上重新设计它们以更接近于对应的数据库表?我过去曾使用过 EF6 数据库优先,所以这是我第一次尝试代码优先模式,我真的希望我可以将数据库看起来像我的模型定义的混乱隔离开来,并保留我在 .NET 中与之交互的 "clean" classes。

如果需要对这些类型及其关系进行更多解释,请询问 - 我尽量保持简短。

怀疑目前是否支持此功能(不确定最终是否会支持)。|我尝试通过稍作更改重新创建您的模型,并且在尝试创建数据库时我得到:

System.NotSupportedException: The property 'PlaylistEntry`1MediaFile' cannot be mapped because it is of type 'MediaFile' which is currently not supported.

更新 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 属性 添加到您的播放列表 class,它将用于 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();

上面的代码会将 "irrelevant" 放入 PlaylistEntry Uri 属性 中,因为它用作外键。

并检索数据:

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

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