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

时间:2015-05-23 12:45:51

标签: c# entity-framework-core

我正在尝试使用我自己项目中另一个程序集中的某些类作为我可以使用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&lt;&gt;而且只是这样做:

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

这引发:

  

NotSupportedException:实体类型“CPlaylistEntry”上的“MediaFile”没有设置值,并且没有值生成器可用于“CMediaFile”类型的属性。在添加实体之前为属性设置值,或者为“CMediaFile”类型的属性配置值生成器

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

是否可以在EF中对这些类进行建模而不需要对它们进行彻底的重新设计,以便更接近相应的数据库表?我过去曾经使用过EF6数据库,所以这是我第一次尝试使用代码优先模式,我真的希望能够将数据库看起来像是我的模型定义,并保留我在.NET中与之交互的“干净”类。

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

1 个答案:

答案 0 :(得分:4)

怀疑目前是否支持(不确定它是否最终会成功)我试图通过轻微的更改重新创建您的模型,当我尝试创建数据库时,我得到:

  

System.NotSupportedException:属性&#39; PlaylistEntry`1MediaFile&#39;   无法映射,因为它的类型为&#39; MediaFile&#39;这是目前的   不支持。   

更新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和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();

上面的代码会使&#34;无关紧要&#34;在PlaylistEntry Uri属性中,因为它被用作外键。

并检索数据:

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

连接将在两个表中的Uri字段上进行。