这些模型可以在 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 字段上。
我正在尝试在我自己的项目中使用来自另一个程序集的一些 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 字段上。